diff --git a/.github/workflows/draft-pdf.yml b/.github/workflows/draft-pdf.yml new file mode 100644 index 000000000..d8e62ff6a --- /dev/null +++ b/.github/workflows/draft-pdf.yml @@ -0,0 +1,33 @@ +name: JossPaperCompilation + +on: + push: + paths: + - papers/joss/** + pull_request: + paths: + - 'papers/joss/**' + workflow_dispatch: +jobs: + paper: + runs-on: ubuntu-latest + name: Paper Draft + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Build draft PDF + uses: openjournals/openjournals-draft-action@85a18372e48f551d8af9ddb7a747de685fbbb01c # v1.0 + with: + journal: joss + # This should be the path to the paper within your repo. + paper-path: papers/joss/paper.md + - name: Upload + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: paper + # This is the output path where Pandoc will write the compiled + # PDF. Note, this should be the same directory as the input + # paper.md + path: papers/joss/paper.pdf diff --git a/.github/workflows/lychee_links.yaml b/.github/workflows/lychee_links.yaml index f1d77c327..3aaf4305f 100644 --- a/.github/workflows/lychee_links.yaml +++ b/.github/workflows/lychee_links.yaml @@ -15,17 +15,19 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Restore lychee cache - uses: actions/cache@v4 + uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3 with: path: .lycheecache key: cache-lychee-${{ github.sha }} restore-keys: cache-lychee- - name: Run lychee - uses: lycheeverse/lychee-action@v2 + uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2.7.0 with: args: >- --cache diff --git a/.github/workflows/nightly_dependency_tests.yaml b/.github/workflows/nightly_dependency_tests.yaml index f10dc67a6..04d1bd2c3 100644 --- a/.github/workflows/nightly_dependency_tests.yaml +++ b/.github/workflows/nightly_dependency_tests.yaml @@ -1,4 +1,4 @@ -name: Nightly dependencies at develop +name: Nightly dependencies on: workflow_dispatch: @@ -14,39 +14,82 @@ concurrency: # Cancel in-progress runs when a new workflow with the same group name is triggered cancel-in-progress: true +permissions: {} + jobs: - nightly_tests: + check_dependencies: + name: Test-${{ matrix.dep-type }}-${{ matrix.os }}-py-${{ matrix.python-version }}-${{ matrix.suite }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-14] - python-version: ["3.12"] + os: [ubuntu-latest, macos-latest] suite: ["unit", "integration", "examples", "notebooks"] - - name: Test-${{ matrix.os }}-py-${{ matrix.python-version }}-${{ matrix.suite }}) + dep-type: ["latest", "lowest"] + # The following must remain in sync with our Python version support as defined in pyproject.toml + include: + # Latest dependencies with Python 3.13 (our maximum supported Python version) + - dep-type: latest + python-version: "3.13" + # Lowest dependencies with Python 3.10 (our minimum supported Python version) + - dep-type: lowest + python-version: "3.10" + permissions: + contents: read + issues: write # for creating issues when tests fail steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0 with: python-version: ${{ matrix.python-version }} - - name: Install dependencies + activate-environment: "true" + + - name: Install dependencies (latest) + if: matrix.dep-type == 'latest' + # uv pip install pip needed to allow %pip install within notebooks run: | - python -m pip install -e .[all,dev] - python -m pip uninstall -y pybamm - python -m pip install "pybamm[all] @ git+https://github.com/pybamm-team/PyBaMM@develop" + uv pip install pip + uv pip install -e .[all] + uv pip install --group dev + uv pip uninstall pybamm + uv pip install "pybamm[all] @ git+https://github.com/pybamm-team/PyBaMM@main" + uv pip install pytest-reportlog + - name: Install dependencies (lowest) + if: matrix.dep-type == 'lowest' + run: | + uv pip install pip + uv pip install --group dev + uv pip install --resolution lowest-direct -e .[all] + uv pip install pytest-reportlog - name: Run ${{ matrix.suite }} tests + id: pytest + env: + PYTEST: "pytest --report-log=pytest-log.jsonl" run: | if [[ "${{ matrix.suite }}" == "unit" ]]; then - python -m pytest --unit + python -m $PYTEST --unit elif [[ "${{ matrix.suite }}" == "integration" ]]; then - python -m pytest --integration + python -m $PYTEST --integration elif [[ "${{ matrix.suite }}" == "examples" ]]; then - python -m pytest --examples + python -m $PYTEST --examples elif [[ "${{ matrix.suite }}" == "notebooks" ]]; then - python -m pytest --notebooks --nbmake --nbmake-timeout=1000 examples/ + python -m $PYTEST --notebooks --nbmake --nbmake-timeout=1000 examples/ fi + + - name: Create issues from pytest logs + uses: scientific-python/issue-from-pytest-log-action@8e905db353437cda1d6a773de245343fbfc940dd # v1.5.0 + if: | + failure() + && steps.pytest.outcome == 'failure' + && github.repository == 'pybop-team/PyBOP' + && github.event_name == 'schedule' + with: + log-path: pytest-log.jsonl + issue-title: "Nightly ${{ matrix.dep-type }} dependencies ${{ matrix.suite }} tests failed (${{ matrix.os }} Python ${{ matrix.python-version }})" diff --git a/.github/workflows/periodic_benchmarks.yaml b/.github/workflows/periodic_benchmarks.yaml index 704c7975a..f7e5c4cec 100644 --- a/.github/workflows/periodic_benchmarks.yaml +++ b/.github/workflows/periodic_benchmarks.yaml @@ -27,7 +27,9 @@ jobs: rm -rf ./* || true rm -rf ./.??* || true - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Install python & create virtualenv shell: bash @@ -41,13 +43,15 @@ jobs: run: | eval "$(pyenv init -)" pyenv activate pybop-312-bench - python -m pip install -e .[all,dev] + python -m pip install --upgrade pip + python -m pip install -e .[all] + python -m pip install --group dev python -m pip install asv[virtualenv] python -m asv machine --machine "SelfHostedRunner" python -m asv run --machine "SelfHostedRunner" NEW --show-stderr -v - name: Upload results as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: asv_periodic_results path: results @@ -67,7 +71,7 @@ jobs: if: github.repository == 'pybop-team/PyBOP' steps: - name: Set up Python 3.12 - uses: actions/setup-python@v5 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.12 @@ -75,13 +79,13 @@ jobs: run: pip install asv - name: Checkout pybop-bench repo - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: repository: pybop-team/pybop-bench token: ${{ secrets.PUSH_BENCH_TOKEN }} - name: Download results artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: asv_periodic_results path: new_results diff --git a/.github/workflows/release_action.yaml b/.github/workflows/release_action.yaml index da6a6920c..364121303 100644 --- a/.github/workflows/release_action.yaml +++ b/.github/workflows/release_action.yaml @@ -11,15 +11,17 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.12" - name: Build a source tarball and a wheel from it run: pipx run build - name: Store the distribution packages - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: python-package-distributions path: dist/ @@ -40,13 +42,13 @@ jobs: steps: - name: Download all the dists - uses: actions/download-artifact@v4 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: python-package-distributions path: dist/ merge-multiple: true - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 github-release: name: >- @@ -62,19 +64,19 @@ jobs: steps: - name: Download all the dists - uses: actions/download-artifact@v4 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: python-package-distributions path: dist/ merge-multiple: true - name: Sign the dists with Sigstore - uses: sigstore/gh-action-sigstore-python@v1.2.3 + uses: sigstore/gh-action-sigstore-python@a5caf349bc536fbef3668a10ed7f5cd309a4b53d # v3.2.0 with: inputs: >- ./dist/*.tar.gz ./dist/*.whl - name: Publish artifacts and signatures to GitHub Releases - uses: softprops/action-gh-release@v2 + uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0 with: # `dist/` contains the built packages, and the # sigstore-produced signatures and certificates. @@ -97,12 +99,12 @@ jobs: steps: - name: Download all the dists - uses: actions/download-artifact@v4 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: name: python-package-distributions path: dist/ merge-multiple: true - name: Publish distribution 📦 to TestPyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 with: repository-url: https://test.pypi.org/legacy/ diff --git a/.github/workflows/scheduled_tests.yaml b/.github/workflows/scheduled_tests.yaml index 474a7c357..c307928f4 100644 --- a/.github/workflows/scheduled_tests.yaml +++ b/.github/workflows/scheduled_tests.yaml @@ -22,8 +22,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out PyBOP repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: + persist-credentials: false sparse-checkout-cone-mode: false sparse-checkout: | scripts/ci/build_matrix.sh @@ -36,7 +37,7 @@ jobs: pybop_matrix: ${{ steps.set-matrix.outputs.matrix }} # Filter the matrix to only include Python and PyBaMM versions. This job - # is used for the self-hosted macOS-14 (arm64) runner at this time. + # is used for the self-hosted macOS (arm64) runner at this time. filter_pybamm_matrix: name: Filter the matrix for OS and Python version needs: [create_pybamm_matrix] @@ -56,8 +57,8 @@ jobs: matrix = json.loads(matrix_json) # Filter the matrix to include just the Python version and PyBaMM version - # First filter the matrix to only include macOS-14 entries - filtered_entries = [entry for entry in matrix['include'] if entry['os'] == 'macos-14'] + # First filter the matrix to only include macOS (arm64) entries + filtered_entries = [entry for entry in matrix['include'] if entry['os'] == 'macos-latest'] # Then remove the os key from the entries for entry in filtered_entries: entry.pop('os') @@ -82,9 +83,11 @@ jobs: PYBAMM_VERSION: ${{ matrix.pybamm_version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Set up Python ${{ matrix.python_version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python_version }} @@ -107,7 +110,7 @@ jobs: # M-series Mac Mini build-apple-mseries: # This job filters the matrix JSON created in build_matrix.sh to provide - # a matrix of only macOS-14 (arm64) entries + # a matrix of only macOS (arm64) entries needs: [filter_pybamm_matrix] name: Build (MacOS M-series, Python ${{ matrix.python_version }}, PyBaMM ${{ matrix.pybamm_version }}) runs-on: [self-hosted, macOS, ARM64] @@ -125,7 +128,10 @@ jobs: rm -rf ./* || true rm -rf ./.??* || true - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Install python & create virtualenv shell: bash run: | diff --git a/.github/workflows/test_on_pull_request.yaml b/.github/workflows/test_on_pull_request.yaml index aa4633036..b5eed916e 100644 --- a/.github/workflows/test_on_pull_request.yaml +++ b/.github/workflows/test_on_pull_request.yaml @@ -20,9 +20,12 @@ jobs: style: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.11 @@ -38,14 +41,17 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest] - python-version: ["3.12"] + python-version: ["3.12", "3.13"] name: Integration tests (${{ matrix.os }} / Python ${{ matrix.python-version }}) steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -62,18 +68,21 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-15-intel, macos-14] - python-version: ["3.10", "3.11", "3.12"] - exclude: # We run the coverage tests on macos-14 with Python 3.12 - - os: macos-14 + os: [ubuntu-latest, windows-latest, macos-15-intel, macos-latest] + python-version: ["3.10", "3.11", "3.12", "3.13"] + exclude: # We run the coverage tests on macOS (arm64) with Python 3.12 + - os: macos-latest python-version: "3.12" name: Unit tests (${{ matrix.os }} / Python ${{ matrix.python-version }}) steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -90,15 +99,18 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-14] + os: [ubuntu-latest, windows-latest, macos-latest] python-version: ["3.12"] name: Test examples (${{ matrix.os }} / Python ${{ matrix.python-version }}) steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -115,15 +127,18 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-14] + os: [macos-latest] python-version: ["3.12"] name: Test notebooks (${{ matrix.os }} / Python ${{ matrix.python-version }}) steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -134,23 +149,24 @@ jobs: run: | nox -s notebooks - # Quick benchmarks on macos-14 + # Quick benchmarks on macOS (arm64) benchmarks: needs: style - runs-on: macos-14 + runs-on: macos-latest strategy: fail-fast: false name: Benchmarks steps: - name: Check out PyBOP repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 2 + persist-credentials: false - name: Set up Python 3.12 id: setup-python - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.12 @@ -165,20 +181,23 @@ jobs: asv machine --machine "GitHubRunner" asv run --machine "GitHubRunner" --quick --show-stderr - # Runs only on macos-14 with Python 3.12 + # Runs only on macOS (arm64) with Python 3.12 check_coverage: needs: style - runs-on: macos-14 + runs-on: macos-latest strategy: fail-fast: false - name: Coverage tests (macos-14 / Python 3.12) + name: Coverage tests (macos-latest / Python 3.12) steps: - name: Check out PyBOP repository - uses: actions/checkout@v4 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Set up Python 3.12 id: setup-python - uses: actions/setup-python@v4 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.12 cache: 'pip' @@ -187,11 +206,11 @@ jobs: - name: Install dependencies run: python -m pip install --upgrade pip nox[uv] - - name: Run coverage tests for macos-14 with Python 3.12 and generate report + - name: Run coverage tests for macos-latest with Python 3.12 and generate report run: nox -s coverage - name: Upload coverage report - uses: codecov/codecov-action@v4 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 with: fail_ci_if_error: true verbose: true diff --git a/.gitignore b/.gitignore index b2123edf4..a2b444c1d 100644 --- a/.gitignore +++ b/.gitignore @@ -323,3 +323,6 @@ results/ # Pycharm *.idea/ + +# JOSS +jats/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4a5bebe76..884952896 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ ci: repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.14.3" + rev: "v0.15.4" hooks: - id: ruff args: [--fix, --show-fixes] @@ -35,7 +35,7 @@ repos: - id: rst-inline-touching-normal - repo: https://github.com/kynan/nbstripout - rev: 0.8.1 + rev: 0.9.1 hooks: - id: nbstripout args: ['--keep-output', '--drop-empty-cells'] diff --git a/.readthedocs.yaml b/.readthedocs.yaml index aaeb9cfcf..cc41fe897 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -3,7 +3,12 @@ version: 2 build: os: ubuntu-20.04 tools: - python: "3.11" + python: "3.13" + jobs: + # https://docs.readthedocs.com/platform/latest/build-customization.html#install-dependencies-from-dependency-groups + install: + - pip install --upgrade pip + - pip install . --group docs # Build documentation in the "docs/" directory with Sphinx sphinx: @@ -13,8 +18,3 @@ formats: - htmlzip - pdf - epub - -python: - install: - - method: pip - path: .[docs] diff --git a/CHANGELOG.md b/CHANGELOG.md index f3264eb74..d8b111f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,36 @@ ## Breaking Changes +# [v26.3](https://github.com/pybop-team/PyBOP/tree/v26.3) - 2026-03-05 + +## Features + +- [#897](https://github.com/pybop-team/PyBOP/pull/897) - Adds separate `LogPrior`, `LogPDF` and `LogPosterior` classes and updates `set_target`. +- [#873](https://github.com/pybop-team/PyBOP/pull/873) - Adds methods for saving result and reconstructing result from saved data. `result.save`: saves entire python object using pickle. `result.save_data`: saves primarily the logger data and any other data required to reconstruct the result from the problem or the sampler (for `SamplingResult`). `Result.load_result`: reconstructs the `Result` object based on the underlying problem (or sampler for `SamplingResult`) and the data saved to file. +- [#862](https://github.com/pybop-team/PyBOP/pull/862) - Adds pybop.MarginalDistribution, pybop.MultivariateLogNormal. +- [#889](https://github.com/pybop-team/PyBOP/pull/889) - Adds methods for setting the initial state from a voltage to the grouped models. +- [#869](https://github.com/pybop-team/PyBOP/issues/869) - Adds methods for pre-processing current data for linear interpolation. +- [#868](https://github.com/pybop-team/PyBOP/pull/868) - Adds support for Python3.13 (NumPy restricted to <2.4, EP-BOLFI optimiser and PyProBE do not support Python 3.13). +- [#871](https://github.com/pybop-team/PyBOP/pull/871) - Adds a lumped thermal model called `CellTemperature`. +- [#846](https://github.com/pybop-team/PyBOP/pull/846) - Adds Bayesian optimisation framework and, as an example, the EP-BOLFI optimiser + +## Optimisations + +## Bug Fixes + +- [#890](https://github.com/pybop-team/PyBOP/pull/890) - Fix the assignment of parameters within a `MetaProblem`. +- [#847](https://github.com/pybop-team/PyBOP/pull/847) - Update readme and diagram of pybop components so that the diagram is displayed correctly in the readme. + +## Breaking Changes + +- [#894](https://github.com/pybop-team/PyBOP/pull/894) - Distinguish different uses of `sigma`, pass the covariance to the samplers, and add parameter `get_mean` and `get_std` functions. +- [#862](https://github.com/pybop-team/PyBOP/pull/862) - Removes MultivariateParameters class. Instead allows multivariate parameters to be passed via pybamm.ParameterValues (as a pybop.Parameter with a pybop.MarginalDistribution). The pybop.Parameters class now handles multivariate parameters. Multivariate distributions are now defined in the model space instead of the search space. +- [#878](https://github.com/pybop-team/PyBOP/pull/878) - Use "Current [A]" instead of "Current function [A]" in datasets and allow list of control functions. +- [#864](https://github.com/pybop-team/PyBOP/pull/864) - Remove `check_already_exists` from `ParameterValues` following PyBaMM PR 5339. +- [#860](https://github.com/pybop-team/PyBOP/pull/860) - Create a parent class for optimisation and sampling results, move `PosteriorSummary` attributes to the `SamplingResult` and deprecate the `pints.AdaptiveCovarianceMCMC` sampler. +- [#857](https://github.com/pybop-team/PyBOP/pull/857) - Deprecate the custom PyBaMM model build process for a simulation without an experiment and rename `batch_solve` as `solve_batch` to align with other functions. +- [#839](https://github.com/pybop-team/PyBOP/pull/839) - Renames 'prior' as 'distribution' ``for pybop.Parameter``. Allows construction of a ``pybop.Parameter`` with a distribution of type ``scipy.stats.distributions.rv_frozen``. Removes ``margins``, ``set_bounds``, ``remove_bounds`` from ``pybop.Parameter``. + # [v25.11](https://github.com/pybop-team/PyBOP/tree/v25.11) - 2025-11-24 ## Features @@ -22,6 +52,8 @@ ## Bug Fixes +- [#834](https://github.com/pybop-team/PyBOP/issues/834) - Finite difference calculations of the Hessian matrix are updated. A new notebbok file is added which demonstrates sensitivity analysis using SALib. + ## Breaking Changes - [#829](https://github.com/pybop-team/PyBOP/pull/829) - Create `SamplingResult` and best inputs property for results. diff --git a/CITATION.cff b/CITATION.cff index 1f7be5c7a..48c664c65 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -1,35 +1,64 @@ -cff-version: 1.2.0 -title: 'PyBOP: A Python package for battery model optimisation and parameterisation' -message: >- - If you use this software, please cite the article below. +cff-version: "1.2.0" authors: - - given-names: Brady - family-names: Planden +- family-names: Planden + given-names: Brady + orcid: "https://orcid.org/0000-0002-1082-9125" +- family-names: Courtier + given-names: Nicola E. + orcid: "https://orcid.org/0000-0002-5714-1096" +- family-names: Robinson + given-names: Martin + orcid: "https://orcid.org/0000-0002-1572-6782" +- family-names: Khetarpal + given-names: Agriya + orcid: "https://orcid.org/0000-0002-1112-1786" +- family-names: Planella + given-names: Ferran Brosa + orcid: "https://orcid.org/0000-0001-6363-2812" +- family-names: Howey + given-names: David A. + orcid: "https://orcid.org/0000-0002-0620-3955" +contact: +- family-names: Howey + given-names: David A. + orcid: "https://orcid.org/0000-0002-0620-3955" +doi: 10.5281/zenodo.17711656 +version: "26.3" # Update this alongside new releases +repository-code: "https://www.github.com/pybop-team/PyBOP" +message: If you use this software, please cite our article in the + Journal of Open Source Software. +preferred-citation: + authors: + - family-names: Planden + given-names: Brady orcid: "https://orcid.org/0000-0002-1082-9125" - - given-names: Nicola E. - family-names: Courtier + - family-names: Courtier + given-names: Nicola E. orcid: "https://orcid.org/0000-0002-5714-1096" - - given-names: Martin - family-names: Robinson + - family-names: Robinson + given-names: Martin orcid: "https://orcid.org/0000-0002-1572-6782" - - given-names: Agriya - family-names: Khetarpal + - family-names: Khetarpal + given-names: Agriya orcid: "https://orcid.org/0000-0002-1112-1786" - - given-names: Ferran - family-names: Brosa Planella + - family-names: Planella + given-names: Ferran Brosa orcid: "https://orcid.org/0000-0001-6363-2812" - - given-names: David A. - family-names: Howey + - family-names: Howey + given-names: David A. orcid: "https://orcid.org/0000-0002-0620-3955" - -keywords: - - "python" - - "battery models" - - "parameter inference" - - "optimization" - -journal: "arXiv" -date-released: 2024-12-20 -doi: 10.48550/arXiv.2412.15859 -version: "25.11" # Update this alongside new releases -repository-code: 'https://www.github.com/pybop-team/pybop' + date-published: 2025-12-03 + doi: 10.21105/joss.07874 + issn: 2475-9066 + issue: 116 + journal: Journal of Open Source Software + publisher: + name: Open Journals + start: 7874 + title: "PyBOP: A Python package for battery model optimisation and + parameterisation" + type: article + url: "https://joss.theoj.org/papers/10.21105/joss.07874" + volume: 10 +title: "PyBOP: A Python package for battery model optimisation and + parameterisation" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f252c1280..6fa2fcde3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,17 +4,19 @@ If you'd like to contribute to PyBOP, please have a look at the guidelines below ## Developer-Installation -To install PyBOP for development purposes, which includes the plotting dependencies, use the `[all]` and the `[dev]` flags as demonstrated below: +To install PyBOP for development purposes, which includes the plotting dependencies, use the `[all]` flag and the `dev` dependency group as demonstrated below: For `zsh`: ```sh -pip install -e '.[all,dev]' +pip install -e '.[all]' +pip install --group dev ``` For `bash`: ```sh -pip install -e .[all,dev] +pip install -e .[all] +pip install --group dev ``` ## Pre-commit checks @@ -117,8 +119,8 @@ On the other hand... We _do_ want to compare several tools, to generate document 1. Core PyBOP: A minimal set, including things like NumPy, SciPy, etc. All infrastructure should run against this set of dependencies, as well as any numerical methods we implement ourselves. 2. Extras: Other inference packages and their dependencies. Methods we don't want to implement ourselves, but do want to provide an interface to can have their dependencies added here. -3. Documentation generating code: Everything you need to generate and work on the docs. This is managed by the `[docs]` set of extras. -4. Development code: Everything you need to do PyBOP development (so all of the above packages, plus ruff and other testing tools). This is managed by the `[dev]` set of extras. +3. Documentation generating code: Everything you need to generate and work on the docs. This is managed by the `[docs]` dependency group. +4. Development code: Everything you need to do PyBOP development (so all of the above packages, plus ruff and other testing tools). This is managed by the `[dev]` dependency group. Only 'core pybop' is installed by default. The others have to be specified explicitly when running the installation command. diff --git a/README.md b/README.md index 909cec3aa..58c94e269 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ [![nbviewer](https://raw.githubusercontent.com/jupyter/design/master/logos/Badges/nbviewer_badge.svg)](https://nbviewer.org/github/pybop-team/PyBOP/tree/develop/examples/notebooks/) [![Static Badge](https://img.shields.io/badge/https%3A%2F%2Fpybop-team.github.io%2Fpybop-bench%2F?label=Benchmarks)](https://pybop-team.github.io/pybop-bench/) [![Releases](https://img.shields.io/github/v/release/pybop-team/PyBOP?color=gold)](https://github.com/pybop-team/PyBOP/releases) + [![DOI](https://joss.theoj.org/papers/10.21105/joss.07874/status.svg)](https://doi.org/10.21105/joss.07874) [Main Branch Examples](https://github.com/pybop-team/PyBOP/tree/main/examples) [Develop Branch Examples](https://github.com/pybop-team/PyBOP/tree/develop/examples) @@ -31,7 +32,7 @@ To understand how to update your use of PyBOP, please take a look at the example

- A diagram showing the main PyBOP classes: the Simulator and Cost, which together define the optimisation Problem, and the Optimiser or Sampler which generates the Result. + A diagram showing the main PyBOP classes: the Simulator and Cost, which together define the optimisation Problem, and the Optimiser or Sampler which generates the Result.

## 💻 Installation diff --git a/assets/PyBOP_components.svg b/assets/PyBOP_components.svg new file mode 100644 index 000000000..9210bce21 --- /dev/null +++ b/assets/PyBOP_components.svg @@ -0,0 +1,4 @@ + + + +
Problem
Problem
Cost
Cost
Simulator
Simulator
model
model
protocol
protocol
target
target
options
options
Optimiser / Sampler
Optimiser / Sampler
model parameters
model param...
cost parameters
cost parame...
Solution
Solution
Evaluation
Evaluation
Result
Result
evaluate
evaluate
run
run
solve
solve
Inputs
Inputs
Text is not SVG - cannot display
diff --git a/benchmarks/benchmark_optim_construction.py b/benchmarks/benchmark_optim_construction.py index 3298d59a8..6151ba015 100644 --- a/benchmarks/benchmark_optim_construction.py +++ b/benchmarks/benchmark_optim_construction.py @@ -42,7 +42,7 @@ def setup(self, model, parameter_set, optimiser): dataset = pybop.Dataset( { "Time [s]": t_eval, - "Current function [A]": solution["Current [A]"](t_eval), + "Current [A]": solution["Current [A]"](t_eval), "Voltage [V]": corrupt_values, } ) @@ -51,13 +51,11 @@ def setup(self, model, parameter_set, optimiser): parameter_values.update( { "Negative electrode active material volume fraction": pybop.Parameter( - prior=pybop.Gaussian(0.6, 0.02), - bounds=[0.375, 0.7], + pybop.Gaussian(0.6, 0.02, truncated_at=[0.375, 0.7]), initial_value=0.63, ), "Positive electrode active material volume fraction": pybop.Parameter( - prior=pybop.Gaussian(0.5, 0.02), - bounds=[0.375, 0.625], + pybop.Gaussian(0.5, 0.02, truncated_at=[0.375, 0.625]), initial_value=0.51, ), } diff --git a/benchmarks/benchmark_parameterisation.py b/benchmarks/benchmark_parameterisation.py index 8d3d66380..ac7334cd5 100644 --- a/benchmarks/benchmark_parameterisation.py +++ b/benchmarks/benchmark_parameterisation.py @@ -58,7 +58,7 @@ def setup(self, model, parameter_set, optimiser): dataset = pybop.Dataset( { "Time [s]": t_eval, - "Current function [A]": solution["Current [A]"](t_eval), + "Current [A]": solution["Current [A]"](t_eval), "Voltage [V]": corrupt_values, } ) @@ -67,12 +67,10 @@ def setup(self, model, parameter_set, optimiser): parameter_values.update( { "Negative electrode active material volume fraction": pybop.Parameter( - prior=pybop.Gaussian(0.55, 0.03), - bounds=[0.375, 0.7], + pybop.Gaussian(0.55, 0.03, truncated_at=[0.375, 0.7]), ), "Positive electrode active material volume fraction": pybop.Parameter( - prior=pybop.Gaussian(0.55, 0.03), - bounds=[0.375, 0.7], + pybop.Gaussian(0.55, 0.03, truncated_at=[0.375, 0.7]), ), } ) diff --git a/benchmarks/benchmark_track_parameterisation.py b/benchmarks/benchmark_track_parameterisation.py index cd4dc9bee..bc476c5ad 100644 --- a/benchmarks/benchmark_track_parameterisation.py +++ b/benchmarks/benchmark_track_parameterisation.py @@ -58,7 +58,7 @@ def setup(self, model, parameter_set, optimiser): dataset = pybop.Dataset( { "Time [s]": t_eval, - "Current function [A]": solution["Current [A]"](t_eval), + "Current [A]": solution["Current [A]"](t_eval), "Voltage [V]": corrupt_values, } ) @@ -67,12 +67,10 @@ def setup(self, model, parameter_set, optimiser): parameter_values.update( { "Negative electrode active material volume fraction": pybop.Parameter( - prior=pybop.Gaussian(0.55, 0.03), - bounds=[0.375, 0.7], + pybop.Gaussian(0.55, 0.03, truncated_at=[0.375, 0.7]), ), "Positive electrode active material volume fraction": pybop.Parameter( - prior=pybop.Gaussian(0.55, 0.03), - bounds=[0.375, 0.7], + pybop.Gaussian(0.55, 0.03, truncated_at=[0.375, 0.7]), ), } ) diff --git a/docs/_static/switcher.json b/docs/_static/switcher.json index 494c342cb..0c58dd605 100644 --- a/docs/_static/switcher.json +++ b/docs/_static/switcher.json @@ -4,11 +4,16 @@ "url": "https://pybop-docs.readthedocs.io/en/latest/" }, { - "name": "v25.11 (stable)", - "version": "v25.11", - "url": "https://pybop-docs.readthedocs.io/en/v25.11/", + "name": "v26.3 (stable)", + "version": "v26.3", + "url": "https://pybop-docs.readthedocs.io/en/v26.3/", "preferred": true }, + { + "name": "v25.11", + "version": "v25.11", + "url": "https://pybop-docs.readthedocs.io/en/v25.11/" + }, { "name": "v25.10", "version": "v25.10", diff --git a/docs/conf.py b/docs/conf.py index 89844517e..3252a5e74 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -22,11 +22,11 @@ "sphinx.ext.autosummary", "sphinx.ext.mathjax", "sphinx.ext.todo", - "sphinx.ext.viewcode", "sphinx_design", "sphinx_copybutton", - "autoapi.extension", + "autoapi.extension", # before ext.viewcode, sphinx-autoapi/issues/422 "sphinx_automodapi.automodapi", + "sphinx.ext.viewcode", # custom extensions "_extension.gallery_directive", # For extension examples and demos diff --git a/examples/data/README.md b/examples/data/README.md index 8f65d70c2..4d46dbbec 100644 --- a/examples/data/README.md +++ b/examples/data/README.md @@ -1,2 +1,2 @@ ## Data directory -This directory contains both the experimental and synthetic data used in the examples. +This directory contains the experimental data used in the examples. diff --git a/examples/notebooks/battery_parameterisation/echem_identification_pitfalls.ipynb b/examples/notebooks/battery_parameterisation/echem_identification_pitfalls.ipynb index f5b3795e4..1eb2a59c5 100644 --- a/examples/notebooks/battery_parameterisation/echem_identification_pitfalls.ipynb +++ b/examples/notebooks/battery_parameterisation/echem_identification_pitfalls.ipynb @@ -107,7 +107,7 @@ " window.PlotlyConfig = {MathJaxConfig: 'local'};\n", " if (window.MathJax && window.MathJax.Hub && window.MathJax.Hub.Config) {window.MathJax.Hub.Config({SVG: {font: \"STIX-Web\"}});}\n", " \n", - " \n", + " \n", " " ] }, @@ -118,9 +118,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -398,9 +401,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -160,10 +159,10 @@ { "data": { "text/html": [ - "
\n", - "
\n", + "
\n", - "
\n", + "
\n", - "
\n", + "
\n", - "
\n", + "
\n", - "
\n", + "
\n", - " \n", + " \n", " " ] }, @@ -268,9 +265,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -229,9 +216,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -133,9 +133,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -281,9 +279,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", + " \n", + " " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "pybop.plot.problem(problem, inputs=result.best_inputs, title=\"Optimised Comparison\");" + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": {}, + "source": [ + "## Performing sensitivity analysis\n", + "\n", + "We use the Sobol and Morris methods from SALib to perform sensitivity analysis on the two RC pair model. More information on the sensitivity analysis methods available from SALib can be found [here](https://salib.readthedocs.io/en/latest/)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "# Define the SALib problem dictionary for sensitivity analysis\n", + "salib_dict = {\n", + " \"names\": problem.parameters.names,\n", + " \"bounds\": problem.parameters.get_bounds_array(),\n", + " \"num_vars\": len(problem.parameters),\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "Let's create a helper function to evaluate the samples in a loop, using the `pybop.Problem` to evaluate the cost for each set of input parameters." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "def evaluate_samples(param_values: np.ndarray):\n", + " \"\"\"\n", + " param_values: np.ndarray shape (N, n_params)\n", + " returns: np.ndarray Y shape (N,)\n", + " \"\"\"\n", + " t0 = time.time()\n", + " N = param_values.shape[0]\n", + " cost_values = np.empty(N)\n", + " # Loop over 1/10th of the array at a time\n", + " n = 0\n", + " for params in np.array_split(param_values, 10):\n", + " n_i = len(params)\n", + " cost_values[n : n + n_i] = problem.evaluate(params).values\n", + " n += n_i\n", + " print(\n", + " f\"Eval {n}/{N}, elapsed time={time.time() - t0:.1f} s, last cost={cost_values[n - 1]:.6g}\"\n", + " )\n", + " return cost_values" + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "## Sobol sensitivity analysis\n", + "\n", + "Choose a base sample size N (use N=512 or higher for better estimates) and generate a set of samples." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Running model for Saltelli sample of size: (896, 5)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 90/896, elapsed time=0.5 s, last cost=7.59596\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 180/896, elapsed time=0.7 s, last cost=0.430763\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 270/896, elapsed time=0.9 s, last cost=0.461599\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 360/896, elapsed time=1.2 s, last cost=8.88535\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 450/896, elapsed time=1.4 s, last cost=4.74524\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 540/896, elapsed time=1.7 s, last cost=0.127041\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 629/896, elapsed time=2.0 s, last cost=0.367499\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 718/896, elapsed time=2.3 s, last cost=1.30012\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 807/896, elapsed time=2.6 s, last cost=2.07151\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Eval 896/896, elapsed time=2.9 s, last cost=5.21243\n" + ] + } + ], + "source": [ + "N = 128\n", + "param_values = sobol_sample.sample(salib_dict, N, calc_second_order=False)\n", + "\n", + "print(\"Running model for Saltelli sample of size:\", param_values.shape)\n", + "Y_saltelli = evaluate_samples(param_values)" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "Use the samples to analyse the Sobol indices." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " ST ST_conf\n", + "R0 [Ohm] 0.955425 0.167954\n", + "R1 [Ohm] 0.025830 0.009102\n", + "R2 [Ohm] 0.000931 0.000467\n", + "Tau1 [s] 0.008554 0.003997\n", + "Tau2 [s] 0.001098 0.000620\n", + " S1 S1_conf\n", + "R0 [Ohm] 0.943294 0.201951\n", + "R1 [Ohm] 0.024291 0.039169\n", + "R2 [Ohm] -0.001352 0.006306\n", + "Tau1 [s] 0.007184 0.022051\n", + "Tau2 [s] 0.001215 0.006819\n", + "Sobol S1: [ 0.94329445 0.02429073 -0.0013523 0.00718427 0.00121541]\n", + "Sobol ST: [9.55424615e-01 2.58303601e-02 9.30643934e-04 8.55433212e-03\n", + " 1.09805930e-03]\n" + ] + } + ], + "source": [ + "Si = sobol.analyze(\n", + " salib_dict, Y_saltelli, calc_second_order=False, print_to_console=True\n", + ")\n", + "print(\"Sobol S1:\", Si[\"S1\"])\n", + "print(\"Sobol ST:\", Si[\"ST\"])" + ] + }, + { + "cell_type": "markdown", + "id": "22", + "metadata": {}, + "source": [ + "Plot the results." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAv0AAAHSCAYAAAB/1HRqAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAWDVJREFUeJzt3Xd8FVX+//H3TU9IbhIIBEKHpQoqSpEmICBNBAsifOlIsVPUlZWqKCquKCsIGhVwlaaCbZGmIAK6UiwbQRYlQGgBQnpP5vdHfplNSG4IJDc3mbyej8d9eHNnzpnP3DHhfeeeOWMzDMMQAAAAAMtyc3UBAAAAAJyL0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFufh6gJQcWRnZ+v06dMKCAiQzWZzdTkAAACVnmEYSkhIUFhYmNzcHJ/PJ/Sj2E6fPq26deu6ugwAAABc5uTJk6pTp47D5YR+FFtAQICknP+p7Ha7i6sBAABAfHy86tata+Y0Rwj9KLbcIT12u53QDwAAUI5caeg1F/ICAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4jxcXQAqnld/vigf/3RXlwEAAK7B021CXF0CXIAz/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOII/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOII/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAszpKhf8yYMbLZbPkeU6ZMceo2IyMjzW1FRkY6dVvXavDgwQXel7lz57q6LAAAADjZVYf+uXPnFgiONptN3t7eCgsLU58+fRQeHq6MjIxi9ffJJ5+oT58+qlGjhnx8fNSwYUNNmjRJR48eveqduZyPj49CQ0MVGhoqu91e5Lqpqalavny57rjjDtWrV0++vr4KDAxUixYtNHHiRH3zzTclrsfVgoODzffD09PT1eUAAACgjHiUpHFoaKj5PCEhQWfOnNGZM2e0ZcsWLV++XFu2bFFwcHChbQ3D0Pjx4/Xee+9Jktzc3OTv76/IyEi99dZb+uc//6n169erf//+11zf0KFDtWLFiiuut3XrVo0bN05RUVHma3a7XWlpaTp8+LAOHz6st99+W/369dP777+vatWqXXNNrpT7XktS9+7dtXPnThdWAwAAgLJSouE9Z8+eNR9JSUk6fvy4JkyYIEnat2+fHnvsMYdtFy5caIbQOXPmKC4uTnFxcTp8+LA6deqk5ORk3XfffTp27FhJSryitWvXqn///oqKilLt2rUVHh6umJgYxcXFKTU1VYcOHdKUKVPk4eGhTZs26ZZbblF0dLRTawIAAABKU6mO6a9Xr57eeust3XbbbZKkdevWKTExscB6ly5d0vz58yVJkyZN0ty5c+Xv7y9Jatasmb744gvVrFlTSUlJmj17dmmWmM+hQ4c0btw4ZWZmqnXr1jp48KDGjx+f79uJ5s2ba9GiRfr000/l5eWlo0ePavjw4U6rCQAAAChtTrmQt2/fvpKk9PR0/fe//y2wfMOGDUpISJAkzZgxo8Dy4OBgTZ48WZL08ccfKykpyRllaubMmUpOTpa3t7fWr1+v6tWrO1y3f//+mjlzpiRp+/bt+vLLL4vs+9y5c3r88cfVsGFD89qC+++/X4cPHy50/R07dpjXR0jSL7/8omHDhiksLEy+vr5q0aKFXnnlFWVmZpptdu/ercGDB6tWrVry8fFRq1attGTJEhmGcbVvBQAAACysRGP6HckbOrOysgos37p1qySpZcuWql+/fqF99OvXT3PnzlVKSoq+++479enTp1RrPHPmjDZu3ChJGjZsmJo1a3bFNlOnTtXChQuVkJCgJUuWaMCAAYWuFxERoXHjxik6Olp+fn6SpOjoaK1du1abNm3St99+qxtuuMHhdjZt2qS7775bqampCgwMNK8tePLJJ7V//36tXr1a4eHhmjx5srKzs83rDyIiIvTII4/o5MmTevHFF6/+TQEAoBxIT3HOyT7kSErydXUJllalShVXl1Aop4T+zZs3S5JsNpsaNmxYYPl//vMfSVKrVq0c9pF3WURERKmH/h07dig7O1uSdM899xSrjb+/v26//XZ9/PHH2rVrlzIzM+XhUfAtHDlypFq2bKkvv/xSbdu2VWZmpnbs2KFRo0bpzJkzevTRR/Xtt9863M7w4cM1aNAgvfzyy6pXr54SEhK0YMECLViwQGvWrNENN9yg2bNn66GHHtLMmTNVo0YNXbp0SdOmTdOKFSu0cOFCjRs3Tk2bNr22N+f/S0tLU1pamvlzfHx8ifoDAKA45nRu4OoSLG2OqwuwuPI64qJUh/ecOHFCEydO1Ndffy1JGjhwYKEz3Zw+fVqSVLt2bYd9+fn5KSgoKN/6pSkiIsJ83qZNm2K3u/HGGyVJiYmJOn78eKHrhIaGauvWrWrbtq0kycPDQ7169dLy5cslSbt27co3U9Dl2rVrp9WrV6tevXqSpICAAL3wwgvq2rWrpJwhUaNHj9bixYtVo0YNSTlDosLDw9WwYUNlZ2dr3bp1xd4nRxYsWKDAwEDzUbdu3RL3CQAAgLJXojP9NWvWNJ8nJCQoOTnZ/Ll58+ZaunRpoe1yx/PnDn1xxM/PT7Gxseb6penixYvm86uZgjMkJCRfH40bNy6wzvTp0+XrW/Crs379+snLy0vp6en69ddfVadOnUK38de//tUc259Xnz59tGvXLkmFXwvh7u6unj17Kjw8XL/88kux98mRGTNmaNq0aebP8fHxBH8AgNPN2x3p6hIsbfoNIVdeCZZTotB/7ty5Ql8fNWqUli9fLh8fn5J0X2F16NCh0Nc9PDxUvXp1nTp1SjExMQ7bt2/fvtDXc++LULVqVTVq1KjIdS5dunQ1JRfK29tb3t7eJe4HAICr4eVbPsdEW0V5HXMO5yrR8B7DMGQYhrKzs3X69GktW7ZMQUFBWrVqld544w2H7QICAiQp3zcDhcldnrt+acp7dj/vWf8ruXDhQqF95FVUvbnXABR1x2JH7XPblrR/AAAAVC6lMqbfZrOpVq1amjRpkjZs2CCbzaannnrKHNt/ubCwMEnSqVOnHPaZnJys2NjYfOuXppYtW5rPDxw4UOx2Bw8elJRzUa+jmYcAAACA8qTU5+nv3r27Ro4cKcMw9OijjxY6ZWfuzDy5s/gUJu+y6667rrTLVI8ePeTmlrP7H3/8cbHaJCYmmtONdu3atdCZewAAAIDyxik355o9e7bc3d3122+/aeXKlQWW9+7dW1LOHXFPnDhRaB9fffWVJMnX11ddunQp9Rpr1aqlQYMGSZLWrFmj33///YptFi1aZF5U/NBDD5V6TQAAAIAzOCX0N27cWEOHDpUkPffccwXGl991110KCAiQYRiF3kQqNjZWy5Ytk5Qzh76zLjh57rnn5Ovrq7S0NA0ZMiTfeP3Lbdq0SfPnz5eU8y2BoxtzAQAAAOWNU0K/lDPdo81mU2RkpN555518y4KDgzVz5kxJ0rJly/Tss88qKSnn7ntHjhzRwIEDdebMGVWpUkXPPvuss0rUddddp/DwcLm7u+vXX39VmzZt9O6775rXEuTWM23aNN15551KT09Xo0aN9OGHHxY6pSYAAABQHjkt9Ldq1Up33nmnJOn555/Pd2dXSXryySc1duxYGYahOXPmKDAwUEFBQWrWrJm+++47+fn5ad26dYXe0bc0DR8+XF988YXCwsIUFRWl8ePHKzg4WEFBQfL19VWzZs20aNEiZWZm6vbbb9f333+f7/4EAAAAQHnntNAvSc8884wkKSoqyrwbbS6bzaZ3331XH330kXr37q3g4GClpqaqfv36mjBhgn7++Wf179/fmeWZ+vbtq6NHj2rp0qXq37+/ateurdTUVHl6eqpp06YaP368tm3bps2bN6t69eplUhMAAABQWmyGYRiuLqK0jRkzRitXrtTo0aO1YsUKV5dTLnXv3l07d+7UnDlzNHfu3GK1iY+PV2BgoOZ8+6d8/Ev/3gkAAMD5nm7DHXmtJDefxcXFyW63O1zPqWf6AQAAALiepUP/ypUrZbPZZLPZNGXKFFeX43KDBw8234+dO3e6uhwAAACUEUveXSowMFChoaH5Xivq647KIjg4uMD74u/v76JqAAAAUFYsOaYfzsGYfgAAKj7G9FsLY/oBAAAASCL0AwAAAJZH6AcAAAAsjtAPAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOII/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4D1cXgIpn2g3VZLfbXV0GAAAAiokz/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOII/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4D1cXgIrn1Z8vysc//araPN0mxEnVAAAA4Eo40w8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxlgz9Y8aMkc1my/eYMmWKU7cZGRlpbisyMtKp27pWgwcPLvC+zJ0719VlAQAAwMmuOvTPnTu3QHC02Wzy9vZWWFiY+vTpo/DwcGVkZBTZz4EDB/Tmm29qwoQJuummm+Tt7S2bzaYGDRpc674U4OPjo9DQUIWGhsputxe5bmpqqpYvX6477rhD9erVk6+vrwIDA9WiRQtNnDhR33zzTanV5SrBwcHm++Hp6enqcgAAAFBGPErSODQ01HyekJCgM2fO6MyZM9qyZYuWL1+uLVu2KDg4uNC2d999t44fP16SzV/R0KFDtWLFiiuut3XrVo0bN05RUVHma3a7XWlpaTp8+LAOHz6st99+W/369dP777+vatWqObFq53nvvffM5927d9fOnTtdWA0AAADKSomG95w9e9Z8JCUl6fjx45owYYIkad++fXrssccctvXy8tKNN96ocePG6Y033tDIkSNLUso1W7t2rfr376+oqCjVrl1b4eHhiomJUVxcnFJTU3Xo0CFNmTJFHh4e2rRpk2655RZFR0e7pFYAAADgWpToTP/l6tWrp7feekt//PGHvv76a61bt05vvvmm/P39C6x76NAhubu7mz+fP3++NEsplkOHDmncuHHKzMxU69attX37dlWvXj3fOs2bN9eiRYvUu3dv3XXXXTp69KiGDx+ubdu2lXm9AAAAwLVwyoW8ffv2lSSlp6frv//9b6Hr5A38rjJz5kwlJyfL29tb69evLxD48+rfv79mzpwpSdq+fbu+/PLLIvs+d+6cHn/8cTVs2NC8tuD+++/X4cOHC11/x44d5vURkvTLL79o2LBhCgsLk6+vr1q0aKFXXnlFmZmZZpvdu3dr8ODBqlWrlnx8fNSqVSstWbJEhmFc7VsBAAAAC3NK6M8bOrOyspyxiRI7c+aMNm7cKEkaNmyYmjVrdsU2U6dOVUBAgCRpyZIlDteLiIjQ9ddfr8WLF5tDgaKjo7V27Vp16NBBP//8c5Hb2bRpkzp06KA1a9YoOTnZvLbgySefNIdBhYeHq1u3bvrss8+UkpKitLQ0RURE6JFHHtGMGTOK8xYAAACgknBK6N+8ebMkyWazqWHDhs7YRInt2LFD2dnZkqR77rmnWG38/f11++23S5J27dqV76x7XiNHjlSTJk30448/KikpSYmJidq6datq1aql+Ph4Pfroo0VuZ/jw4Ro0aJCOHz+u2NhYxcXFmUF+zZo1evHFF/XQQw/poYce0tmzZxUbG6uYmBiNGTNGkrRw4UIdOXKkWPvkDOkpSQUeSUkFHwAAACgbpRr6T5w4oYkTJ+rrr7+WJA0cOLDcznQTERFhPm/Tpk2x2914442SpMTERIezD4WGhmrr1q1q27atJMnDw0O9evXS8uXLJeV8YMg7U9Dl2rVrp9WrV6tevXqSpICAAL3wwgvq2rWrJGnGjBkaPXq0Fi9erBo1akjKmY4zPDxcDRs2VHZ2ttatW1fsfXIkLS1N8fHx+R7FMadzgwIPf3//Ag8AAACUjRKF/po1a5qPKlWqqH79+nr77bcl5VwAu3Tp0lIp0hkuXrxoPr+aDyYhISGF9pHX9OnT5evrW+D1fv36ycvLS5L066+/OtzGX//6V3Nsf159+vQxnxc2hMfd3V09e/aUlHNNQEktWLBAgYGB5qNu3bol7hMAAABlr0Sz95w7d67Q10eNGqXly5fLx8enJN1XWB06dCj0dQ8PD1WvXl2nTp1STEyMw/bt27cv9PXc+yJUrVpVjRo1KnKdS5cuXU3JhZoxY4amTZtm/hwfH1+s4D9vd2SB16bfEFJwRQAAAJSJEp3pNwxDhmEoOztbp0+f1rJlyxQUFKRVq1bpjTfeKK0anSLv2X1HZ+wLc+HChUL7yCv3Yt/CeHjkfM4q6o7Fjtrnti1p/8Xl7e0tu92e71EcXr5VCjyqVCn4AAAAQNkolTH9NptNtWrV0qRJk7RhwwbZbDY99dRT5tj+8qhly5bm8wMHDhS73cGDByXlXNRbv379Uq8LAAAAKG2lPntP9+7dNXLkSBmGoUcffbTcTtnZo0cPubnl7P7HH39crDa5s/BIUteuXc2z6gAAAEB55pQpO2fPni13d3f99ttvWrlypTM2UWK1atXSoEGDJOVMg/n7779fsc2iRYuUkJAgSXrooYecWh8AAABQWpwS+hs3bqyhQ4dKkp577rlSGV/uDM8995x8fX2VlpamIUOG5Buvf7lNmzZp/vz5knK+JRgwYEBZlQkAAACUiFNCv5Qz84vNZlNkZKTeeeedAsuTk5N14cIF85GcnCxJys7Ozvd6UUG8pK677jqFh4fL3d1dv/76q9q0aaN3331XsbGx5jpHjhzRtGnTdOeddyo9PV2NGjXShx9+WOiUmgAAAEB55LTQ36pVK915552SpOeff15paWn5lr/88suqXr26+Vi4cKEk6eTJk/ler169urNKlJRz99svvvhCYWFhioqK0vjx4xUcHKygoCD5+vqqWbNmWrRokTIzM3X77bfr+++/V82aNZ1aEwAAAFCanBb6JemZZ56RJEVFRZl3oy2P+vbtq6NHj2rp0qXq37+/ateurdTUVHl6eqpp06YaP368tm3bps2bNzv9QwgAAABQ2myGYRiuLqK0jRkzRitXrtTo0aO1YsUKV5dTLnXv3l07d+7UnDlzNHfu3GK1iY+PV2BgoOZ8+6d8/B3fK6AwT7fh5lwAAAClLTefxcXFFXlPJaee6QcAAADgepYO/StXrpTNZpPNZtOUKVNcXY7LDR482Hw/du7c6epyAAAAUEYseXepwMBAhYaG5nutqK87Kovg4OAC74u/v7+LqgEAAEBZseSYfjgHY/oBAADKF8b0AwAAAJBE6AcAAAAsj9APAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOII/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOI8XF0AKp5pN1ST3W53dRkAAAAoJs70AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOII/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOII/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOII/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAsjtAPAAAAWByhHwAAALA4Qj8AAABgcYR+AAAAwOII/QAAAIDFEfoBAAAAiyP0AwAAABZH6AcAAAAszpKhf+7cubLZbPkegwcPdvp2c7e1Y8cOp2/rWkyZMqXA+zJmzBhXlwUAAAAnu+rQX1igttls8vb2VlhYmPr06aPw8HBlZGQ47CMrK0vbt2/XE088oU6dOqlatWry9PRUcHCwOnXqpBdeeEGXLl0q0Y5Jkqenp0JDQxUaGqrg4OAi183KytIHH3ygIUOGqGHDhqpSpYoCAgL0l7/8RSNHjtSnn35a4npczW63m++Hj4+Pq8sBAABAGfEoSePQ0FDzeUJCgs6cOaMzZ85oy5YtWr58ubZs2VJo2J48ebLCw8PNn93c3GS32xUbG6u9e/dq7969Wrx4sTZu3Khbbrnlmuvr1KlTsc66Hzx4UMOHD9fhw4fN1/z9/ZWdna0//vhDf/zxh/75z3+qffv2Wrt2rRo0aHDNNbnSs88+q2effVaSNGbMGK1cudLFFQEAAKAslGh4z9mzZ81HUlKSjh8/rgkTJkiS9u3bp8cee6zQdhkZGapRo4aeeOIJ7dmzR6mpqbp06ZISEhIUHh6uatWq6dy5cxowYIDOnz9fkhKv6Ntvv1XXrl11+PBhBQcH6+9//7vOnDmjhIQEJSUlKTIyUvPmzZOfn5/+/e9/q0OHDvk+HAAAAADlXamO6a9Xr57eeust3XbbbZKkdevWKTExscB6Dz74oCIjI7Vw4UJ17NhRnp6eknLOro8fP16ff/65JCkmJkbLly8vzRLziY6O1tChQ5WUlKQ6deroxx9/1LRp01SzZk1znfr162v27NnauXOngoKCFB0drXvvvVepqalOqwsAAAAoTU65kLdv376SpPT0dP33v/8tsLxDhw7y9fV12L5jx45q2bKlJOnHH390RomSpJdeeklnz56VJL3//vtq3Lixw3Xbtm2rxYsXS5IiIiL0zjvvFNl3QkKCZs6cqebNm8vX11fVqlXTHXfcoR9++KHQ9SMjI83rIyIjI81vTerVqycfHx81btxYM2fOVFJSktnmP//5j0aMGKG6devKx8dHTZo00fz584u8ngIAAACVj1NCv2EY5vOsrKxr6iP3QtNrbX8lGRkZ5nUF3bt3V/fu3a/YZsSIEeYHgyVLljhc78yZM7rpppv0/PPP6/jx43Jzc1NMTIy+/PJL3XrrrdqyZUuR2zlw4IBuvPFGhYeHKy4uTpmZmfrzzz/1/PPPq1+/fsrIyNCXX36pDh066IMPPlBCQoLS09N19OhRzZo1SyNHjiz+GwEAAADLc0ro37x5s6ScKSwbNmx41e0vXLig//znP5Kk1q1bl2ptufbt26f4+HhJ0j333FOsNnmn/jx06JDOnTtX6HoPP/ywvLy89PXXXyspKUmJiYn697//rWbNmik9PV0TJ05Udna2w+2MHz9eN998syIiIhQXF6eEhAQtXrxY7u7u2rVrl5599ln93//9nwYOHKjIyEjFxsYqPj5ezzzzjCRp7dq12rZt21W8G4VLS0tTfHx8vgcAAAAqnlIN/SdOnNDEiRP19ddfS5IGDhyoatWqXXU/s2bNUnp6ujw8PJw2j3xERIT5vE2bNsVud+ONN5rPcz+YXM7Dw0PffPONevToITc3N9lsNrVr107r16+XJB0/flx79+51uI3atWvryy+/NIc4+fr66tFHH9Xw4cMlSfPnz1f79u21evVq1a9fX1LO9RDz589X165dJUlr1qwp9j45smDBAgUGBpqPunXrlrhPAAAAlL0Shf6aNWuajypVqqh+/fp6++23JUnNmzfX0qVLr7rPtWvXatmyZZKkJ598Us2aNStJiQ5dvHjRfH41H0xCQkIK7SOviRMnqkaNGgVeb926tfnNxy+//OJwG1OnTpW3t3eB1/v06WM+f/rpp2Wz2RyuU1T/xTVjxgzFxcWZj5MnT5a4TwAAAJS9Es3T72h4y6hRo7R8+fKrvgHUrl27NHbsWEnSbbfdZs4pX9F06NDB4bKwsDAdO3ZMMTExDtdp3759oa/nvS9Cu3btilynNG5u5u3tXeiHDwAAAFQsJTrTbxiGDMNQdna2Tp8+rWXLlikoKEirVq3SG2+8cVV97d27VwMGDFBKSoo6d+6sTz/9VB4eJfpMUqS8Z/cdnbEvzIULFwrtI6+AgACH7XP3qagZdhy1z/t+XGkdZvABAABArlIZ02+z2VSrVi1NmjRJGzZskM1m01NPPWWO7b+SvXv3qm/fvkpISFDHjh21adMm+fv7l0ZpDuWOl5dyZssproMHD5rPr7vuulKtCQAAAHCGUp+9p3v37ho5cqQMw9Cjjz56xSk39+zZoz59+ig+Pl4dO3bU5s2bizxTXlratWtnbufjjz8uVhvDMLRx40ZJUosWLfLdxAsAAAAor5wyZefs2bPl7u6u3377TStXrnS43p49e/Kd4f/qq6/KJPBLkqenpx544AFJ0s6dO7Vjx44rtvnnP/+pP//8U5L00EMPObM8AAAAoNQ4JfQ3btxYQ4cOlSQ999xzhY4vzxv4O3XqpM2bN8tutzujHIf++te/mrPsjBw5Un/88YfDdffv36/HHntMUs5Z/vHjx5dJjQAAAEBJOSX0SznTPdpsNkVGRuqdd97Jt+z77783A3/nzp3L9Ax/XqGhoVq7dq38/PwUFRWldu3aadGiRflmJTp58qSee+453XrrrYqNjVVISIg++ugj+fr6lnm9AAAAwLVwWuhv1aqV7rzzTknS888/r7S0NHPZ3/72NyUkJEiSfvvtNzVp0iTfnP95H46mpiwt3bt3186dO9W0aVNdunRJ06ZNU82aNWW32+Xv76969epp9uzZSk5OVtu2bfX999/nuwgYAAAAKO+cFvol6ZlnnpEkRUVFafny5ebr2dnZ5vNLly7p3LlzDh/nz593ZomSpLZt2yoiIkKrVq3S3Xffrfr16yszM1OS1KhRIw0fPlyffPKJ/v3vf6tx48ZOrwcAAAAoTTbDMAxXF1Ha5s6dq3nz5qlbt27FukC3MhozZoxWrlyp0aNHa8WKFcVqEx8fr8DAQMXFxZX59RcAAAAoqLj5zKln+gEAAAC4nqVD/86dO2Wz2WSz2TR48GBXl+NyU6ZMMd+PoqZSBQAAgLV4uLoAZ/D391doaGi+14KDg11UTflht9sLvC+BgYEuqgYAAABlxZJj+uEcjOkHAAAoXxjTDwAAAEASoR8AAACwPEI/AAAAYHGEfgAAAMDiCP0AAACAxRH6AQAAAIsj9AMAAAAWR+gHAAAALI7QDwAAAFgcoR8AAACwOEI/AAAAYHGEfgAAAMDiCP0AAACAxRH6AQAAAIsj9AMAAAAWR+gHAAAALI7QDwAAAFgcoR8AAACwOEI/AAAAYHGEfgAAAMDiCP0AAACAxRH6AQAAAIsj9AMAAAAWR+gHAAAALI7QDwAAAFgcoR8AAACwOEI/AAAAYHGEfgAAAMDiPFxdACqeV3++KB//dPPnp9uEuLAaAAAAXAln+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0F/OjBkzRjabLd9jypQppdL3a6+9VqDv7t27l0rfAAAAKL9cHvovD6FX81ixYoVLaj5w4IDefPNNTZgwQTfddJO8vb1ls9nUoEGDUtuGj4+PQkNDFRoaKrvdXip9VqlSxeyzSpUqpdInAAAAyj8PVxcQGhpa6OuJiYlKSkoqch1fX1+n1VWUu+++W8ePH3fqNoYOHVrqH2omTJigCRMmSJLmzp2refPmlWr/AAAAKJ9cHvrPnj1b6Ot5Q6mjdVzFy8tLN954o2666SbddNNN+uGHH/T++++7uiwAAACgUC4P/RXRoUOH5O7ubv58/vx5F1YDAAAAFM3lY/qvRUZGhj777DNNnDhRbdu2Va1ateTl5aUaNWqoT58+Wr16tQzDKLTtihUrrjj+PjIy0rxuIDIyssDyvIHfldauXat+/fopNDRUnp6eCgoKUpMmTXTnnXdqyZIlSk1NdXWJAAAAKAcq5Jn+3bt3a9CgQebPdrtdPj4+On/+vLZs2aItW7Zow4YNWrNmjdzcKuTnmisaN26c3nvvPfNnf39/ZWRk6OjRozp69Kg+//xzDRgwoFQvLgYAAEDFVCETsZ+fnyZNmqStW7cqLi5OcXFxio+P18WLF/X666/Lbrdr/fr1euONN1xdqlN89913eu+99+Tm5qaXXnpJFy9eVEJCgpKSknThwgVt3rxZo0ePlpeXl1PrSE9JUnpKkpKSksyLrgEAAFD+VMgz/e3bt1f79u0LvF61alU99thjCgsL05AhQ7R48WI99thjLqjQufbs2SNJ6tWrl5566ql8y6pVq6bbb79dt99+e4m3k5aWprS0NPPn+Pj4fMvndG6Q89///7OjIVUAAABwrQp5pv9KBgwYIEn6448/yt3MP6UhKChIUs4FxFlZWU7bzoIFCxQYGGg+6tat67RtAQAAwHkqbOhPSEjQwoUL1a1bN9WoUUNeXl7mxbd+fn7melFRUS6s0jl69uwpHx8fHTx4UF27dtU777yjY8eOlfp2ZsyYYQ6fiouL08mTJ/Mtn7c7UvN2RyoxMVGJiYmlvn0AAACUjgo5vOfIkSPq2bNnvkDv5+enoKAg88Ldc+fOSZIlx5o3btxY4eHhmjx5svbu3au9e/dKkqpXr64ePXpo+PDhuvPOO2Wz2Uq0HW9vb3l7eztc7uWbc1df7u4LAABQvlXIM/1jx45VVFSUGjRooPXr1+vixYtKSkpSdHS0zp49q1OnTpnrWnWc+f/93//p+PHjWrZsmYYOHaq6devq/PnzWrdunQYPHqxu3boVGIMPAACAyqnChf6TJ0+aF7KuXr1a9957r6pWrZpvnaLG8Xt45Hy5UdQc9nFxcaVQqfNVrVpVkyZN0po1a3TixAkdPXpUTz/9tGw2m3bt2qW5c+e6ukQAAACUAxUy9Odq06ZNoets27bNYfvg4GBJUnR0dL6ZafL64YcfSlCh6zRu3FgLFizQ8OHDJUlbt251cUUAAAAoDypc6A8MDDSf//zzzwWWJyQkaP78+Q7b33DDDZJyhv1s2LChwPKUlBQtWrSoFCp1HkcfVnL5+vpKkmVvTAYAAICrU+FSYYsWLVSvXj1JOXel3b9/v7ls79696t69uy5duuSwfZ06ddSlSxdJ0rRp07Rt2zZz2sv9+/erV69eio6OLrKG5ORkXbhwwXwkJydLkrKzs/O9fuHChRLtqyOPPPKI7rvvPn388cf5ak1MTNSyZcu0atUqSf+buhQAAACVW4WbvcfNzU1LlizRXXfdpYiICLVt29acojM5OVlVqlTRp59+ql69ejns4x//+Ie6deumM2fOqHfv3vLx8ZG7u7uSkpIUGhqq999/v8jA/PLLL2vevHkFXj958qSqV6+e7zVnXEickZGh9evXa/369ZIkf39/eXh4KDY21lynS5cueuaZZ0p92wAAAKh4KtyZfkm644479O2332rAgAEKCgpSZmamQkJCNHbsWO3fv189e/Yssv2NN96oH374Qffff79q1Kih7OxshYSE6OGHH9ZPP/2kli1bltGeXJtZs2Zp8eLFuuuuu9S8eXN5eHgoMTFRNWrUUO/evfXuu+9qx44dTKUJAAAASZLNsOqclhXUmDFjtHLlSo0ePVorVqxw2nbmzp2refPmqVu3btqxY0ex2sTHxyswMFBzvv1TPv4B5utPtwlxUpUAAAAoSm4+i4uLk91ud7hehTzTDwAAAKD4CP3l1MqVK2Wz2WSz2TRlypRS6fO1114z+yzsmgQAAABYU4W7kNfqAgMDFRoamu+1or6quRpVqlQp0PflNzYDAACA9TCmH8XGmH4AAIDyhTH9AAAAACQR+gEAAADLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDgPVxeAimfaDdVkt9tdXQYAAACKiTP9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxhH4AAADA4gj9AAAAgMUR+gEAAACLI/QDAAAAFkfoBwAAACyO0A8AAABYHKEfAAAAsDhCPwAAAGBxHq4uAJWLYRjKyMhQdna2q0sBLMfNzU2enp6y2WyuLgUAUM4Q+lEmsrKydOHCBSUkJCgjI8PV5QCW5enpqYCAAIWEhMjd3d3V5QAAyglCP5wuKytLJ0+eVFpamgIDA+Xv7y93d3fORgKlyDAMZWVlKTExUbGxsUpJSVHdunUJ/gAASYT+cmfu3LmaN29evtcGDRqkjRs3lrjvjRs36q677sr3Wv369RUZGVnivoty4cIFpaWlqV69evL19XXqtoDKzt/fX4GBgTpx4oQuXLig0NBQV5cEACgHXH4hr81mu+bHihUryrzerKwsbd++XU888YQ6deqkatWqydPTU8HBwerUqZNeeOEFXbp0qcTb8fT0VGhoqEJDQxUcHFwKlUs+Pj5mn3a7vVT6vBLDMJSQkKDAwEACP1BGfH19ZbfblZCQIMMwXF0OAKAccPmZfkdnoRITE5WUlFTkOq4IkZMnT1Z4eLj5s5ubm+x2u2JjY7V3717t3btXixcv1saNG3XLLbdc83Y6deqkHTt2lELF/9O3b1+dPXtWkrRixQqNHTu2VPsvTEZGhjIyMuTv7+/0bQH4n4CAAMXGxiojI0NeXl6uLgcA4GIuD/25IfRyeYe5OFrHFTIyMlSjRg2NGjVKd999t9q2bStPT08lJiZq7dq1+utf/6pz585pwIABOnz4sKpXr+7qkl0qd5YexhUDZSv3d46ZsgAAUjkY3lPRPPjgg4qMjNTChQvVsWNHeXp6SsoZRzt+/Hh9/vnnkqSYmBgtX77claWWK1y0C5QtfucAAHlVyNCfkZGhzz77TBMnTlTbtm1Vq1YteXl5qUaNGurTp49Wr17tcBzrihUrZLPZ1KBBA4f9R0ZGmtcNXH6Ra4cOHYocVtSxY0e1bNlSkvTjjz9e9b5djc2bN+vuu+9WnTp15OXlJbvdrkaNGun222/XK6+8opiYGKduHwAAABWDy4f3XIvdu3dr0KBB5s92u10+Pj46f/68tmzZoi1btmjDhg1as2aN3NzK/nONj4+PpJyLfp3l2Wef1Zw5c8yf/fz8ZBiGjh07pmPHjmnr1q1q27atunfv7rQaAAAAUDFUyDP9fn5+mjRpkrZu3aq4uDjFxcUpPj5eFy9e1Ouvvy673a7169frjTfeKPPaLly4oP/85z+SpNatWztlG8ePHzevd5g2bZpOnTqlpKQkJSQkKDY2Vrt27dJDDz2kgICAEm0nLS1N8fHx+R4AAACoeCpk6G/fvr2WLVumXr165Zt6smrVqnrsscf0zjvvSJIWL15c5rXNmjVL6enp8vDw0JgxY5yyjR9++EHZ2dlq2rSp/v73vyssLMxcFhgYqC5dumjJkiW6+eabS7SdBQsWKDAw0HzUrVu3pKUDkqQdO3aYQ+gqqqKGAQIAUN5UyNB/JQMGDJAk/fHHH2U688/atWu1bNkySdKTTz6pZs2aOWU7QUFBkqSEhARzWlNnmDFjhvlNSlxcnE6ePOm0bcGxsr6XxU8//aS5c+fqtddeK/V9AQAArlEhx/RLOYF32bJl+uKLL3To0CFzPurLRUVFqWbNmk6vZ9euXea897fddpueffZZp22rffv2CgkJ0ZkzZ9ShQwdNnjxZvXr1UrNmzUr1zKm3t7e8vb1Lrb8refHghTLbljM93SakVPsr63tZ/PTTT5o3b57q16+vKVOmXHV7AABQ/lTI0H/kyBH17NlTUVFR5mt+fn4KCgoyL9w9d+6cJDn1THiuvXv3asCAAUpJSVHnzp316aefysPDeW9tUFCQVq9ereHDhysiIkKPPvqopJyhPbfeeqvuu+8+DR061JxOFBVbRbuXBQAAKH8q5PCesWPHKioqSg0aNND69et18eJFJSUlKTo6WmfPntWpU6fMdZ19C/q9e/eqb9++SkhIUMeOHbVp06Yyuftsr169dOzYMa1atUqjR49WkyZNFBcXp88//1wjR45UmzZt8r0PAAAAqLwqXOg/efKk9uzZI0lavXq17r33XlWtWjXfOkWd9cw9A5+amupwnbi4uGLVsmfPHvXp00fx8fHq2LGjNm/eXOIZc65GlSpVNHLkSK1YsUJHjhxRVFSUXnrpJfn4+OT7BgCV18GDBzVq1CjVr19fPj4+Cg4OVqdOnfTaa68pLS2twPo2m80cpnb8+PEC1wjMnTvXXDc5OVmrV6/WqFGjdOONN6p69ery9vZWWFiYBg8erE2bNjl1386ePasnn3xS1113napUqaIqVarouuuu01NPPWV+03e5yy++/eOPPzRx4kQ1bNhQ3t7eBe7fcerUKU2aNEl169aVt7e36tSpo7Fjx+ro0aPFqjE9PV1Lly5Vjx49FBISIi8vL9WsWVODBg0q8v3JrXHHjh2Kjo7WtGnT1LRpU/n5+VXoi58BAK5T4Yb35L2YtE2bNoWus23bNoftg4ODJUnR0dFKS0srdMz6Dz/8cMU69uzZk+8M/1dffVWmgb8wtWvX1lNPPaX4+Hg9//zz2rp1q0vrgWstWrRI06dPN7/tCgwMVFJSkvbu3au9e/fqvffe01dffaVatWqZbUJDQ5WSkqL4+Hi5ubmpevXq+frM+y3WunXrzA8INptNdrtdHh4eOnPmjD799FN9+umnmj59ul555ZVS37edO3dq8ODBio2NlZTzAViSfvvtN/32228KDw/XZ599pi5dujjsY8+ePZo0aZISExPl5+dXYDjcgQMH1KtXL126dElSzvURcXFxWrFihT755BO9/fbbRdZ4/PhxDRgwQBEREZL+9x6dO3dOn332mT777DNNnjxZb775psM+jh49qvvvv1/nzp2Tj48PQ/YAANeswp3pDwwMNJ///PPPBZYnJCRo/vz5DtvfcMMNknKG/WzYsKHA8pSUFC1atKjIGvIG/k6dOmnz5s35pg51tsLO0OaVe/GmK25MhvLhiy++0LRp02QYhgYNGqQ///xTsbGxSkxM1KpVqxQQEKBffvlF9957b76byJ09e1avv/66JKlu3bo6e/ZsvscTTzxhrhscHKwnnnhC3333nRITExUbG6ukpCSdPn1a8+bNk6enp/7+97/rs88+K9V9O3nypBn4W7ZsaW4/MTFR3377rZo1a6ZLly5p0KBBRQ5xmzRpkq677jr9+OOPSkpKUmJiorZs2SIp5+/IXXfdpUuXLqlevXrasmWLeS+MPXv2qG7dupo0aZLDvpOSktS3b19FRESoe/fu2rFjh1JSUhQbG6vY2Fi9+uqr8vf317Jly8z3uzBTp05VUFCQtm/frqSkJMXHx+v333+/9jcPAFBpVbhU2KJFC9WrV0+SNG7cOO3fv99ctnfvXnXv3t08M1eYOnXqmGf/pk2bpm3btpmhZ//+/erVq5eio6Mdtv/+++/N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mcco99uDbt26Fbo8NjbW8Pf3NyQZTz/99LWUb3mFfdNbv379Ugn95ATXI/RXIl27djUkGT169Ch0eXZ2ttGwYUNDkjFq1Kir6rtevXqGJGPs2LGFLk9ISDD/2M6ePfuqa68snHmMrmTdunXmP7YHDhwo1b6toqyOz969ew03NzejadOmRkpKCqG/mJx5fJ5++mlz6El2dnZplFspOfMY5Q4Rmj59usN1brrpJkOS8cgjj1xV35VZaYV+coLrcSFvJZGcnKzdu3dLkvr161foOjabTX379pUkbdmypdh9//777zpx4kSRffv7+6tr165X3Xdl4sxjVBw+Pj7m86ysrFLt2wrK6vikpaVp3LhxMgxDb731Vr7jAsecfXxWrVolSRoxYoRsNlsJKq28nH2MGjVqJEnav39/ocvj4uJ05MgRSVLbtm2vqm+UDDmhfCD0VxKHDh1Sdna2JKlVq1YO18tddvbsWcXExBSr7//85z8F2hfV92+//VasfisbZx6j4tixY4ckycvLS02bNi21fq2irI7Ps88+q0OHDmn8+PHq1q3btRVbCTnz+Bw7dkynT5+WJN1888369ddfNXz4cNWqVUve3t6qU6eOhg4dagZaFM7Zv0MPPvigpJy/ZQ8//LBOnTolSTIMQwcOHNAdd9yhxMREdezYUSNGjLjW3cA1ICeUD4T+SiL3HyxJql27tsP18i7L26Y0+46Pj1diYmKx+q5MnHmMruTYsWNatmyZJGno0KGy2+2l0q+VlMXxOXjwoF5++WWFhoZq4cKFV19kJebM45N7dliSdu/erbZt22r16tWKi4uTj4+PTp06pXXr1qlr16567rnnrqH6ysHZv0MPP/ywnnrqKbm5uWnp0qWqU6eOAgIC5OPjo5tvvllHjx7V008/re3bt8vd3f3adgLXhJxQPhD6K4mEhATzuZ+fn8P18i7L28ZVfVcmrnofU1JSNGTIECUnJyskJEQvvvhiifu0Imcfn8zMTI0bN06ZmZlavHixgoKCrqnOysqZx+fSpUvm81mzZiksLExbt25VYmKi4uLiFBERoe7du8swDM2ePVuffPLJNeyB9Tn7d8jNzU0LFizQu+++K39/f0lSYmKiOe9/amqq4uLilJSUdLWlo4TICeUDoR+oxDIzMzV8+HDt379fnp6e+uCDDxQWFubqsiqlF198UT/99JPuuOMO3Xfffa4uB3nkDkmRcoaKfPzxx+rVq5fc3HL+CW3ZsqU+//xz1axZU5I0b948l9RZ2V24cEE9e/bUmDFj1LFjR3333XeKjY3VmTNn9Mknn6h69ep688031aFDB3PoD1CZEPoriYCAAPN5cnKyw/XyLsvbxlV9VyZl/T5mZWXp//7v/7Rx40Z5eHjoww8/1O23337N/VmdM4/Pb7/9pueee07+/v5aunTptRdZiZXV37iePXvqpptuKrCOv7+/Hn74YUnSL7/8onPnzhWr78rE2X/jRo8erR07dqhbt27avHmzOnfurMDAQNWsWVN33XWXvvvuO4WEhOjPP//U008/fW07gWtCTigfCP2VRN6zt0Wd4ci7rLhnfK+2b7vdbn71iv9x5jG6XFZWlkaMGKF169bJ3d1d//znP3XvvfdeU1+VhTOPz8MPP6z09HQ988wzCg4OVmJiYr5HZmampJyzzLmvZWRkXOOeWJMzj0/eMcgtWrRwuF7Lli3N58ePHy9W35WJM4/RoUOH9K9//UuSNH369EJnWKpRo4ZGjRolSfrkk09kGEax+kbJkRPKB0J/JdGiRQvzq+i8V9FfLndZzZo1VbVq1WL1nfdK/OL0nfcfRvyPM49RXrln+NesWWMG/qFDh15b0ZWIM4/PsWPHJEkzZsxQQEBAgccHH3wgSTpx4oT52pIlS0qyO5bjzOPTsmXLYl34mTdEMq1nQc48Rnlne2ncuLHD9Zo0aSIp54xydHR0sfpGyZETygdCfyXh5+enzp07S5K++uqrQtcxDEObN2+WpKsa5tG0aVPVq1evyL6TkpK0a9euq+67MnHmMcqVlZWl4cOHa+3atWbgv//++6+96EqkLI4Prp0zj4+Pj49uvfVWSTlnlB3JDZ42m00NGjQodv+VhTOPUe6HCanob1nyDrviTHLZISeUE665JxhcIff25zabzfj+++8LLF+7du013/489/bafn5+xrFjxwosf+mll7i9djE48xhlZmYaQ4cONSQZHh4expo1a0qr7ErDmcenKNyRt3iceXxWrVpl9r1///4CyxMSEoyaNWsakoxbbrnlmvfB6px1jCIjI812AwcOLHSdxMREo1GjRoYk4/rrr7/mfahsSuuOvOQE1yP0VyIZGRlG69atDUlG7dq1zT+oWVlZxrp16wy73W5IMvr161eg7Zw5c8w/qIX9ssbGxpr/4LVs2dLYt2+fYRiGkZaWZixdutTw8vIyJBkPPvigU/exonPWMcrMzDTuv/9+M/CvW7euLHbHcpz5O1QUQn/xOPP4ZGVlGe3btzckGQ0aNDC2bdtmZGVlGYZhGL/99pvRo0cPQ5Lh5uZmbN++3an7WZE58xgNHDjQXD5ixAjj6NGjRnZ2tpGenm7s3r3baNu2rbl85cqVzt7VCismJsY4f/68+ahbt64hyXjyySfzvZ6QkJCvHTmh/CP0VzLHjh0zGjRoYP5i+vn5GT4+PubPbdq0MWJiYgq0K05g2bdvn1GtWjVzvYCAAMPT09P8+fbbbzdSU1OdvIcVnzOO0c6dO81lnp6eRmhoaJEPvgVwzJm/Q44Q+ovPmcfnzJkzRsuWLfP1HRgYmO9366233nLyHlZ8zjpG58+fN26++WZzndy+PTw88r325JNPlsFeVly5Z/av9Bg9enS+duSE8o8x/ZVMgwYN9Msvv2j27Nlq1aqVbDabPD09dfPNN+uVV17R999/r+Dg4Gvq++abb1ZERISmTp2qJk2aKCMjQ1WqVFGXLl309ttva9OmTfL29i7lPbIeZxyjvPOMZ2Rk6Ny5c0U+UlJSSnu3LMOZv0MoOWcen5o1a+rAgQN65ZVX1K5dO3l6eiolJUUNGjTQuHHjdODAAU2YMKGU98h6nHWMQkJC9P333ys8PFx9+vRRaGioMjIy5OHhoUaNGmnEiBHatWuXXn75ZSfsFYqDnOBaNsNgzioAAADAyjjTDwAAAFgcoR8AAACwOEI/AAAAYHGEfgAAAMDiCP0AAACAxRH6AQAAAIsj9AMAAAAWR+gHAAAALI7QDwAAAFgcoR8AAACwOEI/AMApxowZI5vNpjFjxpT5tlesWCGbzaYGDRqUadvSMHfuXNlsNnXv3t0l2wdgTR6uLgAA4FyGYeijjz7Shx9+qAMHDig6Olru7u4KDQ1VrVq11L59e3Xt2lU9e/aU3W53dbkAACcg9AOAhcXGxmrw4MHauXOn+ZqHh4f8/Px04sQJ/fnnn9q9e7cWLVqk9957zyVn5cubwMBANWvWTLVr13Z1KQBQahjeAwAWNmrUKO3cuVPu7u6aPn26jhw5orS0NF28eFEpKSn6+eef9dJLL+mGG25wdanlxl133aXDhw9r+/btri4FAEoNZ/oBwKL++9//6vPPP5ckzZ8/X08//XS+5R4eHrr++ut1/fXX66mnnlJKSoorygQAlAHO9AOARf3000/m80GDBl1xfV9fX4fLPvnkE91xxx0KDQ2Vl5eXQkNDdccdd2jDhg3FqsUwDC1btkzt27eX3W6X3W5Xly5d9OGHH16x7Y4dOzRkyBDVrl1b3t7eCgkJUc+ePfXee+8pKyurWNu/GkVdyHv5Rbbbt2/XgAEDVL16dfn4+KhFixaaN2+eUlNTi9zGpk2b1Lt3bwUFBcnf31833HCDXn75ZWVkZBSrxsjISE2ZMkXXXXed/P395efnp+bNm+vxxx/XiRMnCqz/4IMPymazKSgoSJGRkYX2+eabb8pms8nDw0PffvttseoAUIEYAABLWrdunSHJkGRs2bLlmvpIS0szhg4davbj5uZmBAcHG25ubuZrw4YNM9LT0wu0HT16tCHJGD16tNlHbnubzWa2Hzt2rJGdnV3o9qdOnWquZ7PZjKCgIMPd3d187bbbbjPi4+MLtHvvvfcMSUb9+vWvep+LajtnzhxDktGtWzfj5ZdfNmw2m1lX3n3q0aOHkZmZWWj/uX3kPoKCggwPDw9DknHrrbcaM2bMMLdRmH/+85+Gt7e32d7b29vw9fU1fw4ICDA2b96cr01ycrJx3XXXGZKMjh07GhkZGfmW//rrr4aPj48hyZg9e/ZVv2cAyj9CPwBY1LFjx8wg2rp1a+P333+/6j6mT59uBu5Zs2YZly5dMgzDMGJiYoy//e1vZtD861//WqBtbugPDAw0bDab8dxzzxlxcXGGYRhGdHS08cgjj5jtX3/99QLt//GPf5jLJ06caJw5c8YwDMNITEw0Fi1aZAbloUOHFmjr7NAfFBRkuLm5GTNmzDDOnz9vGIZhxMXFGbNnzzZrfueddwq0//TTT83lQ4YMMU6cOGEYRk4oX7JkieHl5WUEBQU5DP1btmwx3NzcDA8PD+Opp54yjh07ZmRnZxvZ2dnG4cOHjSFDhhiSDLvdbhw/fjxf219//dX8cPC3v/3NfD3vB4LOnTs7/LACoGIj9AOAhU2YMCHfmfI2bdoYDz30kPHOO+8Yv/76q8Mz7IZhGFFRUWawnjFjRqHrTJs2zZBkeHp6GqdPn863LDf0SzJmzZpVaPsRI0YYkoyqVasaKSkp5uvJyclG1apVzW8SCrN48WKz/3379uVb5uzQL8mYM2dOoe3vvvtuQ5LRq1evAstatmxpBvqsrKwCy5ctW2b2f3noz8rKMpo0aWJIMpYvX+6w/jvvvNOQZDz++OMFlr355pvmNy5ff/21YRiGMWnSJPODzOUfFABYB6EfACwsIyPDmDVrllGlSpV8Q0pyHzVq1DCmTp1qnD17tkDb119/3ZBk+Pj4mGfoLxcTE2MONVm8eHG+Zbmh39fX12H7I0eOmLV89tln5ut5z4g7+oYiMzPTqFWrliHJmDZtWr5lzg793t7eRkJCQqHtV65cab63ef3888/mPm3durXQtllZWUbt2rULDf3ffPONIckICQkp9ANDro8++siQZDRv3rzQ5bkfSsLCwozly5ebNa1fv95hnwAqPi7kBQAL8/Dw0LPPPqtTp07p/fff1wMPPKAbbrhBXl5ekqTo6GgtWrRIrVq10r///e98bfft2ydJateuncObdgUHB6tt27b51r9c27ZtHbZv0qSJ6tSpU6B97vO6deuqadOmhbZ1d3fXbbfdVuS2nSX3AtrChIWFSZJiYmLyvZ5bo4eHh7p27VpoWzc3N4d34t29e7ckKS4uTmFhYapZs2ahjwkTJkiSjh8/Xmg/4eHhqlevnk6fPq1JkyZJkh544AHde++9RewxgIqO0A8AlUBgYKBGjBiht99+Wz/99JPi4uK0detWDRw4UJJ04cIF3XPPPflmnYmOjpakK96kKje0565/uSu1z12et31pbdtZAgICHC7z8MiZDTszMzPf67k1hoSEyNvb22H73H263OnTpyVJGRkZOnfunMPHpUuXJMnhFKzBwcFasmSJ+XOjRo30+uuvO6wHgDUQ+gGgEvLx8VGvXr302WefafTo0ZKkqKgoffXVVy6uDI7kTk/aoUMHGTnDc6/4cOTtt982n586dUpHjx51ev0AXIvQDwCV3MSJE83nv//+u/m8Ro0aknI+DBQld3nu+pc7depUke1zl+dtX1rbLk9ya7xw4YLS09Mdrufo/apZs6Ykx8N2iuuNN97QZ599Jnd3d7Vs2VJpaWm6//77lZycXKJ+AZRvhH4AqOTyjk3PO+wk71j9uLi4QtvGxsbmG/tfmH379ikxMbHQZUePHjWDe+728j6PiorSkSNHCm2blZWlb775pshtlye5+5SZmaldu3YVuk52drZ27NhR6LLOnTtLks6ePXvN1zD8+uuvevLJJyVJs2fP1r/+9S8FBQXp0KFDmjp16jX1CaBiIPQDgEUdO3bMYWDOa+XKlebzm266yXx+zz33yMPDQ6mpqXrppZcKbfvCCy8oLS1Nnp6euueeewpdJyUlRa+88kqhy+bPny9Jqlq1qnr37m2+3rt3b1WrVk1Szl1wC7N8+XJznPuwYcMc7F35cf3116tFixaSpOeff17Z2dkF1nn33XcdfrvRo0cP/eUvf5EkTZ06tchvC6SCFxKnpKTo/vvvV2pqqrp06aJnnnlG9evX11tvvSVJeuutt/Txxx9f9X4BqBgI/QBgUREREWrRooUGDBigVatWKTIy0lyWkZGhgwcPauzYsXr11VclSe3bt1eXLl3MdWrXrq3HH39ckvTiiy9qzpw5io2NlZRzhn/WrFlauHChJGnatGmqVatWoXUEBgbqueee04IFC5SQkCApZ4jL448/bn7gmDVrlnx8fMw2vr6+ZthfvXq1Jk+erHPnzkmSkpOTtXjxYk2ZMkWSNHToUN18880leKfKzvPPPy9J+uabbzR8+HAz4KempmrZsmV65JFHFBQUVGhbDw8PLVu2TB4eHvruu+906623avv27crIyDDX+fPPP7Vs2TK1a9dOS5cuzdd+6tSp+u233xQUFKQPPvhA7u7ukqQhQ4Zo/PjxkqQJEybo5MmTpb3bAMoDV80VCgBwrq+++qrAvPxeXl5G1apVzTv15j5uuukm49SpUwX6SEtLM+677z5zPTc3NyM4ONhwc3MzXxs2bJiRnp5eoG3uPP2jR482hg4dakgy3N3djeDg4HzbHzVqlMN556dOnZrv5mLBwcHmDcMkGT169DDi4+MLtHP2PP2F3S03V+58+o7+iX3mmWfyvfd596lr167GjBkzitzGhg0bjICAALO9p6enUa1aNfN+CbmP+fPnm20+/vjjIufjT0pKMpo3b27WwF15AevhTD8AWFSfPn303//+V6+//rqGDBmiFi1ayNvbW7GxsfLz81OTJk103333ac2aNfrxxx/N+eXz8vLy0tq1a/XRRx+pX79+qlatmhISElStWjX169dPn3zyiT788EN5enoWWcvq1au1dOlStWnTRpmZmapSpYo6duyoVatWaeXKlXJzK/yfo1dffVVff/217rnnHoWGhioxMVEBAQHq0aOH3n33XW3durXI6TPLo/nz5+uLL77QbbfdJrvdrrS0NLVo0UIvvviitm/fbt5DwZHBgwfr6NGjmjNnjtq3by9/f3/FxsbK29tbN9xwgx544AFt2LDBHLt/8uRJPfDAA5Kk8ePHFzofv5+fn1avXi1vb2/t2rXLHHYFwDpshlHEnF4AAAAAKjzO9AMAAAAWR+gHAAAALI7QDwAAAFgcoR8AAACwOEI/AAAAYHGEfgAAAMDiCP0AAACAxRH6AQAAAIsj9AMAAAAWR+gHAAAALI7QDwAAAFgcoR8AAACwOEI/AAAAYHGEfgAAAMDi/h+9CakY4gBRWwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plt.rcParams.update({\"font.size\": 18, \"font.weight\": \"normal\"})\n", + "plt.figure(figsize=(8, 5))\n", + "labels = salib_dict[\"names\"]\n", + "plt.barh(labels, Si[\"ST\"], xerr=Si[\"ST_conf\"], color=\"skyblue\", label=\"Total order\")\n", + "# uncomment the below line to see first order indices\n", + "# plt.barh(labels, Si[\"S1\"], xerr=Si[\"S1_conf\"], color=\"orange\", alpha=0.7, label=\"First order\")\n", + "plt.xlabel(\"Sobol index\", fontweight=\"normal\")\n", + "plt.legend()\n", + "plt.gca().invert_yaxis()\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "24", + "metadata": {}, + "source": [ + "## Morris sensitivity analysis\n", + "\n", + "Generate a set of samples." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Morris sample generated: (24, 5)\n", + "Eval 3/24, elapsed time=0.0 s, last cost=0.275993\n", + "Eval 6/24, elapsed time=0.1 s, last cost=3.82535\n", + "Eval 9/24, elapsed time=0.1 s, last cost=0.710258\n", + "Eval 12/24, elapsed time=0.1 s, last cost=0.817728\n", + "Eval 14/24, elapsed time=0.1 s, last cost=6.54119\n", + "Eval 16/24, elapsed time=0.1 s, last cost=6.45951\n", + "Eval 18/24, elapsed time=0.1 s, last cost=7.90766\n", + "Eval 20/24, elapsed time=0.2 s, last cost=7.66791\n", + "Eval 22/24, elapsed time=0.2 s, last cost=6.95628\n", + "Eval 24/24, elapsed time=0.2 s, last cost=0.447462\n", + "Model evaluations complete: (24,)\n", + "[0.27600551 0.27598928 0.27599281 0.23996188 0.02899002 3.82535263\n", + " 7.69181466 8.05302681 0.71025763 1.08463143 1.48492987 0.81772798\n", + " 0.31895297 6.54118678 6.66690114 6.45951486 6.45956066 7.90766474\n", + " 8.77245913 7.66791074 7.78793779 6.95628197 7.1729321 0.44746171]\n" + ] + } + ], + "source": [ + "# User settings\n", + "r = 10 # number of candidate trajectories (will generate r*(k+1) samples)\n", + "levels = 4 # number of grid levels for each parameter\n", + "optimal_k = 4 # number of optimal trajectories to select (< r)\n", + "fail_val = 1e5 # threshold to mark failed runs\n", + "seed = 123 # random seed for reproducibility\n", + "\n", + "# Generate Morris samples\n", + "param_values_morris = morris_sample.sample(\n", + " salib_dict,\n", + " N=r,\n", + " num_levels=levels,\n", + " optimal_trajectories=optimal_k,\n", + " local_optimization=False,\n", + " seed=seed,\n", + ")\n", + "print(\"Morris sample generated:\", param_values_morris.shape)\n", + "\n", + "# Evaluate model for all samples\n", + "Y_morris = evaluate_samples(param_values_morris)\n", + "print(\"Model evaluations complete:\", Y_morris.shape)\n", + "print(Y_morris)" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "Reshape the samples into trajectories and identify failures. Then analyse the Morris indices." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Total trajectories: 4, Failed trajectories: 0, Valid trajectories: 4\n", + "Valid samples shape: (24, 5) Valid Y shape: (24,)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + " mu mu_star sigma mu_star_conf\n", + "R0 [Ohm] 9.032563 9.032563 2.329086 1.927634\n", + "R1 [Ohm] 1.025996 1.184225 1.027222 0.698512\n", + "R2 [Ohm] 0.249659 0.276682 0.256995 0.179627\n", + "Tau1 [s] -0.564329 0.564341 0.781394 0.689225\n", + "Tau2 [s] -0.263840 0.263843 0.228246 0.181674\n", + "\n", + "Morris mu* (clean): [9.032563496194605 1.1842251230907288 0.27668176858963434\n", + " 0.564340822083737 0.26384256073702517]\n", + "Morris sigma (clean): [2.32908622 1.02722226 0.2569947 0.7813943 0.22824627]\n" + ] + } + ], + "source": [ + "k = salib_dict[\"num_vars\"] # number of parameters\n", + "r_total = int(param_values_morris.shape[0] / (k + 1))\n", + "\n", + "samples_reshaped = param_values_morris.reshape((r_total, k + 1, k))\n", + "Y_reshaped = Y_morris.reshape((r_total, k + 1))\n", + "\n", + "traj_has_fail = np.any(Y_reshaped > fail_val, axis=1)\n", + "n_fail_traj = np.sum(traj_has_fail)\n", + "n_valid_traj = r_total - n_fail_traj\n", + "\n", + "print(\n", + " f\"Total trajectories: {r_total}, Failed trajectories: {n_fail_traj}, Valid trajectories: {n_valid_traj}\"\n", + ")\n", + "\n", + "if n_valid_traj == 0:\n", + " raise RuntimeError(\n", + " \"All trajectories failed — cannot compute Morris indices. \"\n", + " \"Consider reducing penalty or tightening bounds.\"\n", + " )\n", + "\n", + "# Keep only valid trajectories\n", + "valid_idx = np.where(~traj_has_fail)[0]\n", + "samples_valid = samples_reshaped[valid_idx].reshape((n_valid_traj * (k + 1), k))\n", + "Y_valid = Y_reshaped[valid_idx].reshape((n_valid_traj * (k + 1),))\n", + "\n", + "print(\"Valid samples shape:\", samples_valid.shape, \"Valid Y shape:\", Y_valid.shape)\n", + "\n", + "# Recompute Morris indices on valid trajectories\n", + "morris_res = morris_analyze.analyze(\n", + " salib_dict, samples_valid, Y_valid, num_levels=levels, print_to_console=True\n", + ")\n", + "\n", + "print(\"\\nMorris mu* (clean):\", morris_res[\"mu_star\"])\n", + "print(\"Morris sigma (clean):\", morris_res[\"sigma\"])" + ] + }, + { + "cell_type": "markdown", + "id": "28", + "metadata": {}, + "source": [ + "Plot the results of Morris's method." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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Q0GArakp/L28yjcwqnBaJSv3799Hq1atMHPmTFy5cgVKpVJa9UupVOLy5cuYPn06WrdujQcPHugqZiIiIiKqQho7OODChAl4u04dALnJibGBgZS8vF2nDi5MmMAd7PWQ7An2qamp6NWrFyIiImBkZITBgwejZ8+eqPN/F1lkZCSOHDmCXbt24eHDh+jduzdu3rwJKysrnQVPRERERFVDYwcHnB43DjdjY7Hn3j0kvngBe3NzDHZ15a1fekx2srJq1SpERETA2dkZ+/fvR6tWrQqVGT9+PK5fv47+/fvjyZMnWL16NebNm6dNvERERERUhbVwcuKmjySRfRvY3r17IQgC1q5dW2Sikqdly5ZYt24dRFHE7t275XZHRERERER6RnayEhISAlNTU/Tv37/Esn379oWZmRlCQkLkdkdERERERHpGdrKiVCphYmKiUVlBEGBiYgKlUim3OyIiIiIi0jOyk5U6deogNTUVd+7cKbHsrVu3kJKSIk2+JyIiIiIiKonsZKV79+4QRRGTJ09GRkaG2nIZGRn4+OOPIQgCevToIbc7IiIiIiLSM7KTlZkzZ8LU1BSnT59Gy5YtsWHDBoSFhUGpVEKpVCI0NBR+fn5o2bIlTp8+DRMTE8yYMUOXsRMRERERURUme+nihg0bIiAgAKNHj8bDhw8xceLEIsuJoghjY2MEBASgYcOGsgMlIiIiIiL9otUO9kOHDsW5c+fQu3dvAJB2r897CIKAvn374vz58xg6dKhOAiYiIiIiIv0ge2QlT5s2bXDw4EEkJyfj6tWriIuLAwA4OjqiTZs2sLW11TpIIiIiIiLSP1onK3lsbW3h6empq+aIiIiIiEjPaXUbGBERERERUVlhskJERERERJWSRreB5a3i1bhxYwQGBhY4VhqCIODRo0elrkdERERERPpHo2QlLCwMAGBmZlboWGkIglDqOkREREREpJ80SlY2btwIAAVW9so7RkREREREVBY0Sla8vb01OkZERERERKQrnGBPRERERESVkuxkZdy4cZg2bZrG5b/88kuMHz9ebndERERERKRnZCcr/v7+2LZtm8bld+7cCX9/f7ndERERERGRnim328BEUSyvroiIiIiIqAoot2QlPj4eFhYW5dUdERERERG94jRaDUwbycnJ8PPzg0KhwJtvvlnW3RERERERURWhcbKyaNEifP31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YGlpWaicsbExxo8fD1EUsWfPHq2CJSIiIiIi/SE7Wfnll19w//59tGnTBnfv3sWaNWtgZWVVZNlBgwYBAM6cOSO3OyIiIiIi0jOyk5Vdu3ZBEAT8+OOPRY6o5Ne8eXMYGhriwYMHcrsjIiIiIiI9IztZuX//PgwNDdGpU6cSyxoaGsLOzg5JSUlyuyMiIiIiIj0jO1nJzMyEubk5DA0NNSqvUChgZmYmtzsiIiIiItIzspMVJycnpKWlaTRacvv2bbx48QJ169aV2x0REREREekZ2clK586dAQDbt28vseyyZcsgCAI8PT3ldkdERERERHpGdrLy8ccfQxRF+Pr64tatW0WWycrKwpw5c7B582YIgoDJkyfLDpSIiIiIiPSL7E0hO3bsiClTpuCnn37C22+/jT59+iAtLQ0AMHfuXISHh+PIkSOIj48HAHz11Vd4/fXXdRM1ERERERFVeVrtYL9q1SrY2Njg+++/x+7duwEAgiBg6dKlAHJ3tDcyMsL8+fMxf/587aMlIiIiIiK9oVWyIggCvvnmG3z44Yfw9/fHmTNnEB0djZycHNSsWROdOnXCuHHj0LBhQ13FS0REREREekKrZCWPi4sLFi5cqIumiIiIiIiIAGgxwf7JkyeIiorSuHx0dDSePHkitzsiIiIiItIzskdW6tevj1q1ammcsHTq1AkRERHIzs6W2yUREREREekR2SMrQO4E+rIsT0RERERE+kurZKU0MjIyYGSkkykyRERERESkB8olWYmOjsazZ89QrVq18uiOiIiIiIiqAI2HOk6ePInjx48XOJaWloavv/5abR1RFJGUlIT//vsPoijCzc1NdqBERERERKRfNE5Wjh07hkWLFkEQBOlYeno6Fi1aVGJdURRhZmaGOXPmyIuSiIiIiIj0jsbJSv369eHh4SH9fOLECRgbG6NDhw5q6xgYGMDGxgbNmzeHt7c3GjdurF20RERERESkNzROVry9veHt7S39bGBgAAcHBxw7dqxMAiMiIiIiIv0me3mujRs3wtzcXJexEBERERERSWQnK/lHWYiIiIiIiHSt3PZZISIiIiIiKg2td2k8fvw4tm7dihs3biAhIQFKpVJtWUEQ8OjRI227JCIiIiIiPSA7WRFFEePGjcOmTZukn0uSf9ljIiIiIiKi4shOVn766ScEBAQAAN566y0MHDgQzs7O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zEHdunUxbNgwHDlyRJexEhERERGRHtFqNTADAwMMGDAA//77L8LDw7Fo0SLUq1cPSqUSO3fu5GgLERERERHJprOli52dnTF//nw8fvwYBw8exLvvvgsjIyOOthARERERkSw632dFEAT07t0bu3btQmhoKLp06QJRFAvMbWnatCnWrVuHnJwcXXdPRERERERVRJlsCvnkyRMsXLgQbm5uOHXqFIDcJKZVq1YwNDTEw4cPMXnyZLz99tt49uxZWYRARERERESvOJ0lKzk5Odi7dy/69euHRo0aYfHixYiKioKDgwOmT5+OBw8e4MqVK4iIiMCCBQtgaWmJq1evYs6cOboKgYiIiIiIqhCtd7APCwvD+vXr4e/vj6dPn0IURQBAx44dMXnyZLz//vswMTGRyjs5OcHX1xcDBgxA+/btcfDgQW1DICIiIiKiKkh2svL3339j3bp1OHr0KERRhCiKsLa2xqhRozB58mQ0b9682Ppt27ZFzZo18fTpU7khEBERERFRFSY7WRk6dKj0vFWrVpg8eTJGjhwJS0tLjdvIP+JCRERERESUn+xkxczMDMOGDcPkyZPRvn17WW2EhYXJ7Z6IiIiIiKo42clKdHQ07OzsdBgKERERERHR/yd7NbB9+/Zh586dGpffvXs3Nm3aJLc7IiIiIiLSM7KTFR8fH0ydOlXj8tOnT8e4cePkdkdERERERHpGq31W8pYpLqvyRERERESkv8pkB/uipKSkcPUvIiIiIiLSWLkkK+fOnUNiYiJq165dHt0REREREVEVoPFqYAEBAQgICChwLCEhAd26dVNbRxRFJCUl4fbt2xAEAd27d5cfKRERERER6RWNk5WwsDAcP368wLGsrKxCx9Rp2rQpfH19SxEaERERERHpM42Tla5duxb4edGiRbCyssL06dPV1jEwMICNjQ2aN2+Orl27wtDQUHagRERERESkXzROVjw8PODh4SH9nJesLFy4sEwCIyIiIiIi/SZ7B/vQ0FCOlBARERERUZmRnay4uLjoMg4iIiIiIqICym2fFSIiIiIiotLQaGSlYcOGAIDGjRsjMDCwwLHSEAQBjx49KnU9IiIiIiLSPxolK2FhYQAAMzOzQsdKQxCEUtchIiIiIiL9pFGysnHjRgCAra1toWNERERERERlQaNkxdvbW6NjREREREREuiJ7NbAnT54AABwd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n0ehUODHH39Eu3btYGFhgZo1a6JmzZqwsLBA+/btsWrVKigUCl3ESkREREREekSrkZX79+/jnXfewaNHjwqt9KVUKnH58mVcuXIFv/32G/799180adJEq2CJiIiIiEh/yE5WUlNT0atXL0RERMDIyAiDBw9Gz549UadOHQBAZGQkjhw5gl27duHhw4fo3bs3bt68CSsrK50FT0REREREVZfsZGXVqlWIiIiAs7Mz9u/fj1atWhUqM378eFy/fh39+/fHkydPsHr1asybN0+beImIiIiISE/InrOyd+9eCIKAtWvXFpmo5GnZsiXWrVsHURSxe/duud0REREREZGekZ2shISEwNTUFP379y+xbN++fWFmZoaQkBC53RERERERkZ6RnawolUqYmJhoVFYQBJiYmECpVMrtjoiIiIiI9IzsZKVOnTpITU3FnTt3Six769YtpKSkSJPviYiIiIiISiI7WenevTtEUcTkyZORkZGhtlxGRgY+/vhjCIKAHj16yO2OiIiIiIj0jOxkZebMmTA1NcXp06fRsmVLbNiwAWFhYVAqlVAqlQgNDYWfnx9atmyJ06dPw8TEBDNmzNBl7EREREREVIXJXrq4YcOGCAgIwOjRo/Hw4UNMnDixyHKiKMLY2BgBAQFo2LCh7ECJiIiIiEi/yB5ZAYChQ4fi3Llz6N27N4DcxCT/QxAE9O3bF+fPn8fQoUN1EjAREREREekH2SMredq0aYODBw8iOTkZV69eRVxcHADA0dERbdq0ga2trdZBEhERERGR/tE6Wclja2sLT09PXTVHRERERER6TqvbwIiIiIiIiMqKzkZWMjIykJiYWOLGj/Xq1dNVl0REREREVIVplawoFAosW7YMW7duRUhISInlBUFAdna2Nl0SEREREZGekJ2sJCUloUuXLrh9+zZEUdSojqbliIiIiIiIZCcr33zzDW7dugVjY2NMmTIFgwYNgrOzM4yMdHZnGRERERER6THZmcXevXshCAJWrVqFyZMn6zImIiIiIiIi+auBRUVFwcDAAGPHjtVlPERERERERAC0GFlxcHBARkYGzMzMdBkPERERERERAC1GVjp37ozk5GRERUXpMh4iIiIiIiIAWiQrs2bNgpGREb755htdxkNERERERARAi2Tlrbfegr+/PwICAjB+/Hg8fvxYl3EREREREZGekz1npWHDhgAAQ0ND+Pv7w9/fHw4ODrC2tlZbRxAEPHr0SG6XRERERESkR2QnK2FhYYWOPX/+HM+fP1dbRxAEud0REREREZGekZ2sbNy4UZdxEBERERERFSA7WfH29tZlHERERERERAXInmBPRERERERUlpisEBERERFRpaRRslJWGz/GxMSUuk5qaip8fX3RokULWFlZwdbWFu3atcMPP/yArKwsWXH4+vpCEIQSHyEhIbLaJyIiIiKi0tMoWWncuDE+++wzREdH66TTv//+G2+++SbWr19fqnrh4eF48803sWjRIty6dQuiKCIzMxOXL1/GjBkz8PbbbyMxMVF2XMbGxnByclL7MDKSPcWHiIiIiIhKSaNkxdnZGT///DMaN26M4cOH499//0VOTk6pOnr06BG+/vprNGnSBMOGDcOdO3dQv359jetnZ2fjnXfeQVhYGGrVqoWgoCCkp6dDoVBg27ZtsLa2xrVr1zBq1KhSxZVfx44d8fTpU7WP0sRLRERERETa0Wio4N69e1izZg2WLFmCHTt2YOfOnbCzs4Obmxvat2+Pli1bokaNGnBwcICpqSkSExORkJCAx48f4+LFi7hw4QLu3bsHABBFEb169cKKFSvQvHlzjQMNCAjAzZs3AQC7du1Chw4dAAAGBgYYNmwYVCoVRo4cif/++w/BwcHo3r17ad8LIiIiIiKqRDRKVoyNjTF9+nSMGzcOa9euxfr16xEaGopDhw7h8OHDJdYXRRHGxsZ499138cknn8Dd3b3UgQYEBAAAPD09pUQlv+HDh2PevHkIDQ3Fpk2bmKwQEREREb3iSrUamL29PWbPno1Hjx4hMDAQ06ZNw1tvvQUDAwOIoljo4eTkhPfffx+//vorIiMjsW3bNlmJikKhwJkzZwAAffv2LbKMIAjo06cPACAwMLDUfRARERERUeUie8Z4jx490KNHDwCAUqlEXFwcnj17hoyMDFSrVg01atSAnZ2dToK8e/cuVCoVABR761jeuadPnyIhIQEODg6l6uf27dto3rw5Hj9+DAMDA9SuXRtdunTBxx9/jNatW8t/AUREREREVGo6Wd7K2NgYtWvXRu3atXXRXCH5VyErro/856Kjo0udrMTHxyMhIQF2dnZISUnBgwcP8ODBA2zYsAFz587F4sWLS2wjMzMTmZmZ0s8pKSkAchM6pVJZbN288yWVo6qL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EVnDnhUisruZM2fi+PHjeOaZZxAQEICOjg6EhYVh1apVKC4uNrvooj0NHDgQzz33HABg7969FvPFxsaipKQEK1asQGRkJDo7OyGEQGRkJDIyMlBSUnLX3o2goCAkJCQo/546darVdSuArjUzTpw4gX379uHZZ5/FI488gpaWFrS1tSEiIgKzZ8/GW2+9Ba1Wew9HbSotLQ1//etfERcXBx8fH7S3tyM0NBRLlizBV199hXHjxvW67ofV2rVrkZubi4kTJ0KtVqOjowPR0dHYsWMHDh48aHUGNqBrMc+vv/4aWVlZmDBhAnx8fKDX66FSqfDYY48hNTUVH3/8MTIzM20e++OPP46SkhK8+eabmDhxInx9fWEwGBAcHIy4uDhs2bIFL7zwgs1jlr9fP/nJT2x+TETUP0hCcE5KIup/tFotYmNjMWzYMFy+fJmvPRHZWFlZGYYOHQpfX19cvXrV6joxRESWsGeFiPqlmJgYzJgxA1euXEFeXp6jwyF66Pz2t7+FEAKrV69mQ4WIeo09K0TUb509exbR0dGIjIzEmTNn4OLC5zdEtlBZWYnhw4dj0KBBuHjxIjw9PR0dEhE9oNzunoWI6OE0btw47Nq1C2VlZfjmm28srjlCRPemvLwcq1evRnx8PBsqRHRf2LNCREREREROie88EBERERGRU2JjhYiIiIiInBIbK0RERERE5JTYWCEiIiIiIqfExgoRERERETklNlaIiIiIiMgpsbFCREREREROiY0VIiIiIiJySmysEBERERGRU/ofooVxsW+m2YwAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "labels = salib_dict[\"names\"]\n", + "plt.figure(figsize=(8, 5))\n", + "plt.scatter(morris_res[\"mu_star\"], morris_res[\"sigma\"], s=60, color=\"teal\")\n", + "for i, txt in enumerate(labels):\n", + " plt.text(morris_res[\"mu_star\"][i] * 1, morris_res[\"sigma\"][i] * 1.1, txt)\n", + "plt.xlabel(\"mu* (overall influence)\")\n", + "plt.ylabel(\"sigma (nonlinearity / interactions)\")\n", + "plt.title(\"Morris screening\")\n", + "plt.grid(True)\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "30", + "metadata": {}, + "source": [ + "## Concluding thoughts\n", + "\n", + "This notebook illustrates how to extract EC parameters from a HPPC pulse using PyBOP. A sensitivity analysis of the model is performed using the Sobol and Morris methods from the SALib module. Other methods from SALib can also be used. More information can be found [here](https://salib.readthedocs.io/en/latest/)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "pybop-env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebooks/comparison_examples/comparing_cost_functions.ipynb b/examples/notebooks/comparison_examples/comparing_cost_functions.ipynb index 0cdb44d00..71dc80155 100644 --- a/examples/notebooks/comparison_examples/comparing_cost_functions.ipynb +++ b/examples/notebooks/comparison_examples/comparing_cost_functions.ipynb @@ -64,30 +64,9 @@ "source": [ "t_eval = np.arange(0, 300, 20)\n", "solution = pybamm.Simulation(model, parameter_values=parameter_values).solve(\n", - " t_eval=t_eval\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "We can then construct the PyBOP dataset class with the synthetic data as," - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "dataset = pybop.Dataset(\n", - " {\n", - " \"Time [s]\": t_eval,\n", - " \"Current function [A]\": solution[\"Current [A]\"](t_eval),\n", - " \"Voltage [V]\": solution[\"Voltage [V]\"](t_eval),\n", - " }\n", - ")" + " t_eval=t_eval, t_interp=t_eval\n", + ")\n", + "dataset = pybop.import_pybamm_solution(solution)" ] }, { @@ -106,12 +85,10 @@ "parameter_values.update(\n", " {\n", " \"Positive electrode thickness [m]\": pybop.Parameter(\n", - " prior=pybop.Gaussian(7.56e-05, 0.5e-05),\n", - " bounds=[65e-06, 10e-05],\n", + " pybop.Gaussian(7.56e-05, 0.5e-05, truncated_at=[65e-06, 10e-05]),\n", " ),\n", " \"Positive particle radius [m]\": pybop.Parameter(\n", - " prior=pybop.Gaussian(5.22e-06, 0.5e-06),\n", - " bounds=[2e-06, 9e-06],\n", + " pybop.Gaussian(5.22e-06, 0.5e-06, truncated_at=[2e-06, 9e-06]),\n", " ),\n", " }\n", ")\n", @@ -175,7 +152,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "3.302610173647733e-09 1.48382617437662e-05\n" + "3.3026101740368053e-09 1.4838261744640228e-05\n" ] } ], @@ -201,21 +178,15 @@ "name": "stdout", "output_type": "stream", "text": [ - "Samples: [[6.58651485e-05 4.07175421e-06]\n", - " [6.86682523e-05 6.42491715e-06]]\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "SSE costs: [np.float64(0.00031141539073177706), np.float64(0.012393474567017178)]\n", - "RMSE costs: [np.float64(0.004556426894923821), np.float64(0.028744245298861913)]\n" + "Samples: [[8.13658824e-05 5.78129588e-06]\n", + " [8.49368515e-05 5.25871051e-06]]\n", + "SSE costs: [1.1526402047573603e-05, 0.0008508296049987403]\n", + "RMSE costs: [0.0008765995683158723, 0.007531399183855726]\n" ] } ], "source": [ - "samples = simulator.parameters.sample_from_priors(2)\n", + "samples = simulator.parameters.sample_from_distribution(2)\n", "print(\"Samples:\", samples)\n", "inputs = [simulator.parameters.to_dict(s) for s in samples]\n", "solution = simulator.solve(inputs)\n", @@ -250,7 +221,7 @@ " window.PlotlyConfig = {MathJaxConfig: 'local'};\n", " if (window.MathJax && window.MathJax.Hub && window.MathJax.Hub.Config) {window.MathJax.Hub.Config({SVG: {font: \"STIX-Web\"}});}\n", " \n", - " \n", + " \n", " " ] }, @@ -261,9 +232,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -299,9 +270,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -337,9 +334,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -249,9 +250,43 @@ "data": { "text/html": [ "
\n", - "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "
\n", - "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -264,9 +254,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - "
\n", - "
\n", - " \n", + " \n", " " ] }, @@ -213,9 +207,9 @@ "data": { "text/html": [ "
\n", - "
\n", - "
\n", - "