From 5591b09adca8a924fb44d7b622c8534e69df557b Mon Sep 17 00:00:00 2001 From: Bryn Pickering <17178478+brynpickering@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:19:31 +0100 Subject: [PATCH] Restructure project (#488) * Use pyproject.toml as is the recommended way to define python project metadata now. * Move calliope into src to make sure tests are not getting confused about relative filepaths in a way we don't realise. * Move from azure pipelines to github actions, which allow us to leverage the growing number of pre-built actions (like micromamba for speedy env install). * Move tests out of the calliope package itself. * Move and restructure requirements files so that dev install becomes a much easier one-liner for the mamba/conda part. * Remove unused dirs/files --- .azure-pipelines.yml | 172 ----- .coveragerc | 2 - .flake8 | 11 - codecov.yml => .github/.codecov.yml | 0 .github/workflows/commit-ci.yml | 44 ++ .github/workflows/pr-ci.yml | 73 +++ .hound.yml | 3 - .pylintrc | 589 ------------------ .readthedocs.yml | 2 +- CONTRIBUTING.md | 2 +- MANIFEST.in | 1 - README.md | 8 +- calliope/test/common/__init__.py | 0 .../model_config/data/csp_r.csv | 49 -- .../model_config/data/demand-1.csv | 49 -- .../model_config/data/demand-2.csv | 49 -- .../model_config/data/set_t.csv | 48 -- .../model_config/locations.yaml | 64 -- .../national_scale/model_config/model.yaml | 45 -- .../national_scale/model_config/techs.yaml | 98 --- .../model_conversion/national_scale/run.yaml | 73 --- .../notebooks/calliope_model_object.ipynb | 562 +---------------- doc/_static/notebooks/milp.ipynb | 6 +- doc/_static/notebooks/national_scale.ipynb | 17 +- doc/api/api.rst | 8 + doc/conf.py | 4 +- doc/helpers/generate_math.py | 5 +- doc/helpers/generate_readable_schema.py | 2 +- doc/helpers/generate_tables.py | 2 +- doc/user/advanced_constraints.rst | 2 +- doc/user/develop.rst | 29 +- doc/user/installation.rst | 28 +- doc/user/ref_example_models.rst | 16 +- doc/user/tutorials_01_national.rst | 16 +- doc/user/tutorials_02_urban.rst | 22 +- doc/user/tutorials_03_milp.rst | 6 +- paper/codemeta.json | 28 - paper/paper.bib | 78 --- paper/paper.md | 41 -- paper/timeseries.pdf | Bin 31307 -> 0 bytes pyproject.toml | 89 +++ pytest | 0 pytest.ini | 12 - requirements.yml | 16 - requirements.txt => requirements/base.txt | 2 +- requirements/dev.txt | 11 + .../docs.yml | 0 setup.py | 57 -- {calliope => src/calliope}/__init__.py | 0 {calliope => src/calliope}/_version.py | 0 .../calliope}/backend/__init__.py | 0 .../calliope}/backend/backends.py | 0 .../calliope}/backend/expression_parser.py | 0 .../calliope}/backend/helper_functions.py | 0 .../calliope}/backend/latex_backend.py | 0 {calliope => src/calliope}/backend/parsing.py | 0 .../calliope}/backend/where_parser.py | 0 {calliope => src/calliope}/cli.py | 0 .../calliope}/config/defaults.yaml | 0 .../calliope}/config/math_schema.yaml | 0 .../calliope}/config/model_data_lookup.yaml | 0 {calliope => src/calliope}/core/__init__.py | 0 {calliope => src/calliope}/core/attrdict.py | 0 {calliope => src/calliope}/core/io.py | 0 {calliope => src/calliope}/core/model.py | 0 .../calliope}/core/util/__init__.py | 0 .../calliope}/core/util/generate_runs.py | 0 .../calliope}/core/util/logging.py | 0 {calliope => src/calliope}/core/util/tools.py | 0 .../example_models/national_scale/model.yaml | 0 .../model_config/locations.yaml | 0 .../national_scale/model_config/techs.yaml | 0 .../national_scale/scenarios.yaml | 0 .../national_scale/timeseries_data/README.rst | 0 .../timeseries_data/csp_resource.csv | 0 .../timeseries_data/demand-1.csv | 0 .../timeseries_data/demand-2.csv | 0 .../example_models/urban_scale/model.yaml | 0 .../urban_scale/model_config/locations.yaml | 0 .../urban_scale/model_config/techs.yaml | 0 .../example_models/urban_scale/scenarios.yaml | 0 .../timeseries_data/demand_heat.csv | 0 .../timeseries_data/demand_power.csv | 0 .../timeseries_data/export_power.csv | 0 .../timeseries_data/pv_resource.csv | 0 {calliope => src/calliope}/examples.py | 0 {calliope => src/calliope}/exceptions.py | 0 {calliope => src/calliope}/math/base.yaml | 0 {calliope => src/calliope}/math/operate.yaml | 0 {calliope => src/calliope}/math/spores.yaml | 0 .../calliope}/math/storage_inter_cluster.yaml | 0 .../calliope}/postprocess/__init__.py | 0 .../calliope}/postprocess/results.py | 0 .../calliope}/postprocess/util.py | 0 .../calliope}/preprocess/__init__.py | 0 .../calliope}/preprocess/checks.py | 0 .../calliope}/preprocess/model_data.py | 0 .../calliope}/preprocess/model_run.py | 0 .../calliope}/preprocess/nodes.py | 0 {calliope => src/calliope}/preprocess/time.py | 0 {calliope => src/calliope}/preprocess/util.py | 0 {calliope => src/calliope}/time/__init__.py | 0 {calliope => src/calliope}/time/clustering.py | 0 {calliope => src/calliope}/time/funcs.py | 0 {calliope => src/calliope}/time/masks.py | 0 {calliope/test => tests}/__init__.py | 0 .../common/constraint_sets.yaml | 0 .../test => tests}/common/html_strings.yaml | 0 .../common/lp_files/balance_conversion.lp | 0 .../common/lp_files/carrier_production_max.lp | 0 .../common/lp_files/energy_cap.lp | 0 .../common/lp_files/resource_max.lp | 0 .../common/lp_files/storage_max.lp | 0 .../energy_cap_per_storage_cap.yaml | 0 .../common/test_model/model.yaml | 0 .../common/test_model/model_minimal.yaml | 0 .../common/test_model/scenarios.yaml | 0 .../timeseries_data/alternating_cost.csv | 0 .../timeseries_data/binary_one_day.csv | 0 .../timeseries_data/carrier_ratio.csv | 0 .../timeseries_data/cluster_days.csv | 0 .../test_model/timeseries_data/clusters.csv | 0 .../test_model/timeseries_data/cost.csv | 0 .../timeseries_data/demand_elec.csv | 0 .../timeseries_data/demand_elec_15T_to_2h.csv | 0 .../timeseries_data/demand_elec_15mins.csv | 0 .../timeseries_data/demand_elec_positive.csv | 0 .../timeseries_data/demand_heat.csv | 0 .../demand_heat_diff_dateformat.csv | 0 .../demand_heat_wrong_dateformat.csv | 0 .../demand_heat_wrong_length.csv | 0 .../timeseries_data/demand_simple.csv | 0 .../timeseries_data/supply_plus_resource.csv | 0 .../supply_plus_resource_inf.csv | 0 .../timeseries_data/supply_simple.csv | 0 .../common/test_model/weighted_obj_func.yaml | 0 {calliope/test => tests}/common/util.py | 3 +- .../test => tests}/common/yaml_file.yaml | 0 {calliope/test => tests}/conftest.py | 4 +- .../test_backend_expression_parser.py | 4 +- .../test_backend_helper_functions.py | 4 +- .../test_backend_latex_backend.py | 4 +- .../test => tests}/test_backend_parsing.py | 6 +- .../test => tests}/test_backend_pyomo.py | 6 +- ...ckend_pyomo_constraints_conversion_plus.py | 4 +- .../test_backend_pyomo_objective.py | 4 +- .../test_backend_where_parser.py | 4 +- {calliope/test => tests}/test_cli.py | 21 +- .../test => tests}/test_constraint_results.py | 4 +- .../test => tests}/test_core_attrdict.py | 4 +- .../test_core_future_warnings.py | 4 +- {calliope/test => tests}/test_core_model.py | 6 +- .../test => tests}/test_core_preprocess.py | 12 +- {calliope/test => tests}/test_core_time.py | 6 +- {calliope/test => tests}/test_core_util.py | 16 +- .../test => tests}/test_example_models.py | 6 +- {calliope/test => tests}/test_io.py | 3 +- {calliope/test => tests}/test_math.py | 10 +- {calliope/test => tests}/test_model_data.py | 19 +- .../test => tests}/test_model_manipulation.py | 6 +- 160 files changed, 384 insertions(+), 2217 deletions(-) delete mode 100644 .azure-pipelines.yml delete mode 100644 .coveragerc delete mode 100644 .flake8 rename codecov.yml => .github/.codecov.yml (100%) create mode 100644 .github/workflows/commit-ci.yml create mode 100644 .github/workflows/pr-ci.yml delete mode 100644 .hound.yml delete mode 100644 .pylintrc delete mode 100644 MANIFEST.in delete mode 100644 calliope/test/common/__init__.py delete mode 100644 calliope/test/model_conversion/national_scale/model_config/data/csp_r.csv delete mode 100644 calliope/test/model_conversion/national_scale/model_config/data/demand-1.csv delete mode 100644 calliope/test/model_conversion/national_scale/model_config/data/demand-2.csv delete mode 100644 calliope/test/model_conversion/national_scale/model_config/data/set_t.csv delete mode 100644 calliope/test/model_conversion/national_scale/model_config/locations.yaml delete mode 100644 calliope/test/model_conversion/national_scale/model_config/model.yaml delete mode 100644 calliope/test/model_conversion/national_scale/model_config/techs.yaml delete mode 100644 calliope/test/model_conversion/national_scale/run.yaml delete mode 100644 paper/codemeta.json delete mode 100644 paper/paper.bib delete mode 100644 paper/paper.md delete mode 100644 paper/timeseries.pdf delete mode 100644 pytest delete mode 100644 pytest.ini delete mode 100644 requirements.yml rename requirements.txt => requirements/base.txt (93%) create mode 100644 requirements/dev.txt rename requirements_docs.yml => requirements/docs.yml (100%) delete mode 100644 setup.py rename {calliope => src/calliope}/__init__.py (100%) rename {calliope => src/calliope}/_version.py (100%) rename {calliope => src/calliope}/backend/__init__.py (100%) rename {calliope => src/calliope}/backend/backends.py (100%) rename {calliope => src/calliope}/backend/expression_parser.py (100%) rename {calliope => src/calliope}/backend/helper_functions.py (100%) rename {calliope => src/calliope}/backend/latex_backend.py (100%) rename {calliope => src/calliope}/backend/parsing.py (100%) rename {calliope => src/calliope}/backend/where_parser.py (100%) rename {calliope => src/calliope}/cli.py (100%) rename {calliope => src/calliope}/config/defaults.yaml (100%) rename {calliope => src/calliope}/config/math_schema.yaml (100%) rename {calliope => src/calliope}/config/model_data_lookup.yaml (100%) rename {calliope => src/calliope}/core/__init__.py (100%) rename {calliope => src/calliope}/core/attrdict.py (100%) rename {calliope => src/calliope}/core/io.py (100%) rename {calliope => src/calliope}/core/model.py (100%) rename {calliope => src/calliope}/core/util/__init__.py (100%) rename {calliope => src/calliope}/core/util/generate_runs.py (100%) rename {calliope => src/calliope}/core/util/logging.py (100%) rename {calliope => src/calliope}/core/util/tools.py (100%) rename {calliope => src/calliope}/example_models/national_scale/model.yaml (100%) rename {calliope => src/calliope}/example_models/national_scale/model_config/locations.yaml (100%) rename {calliope => src/calliope}/example_models/national_scale/model_config/techs.yaml (100%) rename {calliope => src/calliope}/example_models/national_scale/scenarios.yaml (100%) rename {calliope => src/calliope}/example_models/national_scale/timeseries_data/README.rst (100%) rename {calliope => src/calliope}/example_models/national_scale/timeseries_data/csp_resource.csv (100%) rename {calliope => src/calliope}/example_models/national_scale/timeseries_data/demand-1.csv (100%) rename {calliope => src/calliope}/example_models/national_scale/timeseries_data/demand-2.csv (100%) rename {calliope => src/calliope}/example_models/urban_scale/model.yaml (100%) rename {calliope => src/calliope}/example_models/urban_scale/model_config/locations.yaml (100%) rename {calliope => src/calliope}/example_models/urban_scale/model_config/techs.yaml (100%) rename {calliope => src/calliope}/example_models/urban_scale/scenarios.yaml (100%) rename {calliope => src/calliope}/example_models/urban_scale/timeseries_data/demand_heat.csv (100%) rename {calliope => src/calliope}/example_models/urban_scale/timeseries_data/demand_power.csv (100%) rename {calliope => src/calliope}/example_models/urban_scale/timeseries_data/export_power.csv (100%) rename {calliope => src/calliope}/example_models/urban_scale/timeseries_data/pv_resource.csv (100%) rename {calliope => src/calliope}/examples.py (100%) rename {calliope => src/calliope}/exceptions.py (100%) rename {calliope => src/calliope}/math/base.yaml (100%) rename {calliope => src/calliope}/math/operate.yaml (100%) rename {calliope => src/calliope}/math/spores.yaml (100%) rename {calliope => src/calliope}/math/storage_inter_cluster.yaml (100%) rename {calliope => src/calliope}/postprocess/__init__.py (100%) rename {calliope => src/calliope}/postprocess/results.py (100%) rename {calliope => src/calliope}/postprocess/util.py (100%) rename {calliope => src/calliope}/preprocess/__init__.py (100%) rename {calliope => src/calliope}/preprocess/checks.py (100%) rename {calliope => src/calliope}/preprocess/model_data.py (100%) rename {calliope => src/calliope}/preprocess/model_run.py (100%) rename {calliope => src/calliope}/preprocess/nodes.py (100%) rename {calliope => src/calliope}/preprocess/time.py (100%) rename {calliope => src/calliope}/preprocess/util.py (100%) rename {calliope => src/calliope}/time/__init__.py (100%) rename {calliope => src/calliope}/time/clustering.py (100%) rename {calliope => src/calliope}/time/funcs.py (100%) rename {calliope => src/calliope}/time/masks.py (100%) rename {calliope/test => tests}/__init__.py (100%) rename {calliope/test => tests}/common/constraint_sets.yaml (100%) rename {calliope/test => tests}/common/html_strings.yaml (100%) rename {calliope/test => tests}/common/lp_files/balance_conversion.lp (100%) rename {calliope/test => tests}/common/lp_files/carrier_production_max.lp (100%) rename {calliope/test => tests}/common/lp_files/energy_cap.lp (100%) rename {calliope/test => tests}/common/lp_files/resource_max.lp (100%) rename {calliope/test => tests}/common/lp_files/storage_max.lp (100%) rename {calliope/test => tests}/common/test_model/energy_cap_per_storage_cap.yaml (100%) rename {calliope/test => tests}/common/test_model/model.yaml (100%) rename {calliope/test => tests}/common/test_model/model_minimal.yaml (100%) rename {calliope/test => tests}/common/test_model/scenarios.yaml (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/alternating_cost.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/binary_one_day.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/carrier_ratio.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/cluster_days.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/clusters.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/cost.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/demand_elec.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/demand_elec_15T_to_2h.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/demand_elec_15mins.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/demand_elec_positive.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/demand_heat.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/demand_heat_diff_dateformat.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/demand_heat_wrong_dateformat.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/demand_heat_wrong_length.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/demand_simple.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/supply_plus_resource.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/supply_plus_resource_inf.csv (100%) rename {calliope/test => tests}/common/test_model/timeseries_data/supply_simple.csv (100%) rename {calliope/test => tests}/common/test_model/weighted_obj_func.yaml (100%) rename {calliope/test => tests}/common/util.py (99%) rename {calliope/test => tests}/common/yaml_file.yaml (100%) rename {calliope/test => tests}/conftest.py (99%) rename {calliope/test => tests}/test_backend_expression_parser.py (99%) rename {calliope/test => tests}/test_backend_helper_functions.py (99%) rename {calliope/test => tests}/test_backend_latex_backend.py (99%) rename {calliope/test => tests}/test_backend_parsing.py (99%) rename {calliope/test => tests}/test_backend_pyomo.py (99%) rename {calliope/test => tests}/test_backend_pyomo_constraints_conversion_plus.py (98%) rename {calliope/test => tests}/test_backend_pyomo_objective.py (98%) rename {calliope/test => tests}/test_backend_where_parser.py (99%) rename {calliope/test => tests}/test_cli.py (96%) rename {calliope/test => tests}/test_constraint_results.py (99%) rename {calliope/test => tests}/test_core_attrdict.py (99%) rename {calliope/test => tests}/test_core_future_warnings.py (89%) rename {calliope/test => tests}/test_core_model.py (99%) rename {calliope/test => tests}/test_core_preprocess.py (99%) rename {calliope/test => tests}/test_core_time.py (99%) rename {calliope/test => tests}/test_core_util.py (95%) rename {calliope/test => tests}/test_example_models.py (99%) rename {calliope/test => tests}/test_io.py (99%) rename {calliope/test => tests}/test_math.py (95%) rename {calliope/test => tests}/test_model_data.py (98%) rename {calliope/test => tests}/test_model_manipulation.py (95%) diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml deleted file mode 100644 index 1bb17e844..000000000 --- a/.azure-pipelines.yml +++ /dev/null @@ -1,172 +0,0 @@ -trigger: - branches: - include: - - "main" - - "0.6" - paths: - exclude: - - ".gitattributes" - - ".github" - - ".gitignore" - - ".hound.yml" - - ".pre-commit-config.yaml" - - ".readthedocs.yml" - - "AUTHORS" - - "CITATION" - - "LICENSE" - - "Makefile" - - "changelog.rst" - - "doc" - - "paper" - - "requirements_docs.yml" - - "*.md" - -pr: - autoCancel: true # new PR commits will cancel current pipeline jobs in favour of latest commit - branches: - include: - - "main" - - "0.6" - paths: - exclude: - - ".gitattributes" - - ".github/*" - - ".gitignore" - - ".hound.yml" - - ".pre-commit-config.yaml" - - ".readthedocs.yml" - - "AUTHORS" - - "CITATION" - - "LICENSE" - - "Makefile" - - "changelog.rst" - - "doc" - - "paper" - - "requirements_docs.yml" - - "*.md" - -pool: - vmImage: $(IMAGE_NAME) - -strategy: - maxParallel: 10 # free limit is 10 parallel jobs - matrix: - linux-py3.9: - IMAGE_NAME: ubuntu-latest - PYTHON_VERSION: 3.9 - CODECOV: True # Only run on one build - linux-py3.11: - IMAGE_NAME: ubuntu-latest - PYTHON_VERSION: 3.11 - CODECOV: True # Only run on one build - macos-py3.11: - IMAGE_NAME: macOS-latest - PYTHON_VERSION: 3.11 - windows-py3.11: - IMAGE_NAME: windows-latest - PYTHON_VERSION: 3.11 - -steps: - - bash: echo "##vso[task.prependpath]$CONDA/bin" - displayName: Enable conda (UNIX) - condition: ne( variables['Agent.OS'], 'Windows_NT' ) - - - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" - displayName: Enable conda (Windows) - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - - - task: Cache@2 - displayName: Use cached Anaconda environment - inputs: - key: 'conda | "$(Agent.OS)" | "$(PYTHON_VERSION)" | requirements.yml | requirements.txt' - restoreKeys: | - python | "$(Agent.OS)" - python - path: $(CONDA)/envs/calliope - cacheHitVar: CONDA_CACHE_RESTORED - - - powershell: wget https://github.com/coin-or/Cbc/releases/download/releases%2F2.10.8/Cbc-releases.2.10.8-w64-msvc17-md.zip -O cbc.zip - displayName: Download CBC (Windows) - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - - - task: ExtractFiles@1 - inputs: - archiveFilePatterns: 'cbc.zip' # string. Required. Archive file patterns. Default: **/*.zip. - destinationFolder: $(agent.builddirectory)\cbc # string. Required. Destination folder. - cleanDestinationFolder: true # boolean. Required. Clean destination folder before extracting. Default: true. - overwriteExistingFiles: false # boolean. Required. Overwrite existing files. Default: false. - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - - - powershell: Write-Host "##vso[task.prependpath]$(agent.builddirectory)\cbc\bin" - displayName: set path for CBC (Windows) - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - - - bash: sudo chown -R $USER $CONDA - displayName: Take ownership of conda installation (macOS) - condition: eq( variables['Agent.OS'], 'Darwin' ) - - - bash: | - conda config --set always_yes yes --set changeps1 no - conda update -q conda - conda info -a - displayName: Configure and update conda - - - bash: | - conda config --add channels conda-forge - conda create --yes --quiet -n calliope python=$(PYTHON_VERSION) - conda env update --file requirements.yml --name calliope - conda env update --file requirements.txt --name calliope - displayName: Set up environment - condition: eq(variables.CONDA_CACHE_RESTORED, 'false') - - - bash: | - conda install --yes -n calliope coin-or-cbc - displayName: Install coincbc (UNIX) - condition: and(ne(variables['Agent.OS'], 'Windows_NT'), eq(variables.CONDA_CACHE_RESTORED, 'false')) - - - bash: | - source activate calliope - pip install --no-cache-dir --verbose -e . - displayName: Install calliope (UNIX) - condition: and(ne(variables['Agent.OS'], 'Windows_NT'), eq(variables.CONDA_CACHE_RESTORED, 'false')) - - - script: | - call activate calliope - pip install --no-cache-dir --verbose -e . - displayName: Install calliope (Windows) - condition: and(eq(variables['Agent.OS'], 'Windows_NT'), eq(variables.CONDA_CACHE_RESTORED, 'false')) - - - bash: | # cbc -quit may be required to make sure that cbc is 'reset' so its timeout time fits within pyomo's strict limit (see: https://github.com/Pyomo/pyomo/issues/2102) - source activate calliope - cbc -quit - py.test -n 2 --junitxml=junit/test-results.xml --cov=calliope --cov-report=term-missing --cov-report=xml -W ignore::FutureWarning --dist=loadscope - displayName: Run tests (UNIX) - condition: ne( variables['Agent.OS'], 'Windows_NT' ) - - - script: | - call activate calliope - py.test -n 2 --junitxml=junit/test-results.xml --cov=calliope --cov-report=term-missing --cov-report=xml -W ignore::FutureWarning --dist=loadscope - displayName: Run tests (Windows) - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - - - task: PublishTestResults@2 - condition: succeededOrFailed() - inputs: - testResultsFiles: '**/test-*.xml' - testRunTitle: '$(Agent.JobName)' - - - task: PublishCodeCoverageResults@1 - condition: succeededOrFailed() - inputs: - codeCoverageTool: Cobertura - summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml' - - - bash: | - curl -Os https://uploader.codecov.io/latest/linux/codecov - - chmod +x codecov - ./codecov -t ${CODECOV_TOKEN} - displayName: Send results to Codecov - condition: eq( variables['CODECOV'], 'True' ) - env: - CODECOV_TOKEN: $(CodecovToken) diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 4df986561..000000000 --- a/.coveragerc +++ /dev/null @@ -1,2 +0,0 @@ -[run] -omit = calliope/test/*, calliope/lib/* diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 23a40ca1c..000000000 --- a/.flake8 +++ /dev/null @@ -1,11 +0,0 @@ -[flake8] -# Recommend matching the black line length (default 88), -# rather than using the flake8 default of 79: -max-line-length = 88 -extend-ignore = - # See https://github.com/PyCQA/pycodestyle/issues/373 - E203, - # E501: line too long - E501, -exclude = .git,__pycache__,dist,doc -max-complexity = 10 diff --git a/codecov.yml b/.github/.codecov.yml similarity index 100% rename from codecov.yml rename to .github/.codecov.yml diff --git a/.github/workflows/commit-ci.yml b/.github/workflows/commit-ci.yml new file mode 100644 index 000000000..71f287c32 --- /dev/null +++ b/.github/workflows/commit-ci.yml @@ -0,0 +1,44 @@ +name: Branch Push CI + +on: + push: + branches: + - "**" + paths-ignore: + - README.md + - changelog.rst + - LICENSE + - CITATION + - AUTHORS + - doc/** + +defaults: + run: + shell: bash -l {0} + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: mamba-org/setup-micromamba@v1 + with: + micromamba-version: latest + environment-name: ${{ github.event.repository.name }}-ubuntu-latest-311 + environment-file: requirements/base.txt + create-args: >- + -f requirements/dev.txt + python=3.11 + coin-or-cbc + post-cleanup: all + cache-environment: true + + - name: Install package + run: pip install --no-dependencies -e . + + - name: Install jupyter kernel + run: python -m ipykernel install --user --name calliope + + - name: run tests + run: pytest diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml new file mode 100644 index 000000000..a6ca10736 --- /dev/null +++ b/.github/workflows/pr-ci.yml @@ -0,0 +1,73 @@ +name: Pull Request CI + +on: + pull_request: + branches: + - "main" + paths-ignore: + - README.md + - changelog.rst + - LICENSE + - CITATION + - AUTHORS + - doc/** + +defaults: + run: + shell: bash -l {0} + +concurrency: + # Use github.run_id on main branch + # Use github.event.pull_request.number on pull requests, so it's unique per pull request + # Use github.ref on other branches, so it's unique per branch + group: ${{ github.workflow }}-${{ github.ref == 'refs/heads/main' && github.run_id || github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + test: + strategy: + matrix: + os: [windows-latest, ubuntu-latest, macos-latest] + py3version: ["9", "10", "11"] + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + + - uses: mamba-org/setup-micromamba@v1 + with: + micromamba-version: latest + environment-name: ${{ github.event.repository.name }}-${{ matrix.os }}-3${{ matrix.py3version }} + environment-file: requirements/base.txt + create-args: >- + -f requirements/dev.txt + curl + python=3.${{ matrix.py3version }} + post-cleanup: all + cache-environment: true + + - name: Install package + run: pip install --no-dependencies -e . + + - name: install CBC (Windows) + if: matrix.os == 'windows-latest' + run: | + curl -L https://github.com/coin-or/Cbc/releases/download/releases%2F2.10.10/Cbc-releases.2.10.10-w64-msvc17-md.zip -o cbc.zip + unzip cbc.zip -d ${HOME}/cbc + echo "${HOME}/cbc/bin" >> $GITHUB_PATH + + - name: install CBC (Unix) + if: matrix.os != 'windows-latest' + run: micromamba install coin-or-cbc + + - name: Install jupyter kernel + run: python -m ipykernel install --user --name calliope + + - name: run tests + run: pytest + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + if: matrix.os == 'ubuntu-latest' && matrix.py3version == '11' + env: + directory: "./reports/coverage/" \ No newline at end of file diff --git a/.hound.yml b/.hound.yml deleted file mode 100644 index 09d8c1baf..000000000 --- a/.hound.yml +++ /dev/null @@ -1,3 +0,0 @@ -flake8: - enabled: true - config_file: .flake8 diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index e71568d76..000000000 --- a/.pylintrc +++ /dev/null @@ -1,589 +0,0 @@ -# Based on the Apache-2.0 licensed https://github.com/ClusterHQ/flocker/blob/master/.pylintrc -# Inspiration from https://codewithoutrules.com/2016/10/19/pylint/ - -[MAIN] - -# Specify a configuration file. -#rcfile= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -init-hook="import calliope" - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore= - -# Pickle collected data for later comparisons. -persistent=no - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Use multiple processes to speed up Pylint. -# DO NOT CHANGE THIS VALUES >1 HIDE RESULTS!!!!! -jobs=1 - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist= - -# Allow optimization of some AST trees. This will activate a peephole AST -# optimizer, which will apply various small optimizations. For instance, it can -# be used to obtain the result of joining multiple strings with the addition -# operator. Joining a lot of strings can lead to a maximum recursion error in -# Pylint and this flag can prevent that. It has one side effect, the resulting -# AST will be different than the one from reality. -optimize-ast=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time. See also the "--disable" option for examples. -disable=all - -enable=import-error, - import-self, - reimported, - wildcard-import, - misplaced-future, - relative-import, - deprecated-module, - unpacking-non-sequence, - invalid-all-object, - undefined-all-variable, - used-before-assignment, - cell-var-from-loop, - global-variable-undefined, - redefined-builtin, - redefine-in-handler, - unused-import, - unused-wildcard-import, - global-variable-not-assigned, - undefined-loop-variable, - global-statement, - global-at-module-level, - bad-open-mode, - redundant-unittest-assert, - boolean-datetime, - undefined-variable, - redefined-outer-name, - no-name-in-module, - unused-argument, - trailing-whitespace, - trailing-newlines, - missing-final-newline, - unused-variable, - undefined-variable - -# Things we'd like to enable someday: -# redefined-outer-name (requires a bunch of work to clean up our code first) -# no-name-in-module (giving us spurious warnings https://github.com/PyCQA/pylint/issues/73) -# unused-argument (need to clean up or code a lot, e.g. prefix unused_?) - -# Things we'd like to try. -# Procedure: -# 1. Enable a bunch. -# 2. See if there's spurious ones; if so disable. -# 3. Record above. -# 4. Remove from this list. - # deprecated-method, - # anomalous-unicode-escape-in-string, - # anomalous-backslash-in-string, - # not-in-loop, - # function-redefined, - # continue-in-finally, - # abstract-class-instantiated, - # star-needs-assignment-target, - # duplicate-argument-name, - # return-in-init, - # too-many-star-expressions, - # nonlocal-and-global, - # return-outside-function, - # return-arg-in-generator, - # invalid-star-assignment-target, - # bad-reversed-sequence, - # nonexistent-operator, - # yield-outside-function, - # init-is-generator, - # nonlocal-without-binding, - # lost-exception, - # assert-on-tuple, - # dangerous-default-value, - # duplicate-key, - # useless-else-on-loop, - # expression-not-assigned, - # confusing-with-statement, - # unnecessary-lambda, - # pointless-statement, - # pointless-string-statement, - # unnecessary-pass, - # unreachable, - # eval-used, - # exec-used, - # bad-builtin, - # using-constant-test, - # deprecated-lambda, - # bad-super-call, - # missing-super-argument, - # slots-on-old-class, - # super-on-old-class, - # property-on-old-class, - # not-an-iterable, - # not-a-mapping, - # format-needs-mapping, - # truncated-format-string, - # missing-format-string-key, - # mixed-format-string, - # too-few-format-args, - # bad-str-strip-call, - # too-many-format-args, - # bad-format-character, - # format-combined-specification, - # bad-format-string-key, - # bad-format-string, - # missing-format-attribute, - # missing-format-argument-key, - # unused-format-string-argument, - # unused-format-string-key, - # invalid-format-index, - # bad-indentation, - # mixed-indentation, - # unnecessary-semicolon, - # lowercase-l-suffix, - # fixme, - # invalid-encoded-data, - # unpacking-in-except, - # import-star-module-level, - # parameter-unpacking, - # long-suffix, - # old-octal-literal, - # old-ne-operator, - # backtick, - # old-raise-syntax, - # print-statement, - # metaclass-assignment, - # next-method-called, - # dict-iter-method, - # dict-view-method, - # indexing-exception, - # raising-string, - # standarderror-builtin, - # using-cmp-argument, - # cmp-method, - # coerce-method, - # delslice-method, - # getslice-method, - # hex-method, - # nonzero-method, - # oct-method, - # setslice-method, - # apply-builtin, - # basestring-builtin, - # buffer-builtin, - # cmp-builtin, - # coerce-builtin, - # old-division, - # execfile-builtin, - # file-builtin, - # filter-builtin-not-iterating, - # no-absolute-import, - # input-builtin, - # intern-builtin, - # long-builtin, - # map-builtin-not-iterating, - # range-builtin-not-iterating, - # raw_input-builtin, - # reduce-builtin, - # reload-builtin, - # round-builtin, - # unichr-builtin, - # unicode-builtin, - # xrange-builtin, - # zip-builtin-not-iterating, - # logging-format-truncated, - # logging-too-few-args, - # logging-too-many-args, - # logging-unsupported-format, - # logging-not-lazy, - # logging-format-interpolation, - # invalid-unary-operand-type, - # unsupported-binary-operation, - # no-member, - # not-callable, - # redundant-keyword-arg, - # assignment-from-no-return, - # assignment-from-none, - # not-context-manager, - # repeated-keyword, - # missing-kwoa, - # no-value-for-parameter, - # invalid-sequence-index, - # invalid-slice-index, - # too-many-function-args, - # unexpected-keyword-arg, - # unsupported-membership-test, - # unsubscriptable-object, - # access-member-before-definition, - # method-hidden, - # assigning-non-slot, - # duplicate-bases, - # inconsistent-mro, - # inherit-non-class, - # invalid-slots, - # invalid-slots-object, - # no-method-argument, - # no-self-argument, - # unexpected-special-method-signature, - # non-iterator-returned, - # protected-access, - # arguments-differ, - # attribute-defined-outside-init, - # no-init, - # abstract-method, - # signature-differs, - # bad-staticmethod-argument, - # non-parent-init-called, - # super-init-not-called, - # bad-except-order, - # catching-non-exception, - # bad-exception-context, - # notimplemented-raised, - # raising-bad-type, - # raising-non-exception, - # misplaced-bare-raise, - # duplicate-except, - # broad-except, - # nonstandard-exception, - # binary-op-exception, - # bare-except, - # not-async-context-manager, - # yield-inside-async-function, - -# ... -[REPORTS] - -# Set the output format. Available formats are text, parseable, colorized, msvs -# (visual studio) and html. You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=parseable - -# Put messages in a separate file for each module / package specified on the -# command line instead of printing them on stdout. Reports (if any) will be -# written in a file name "pylint_global.[txt|html]". -files-output=no - -# Tells whether to display a full report or only the messages -reports=no - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[FORMAT] - -# Maximum number of characters on a single line. -max-line-length=100 - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - -# Maximum number of lines in a module -max-module-lines=1000 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - - -[TYPECHECK] - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules= - -# List of classes names for which member attributes should not be checked -# (useful for classes with attributes dynamically set). This supports can work -# with qualified names. -ignored-classes= - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - - -[VARIABLES] - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_$|dummy - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - - -[SIMILARITIES] - -# Minimum lines number of a similarity. -min-similarity-lines=4 - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -[BASIC] - -# List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,input - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,ex,Run,_ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Regular expression matching correct function names -function-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for function names -function-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct variable names -variable-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for variable names -variable-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression matching correct attribute names -attr-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for attribute names -attr-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct argument names -argument-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for argument names -argument-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Naming hint for class names -class-name-hint=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression matching correct method names -method-rgx=[a-z_][a-z0-9_]{2,30}$ - -# Naming hint for method names -method-name-hint=[a-z_][a-z0-9_]{2,30}$ - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=^_ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - - -[ELIF] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[IMPORTS] - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,TERMIOS,Bastion,rexec - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.* - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of statements in function / method body -max-statements=50 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/.readthedocs.yml b/.readthedocs.yml index edd59acf8..909c2c81b 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,7 +1,7 @@ version: 2 # required formats: [pdf] conda: - environment: requirements_docs.yml + environment: requirements/docs.yml build: os: ubuntu-22.04 # required tools: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 16ed6350a..84e357535 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,7 +63,7 @@ Please try to write clear commit messages. One-line messages are fine for small We have existing test coverage for the key functionality of Calliope. -All tests are in the ``calliope/test`` directory and use [pytest](https://docs.pytest.org/en/latest/). +All tests are in the ``tests`` directory and use [pytest](https://docs.pytest.org/en/latest/). Our test coverage is not perfect and an easy way to contribute code is to work on better tests. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 050c1cf4c..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1 +0,0 @@ -include requirements.txt README.md LICENSE CITATION changelog.rst diff --git a/README.md b/README.md index 8a8e0a25c..10dcda68f 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,9 @@ Calliope comes with several built-in analysis and visualisation tools. Having so ## Quick start -Calliope can run on Windows, macOS and Linux. Installing it is quickest with the `conda` package manager by running a single command: `conda create -c conda-forge -n calliope calliope`. +Calliope can run on Windows, macOS and Linux. Installing it is quickest with the `mamba` package manager by running a single command: `mamba create -c conda-forge -n calliope calliope`. -See the documentation for more [information on installing](https://calliope.readthedocs.io/en/stable/user/installation.html), including what to do if you are having issues with `conda`. +See the documentation for more [information on installing](https://calliope.readthedocs.io/en/stable/user/installation.html). Several easy to understand example models are [included with Calliope](calliope/example_models) and accessible through the `calliope.examples` submodule. @@ -51,8 +51,8 @@ More fully-featured examples that have been used in peer-reviewed scientific pub Documentation is available on Read the Docs: -* [Read the documentation online (recommended)](https://calliope.readthedocs.io/en/stable/) -* [Download all documentation in a single PDF file](https://readthedocs.org/projects/calliope/downloads/pdf/stable/) +- [Read the documentation online (recommended)](https://calliope.readthedocs.io/en/stable/) +- [Download all documentation in a single PDF file](https://readthedocs.org/projects/calliope/downloads/pdf/stable/) ## Contributing diff --git a/calliope/test/common/__init__.py b/calliope/test/common/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/calliope/test/model_conversion/national_scale/model_config/data/csp_r.csv b/calliope/test/model_conversion/national_scale/model_config/data/csp_r.csv deleted file mode 100644 index 29ee4ccc3..000000000 --- a/calliope/test/model_conversion/national_scale/model_config/data/csp_r.csv +++ /dev/null @@ -1,49 +0,0 @@ -,region1-1,region1-2,region1-3 -0,0.0,0.0,0.0 -1,0.0,0.0,0.0 -2,0.0,0.0,0.0 -3,0.0,0.0,0.0 -4,0.0,0.0,0.0 -5,0.0,0.0,0.0 -6,0.0,0.0,0.0 -7,0.02106,0.009056000000000002,0.0 -8,0.263805,0.096755,0.0 -9,0.43403699999999995,0.24535099999999999,0.026837 -10,0.5182769999999999,0.120591,0.171912 -11,0.531398,0.044248,0.27008499999999996 -12,0.5199640000000001,0.020565,0.295433 -13,0.451715,0.0,0.24085299999999998 -14,0.304444,0.0,0.106809 -15,0.062078999999999995,0.0,0.0 -16,0.0,0.0,0.0 -17,0.0,0.0,0.0 -18,0.0,0.0,0.0 -19,0.0,0.0,0.0 -20,0.0,0.0,0.0 -21,0.0,0.0,0.0 -22,0.0,0.0,0.0 -23,0.0,0.0,0.0 -24,0.0,0.0,0.0 -25,0.0,0.0,0.0 -26,0.0,0.0,0.0 -27,0.0,0.0,0.0 -28,0.0,0.0,0.0 -29,0.0,0.0,0.0 -30,0.0,0.0,0.0 -31,0.018765999999999998,0.096031,0.0 -32,0.257264,0.144047,0.0 -33,0.425005,0.098728,0.0 -34,0.510026,0.0,0.12358799999999999 -35,0.520726,0.001851,0.286832 -36,0.5140790000000001,0.0,0.285821 -37,0.44711,0.0,0.21121099999999998 -38,0.298619,0.0,0.113541 -39,0.06490399999999999,0.0,0.0 -40,0.0,0.0,0.0 -41,0.0,0.0,0.0 -42,0.0,0.0,0.0 -43,0.0,0.0,0.0 -44,0.0,0.0,0.0 -45,0.0,0.0,0.0 -46,0.0,0.0,0.0 -47,0.0,0.0,0.0 diff --git a/calliope/test/model_conversion/national_scale/model_config/data/demand-1.csv b/calliope/test/model_conversion/national_scale/model_config/data/demand-1.csv deleted file mode 100644 index d36096995..000000000 --- a/calliope/test/model_conversion/national_scale/model_config/data/demand-1.csv +++ /dev/null @@ -1,49 +0,0 @@ -,region1 -0,-1580280 -1,-1524215 -2,-1483166 -3,-1445190 -4,-1444975 -5,-1480205 -6,-1522795 -7,-1578123 -8,-1630638 -9,-1679404 -10,-1693540 -11,-1687598 -12,-1693571 -13,-1714887 -14,-1788937 -15,-1885392 -16,-1975820 -17,-1997830 -18,-1946237 -19,-1889341 -20,-1822851 -21,-1719294 -22,-1625102 -23,-1565667 -24,-1524446 -25,-1508993 -26,-1514860 -27,-1552608 -28,-1677047 -29,-1830850 -30,-1941396 -31,-2012018 -32,-2060581 -33,-2097656 -34,-2095969 -35,-2084733 -36,-2077134 -37,-2095375 -38,-2154679 -39,-2235576 -40,-2301214 -41,-2302376 -42,-2208150 -43,-2111953 -44,-2012825 -45,-1877927 -46,-1765100 -47,-1692912 diff --git a/calliope/test/model_conversion/national_scale/model_config/data/demand-2.csv b/calliope/test/model_conversion/national_scale/model_config/data/demand-2.csv deleted file mode 100644 index e151a1e75..000000000 --- a/calliope/test/model_conversion/national_scale/model_config/data/demand-2.csv +++ /dev/null @@ -1,49 +0,0 @@ -,region2 -0,-1127049 -1,-1065574 -2,-1045082 -3,-1065574 -4,-1086066 -5,-1086066 -6,-1106557 -7,-1147541 -8,-1229508 -9,-1229508 -10,-1209016 -11,-1209016 -12,-1188525 -13,-1168033 -14,-1147541 -15,-1229508 -16,-1454918 -17,-1434426 -18,-1393443 -19,-1372951 -20,-1311475 -21,-1229508 -22,-1127049 -23,-1147541 -24,-1127049 -25,-1065574 -26,-1045082 -27,-1065574 -28,-1086066 -29,-1086066 -30,-1106557 -31,-1147541 -32,-1229508 -33,-1229508 -34,-1209016 -35,-1209016 -36,-1188525 -37,-1168033 -38,-1147541 -39,-1229508 -40,-1454918 -41,-1434426 -42,-1393443 -43,-1372951 -44,-1311475 -45,-1229508 -46,-1127049 -47,-1147541 diff --git a/calliope/test/model_conversion/national_scale/model_config/data/set_t.csv b/calliope/test/model_conversion/national_scale/model_config/data/set_t.csv deleted file mode 100644 index 7725762dc..000000000 --- a/calliope/test/model_conversion/national_scale/model_config/data/set_t.csv +++ /dev/null @@ -1,48 +0,0 @@ -0,2005-01-01 00:00:00 -1,2005-01-01 01:00:00 -2,2005-01-01 02:00:00 -3,2005-01-01 03:00:00 -4,2005-01-01 04:00:00 -5,2005-01-01 05:00:00 -6,2005-01-01 06:00:00 -7,2005-01-01 07:00:00 -8,2005-01-01 08:00:00 -9,2005-01-01 09:00:00 -10,2005-01-01 10:00:00 -11,2005-01-01 11:00:00 -12,2005-01-01 12:00:00 -13,2005-01-01 13:00:00 -14,2005-01-01 14:00:00 -15,2005-01-01 15:00:00 -16,2005-01-01 16:00:00 -17,2005-01-01 17:00:00 -18,2005-01-01 18:00:00 -19,2005-01-01 19:00:00 -20,2005-01-01 20:00:00 -21,2005-01-01 21:00:00 -22,2005-01-01 22:00:00 -23,2005-01-01 23:00:00 -24,2005-01-02 00:00:00 -25,2005-01-02 01:00:00 -26,2005-01-02 02:00:00 -27,2005-01-02 03:00:00 -28,2005-01-02 04:00:00 -29,2005-01-02 05:00:00 -30,2005-01-02 06:00:00 -31,2005-01-02 07:00:00 -32,2005-01-02 08:00:00 -33,2005-01-02 09:00:00 -34,2005-01-02 10:00:00 -35,2005-01-02 11:00:00 -36,2005-01-02 12:00:00 -37,2005-01-02 13:00:00 -38,2005-01-02 14:00:00 -39,2005-01-02 15:00:00 -40,2005-01-02 16:00:00 -41,2005-01-02 17:00:00 -42,2005-01-02 18:00:00 -43,2005-01-02 19:00:00 -44,2005-01-02 20:00:00 -45,2005-01-02 21:00:00 -46,2005-01-02 22:00:00 -47,2005-01-02 23:00:00 diff --git a/calliope/test/model_conversion/national_scale/model_config/locations.yaml b/calliope/test/model_conversion/national_scale/model_config/locations.yaml deleted file mode 100644 index 46762c1da..000000000 --- a/calliope/test/model_conversion/national_scale/model_config/locations.yaml +++ /dev/null @@ -1,64 +0,0 @@ -## -# nodes -## - -nodes: - region1: - techs: ["demand_power", "unmet_demand_power", "ccgt"] - override: - demand_power: - x_map: "region1: demand" - constraints: - r: file=demand-1.csv - r_scale: 30 - ccgt: - constraints: - e_cap.max: 30000 # increased to ensure no unmet_demand in first timestep - - region2: - techs: - ["demand_power", "unmet_demand_power", "battery", "dummy_demand_power"] - override: - demand_power: - x_map: "region2: demand" - constraints: - r: file=demand-2.csv - r_scale: 220 - dummy_demand_power: - constraints: - r_scale_to_peak: -5000 - - region1-1,region1-2,region1-3: - within: region1 - techs: ["csp"] - -## -# TRANSMISSION CAPACITIES -## - -links: - region1,region2: - ac_transmission: - constraints: - e_cap.max: 10000 - -## -# METADATA -## - -metadata: - # map boundary defined by the lower left and upper right of the square - map_boundary: - lower_left: - lat: 35 - lon: -10 - upper_right: - lat: 45 - lon: 5 - - node_coordinates: # lat, lon coordinates in a dictionary - region1: { lat: 40, lon: -2 } - region2: { lat: 40, lon: -8 } - region1-1: { lat: 41, lon: -2 } - region1-2: { lat: 39, lon: -1 } - region1-3: { lat: 39, lon: -2 } diff --git a/calliope/test/model_conversion/national_scale/model_config/model.yaml b/calliope/test/model_conversion/national_scale/model_config/model.yaml deleted file mode 100644 index 5cbece328..000000000 --- a/calliope/test/model_conversion/national_scale/model_config/model.yaml +++ /dev/null @@ -1,45 +0,0 @@ -## -# IMPORT OTHER FILES -## - -# Can either be paths relative to this file, or absolute paths - -import: - - "techs.yaml" - - "locations.yaml" - -## -# MODEL NAME -## - -name: "Test model" - -## -# DATASET PATH -## - -# Can either be a path relative to this file, or an absolute path - -data_path: "data" - -## -# OBJECTIVE FUNCTION -## - -# 'constraints.objective.objective_cost_minimization' is used by default -# objective: - -## -# ADDITIONAL CONSTRAINTS -## - -constraints: - - constraints.optional.ramping_rate - -## -# OTHER MODEL-WIDE OPTIONS -## - -system_margin: - power: 0 - heat: 0 diff --git a/calliope/test/model_conversion/national_scale/model_config/techs.yaml b/calliope/test/model_conversion/national_scale/model_config/techs.yaml deleted file mode 100644 index 1bf8a85b9..000000000 --- a/calliope/test/model_conversion/national_scale/model_config/techs.yaml +++ /dev/null @@ -1,98 +0,0 @@ -## -# TECHNOLOGY DEFINITIONS -## - -techs: - renewable_supply_plus: - name: "Renewable supply" - parent: supply_plus - carrier_out: "power" - - ## - # Supply - ## - ccgt: - name: "Combined cycle gas turbine" - color: "#FDC97D" - stack_weight: 200 - parent: supply - carrier_out: power - constraints: - r: inf - e_eff: 0.5 - e_cap.max: 40000 # kW - costs: - monetary: - e_cap: 750 # USD per kW - om_fuel: 0.02 # USD per kWh - csp: - name: "Concentrating solar power" - color: "#99CB48" - stack_weight: 100 - parent: renewable_supply_plus - constraints: - use_s_time: true - s_time.max: 24 - s_loss: 0.002 - r: file # Will look for `csp_r.csv` in data directory - e_eff: 0.4 - p_eff: 0.9 - r_area.max: inf - e_cap.max: 10000 - costs: - monetary: - s_cap: 50 - r_area: 200 - r_cap: 200 - e_cap: 1000 - om_var: 0.002 - depreciation: - monetary: - interest: 0.12 - - ## - # Storage - ## - battery: - name: "Battery storage" - color: "#DC5CE5" - parent: storage - carrier: power - constraints: - e_cap.max: 1000 # kW - s_cap.max: inf - c_rate: 4 - e_eff: 0.95 # 0.95 * 0.95 = 0.9025 round trip efficiency - s_loss: 0 # No loss over time assumed - costs: - monetary: - s_cap: 200 # USD per kWh storage capacity - ## - # Demand - ## - demand_power: - name: "Power demand" - parent: demand - carrier: power - dummy_demand_power: - name: "DUMMY power demand" - parent: demand - carrier: power - unmet_demand_power: - name: "Unmet power demand" - parent: unmet_demand - carrier: power - - ## - # Transmission - ## - ac_transmission: - name: "AC power transmission" - parent: transmission - carrier: power - constraints: - e_eff: 0.85 - costs: - monetary: - e_cap: 200 - om_var: 0.002 diff --git a/calliope/test/model_conversion/national_scale/run.yaml b/calliope/test/model_conversion/national_scale/run.yaml deleted file mode 100644 index 848babbb7..000000000 --- a/calliope/test/model_conversion/national_scale/run.yaml +++ /dev/null @@ -1,73 +0,0 @@ -## -# RUN SETTINGS -## - -name: "Test run" # Run name -- distinct from model name! - -model: "model_config/model.yaml" - -output: # Only used if run via the 'calliope run' command-line tool - format: csv # Choices: netcdf, csv - path: "Output" # Will be created if it doesn't exist - -mode: plan # Choices: plan, operate - -solver: cbc - -## -# PARALLEL RUN SETTINGS -## - -# Ignored unless run via the 'calliope generate' tool - -parallel: - name: example-model-national - environment: bsub # Choices: bsub, qsub - pre_run: # Commands to run before executing model - post_run: # Commands to run after executing model - iterations: - - subset_t: ["2005-01-01", "2005-01-31"] - override.nodes.r1.techs: ["demand", "unmet_demand", "ccgt"] - - subset_t: ["2005-02-01", "2005-02-31"] - override.nodes.r1.techs: ["demand", "unmet_demand"] - resources: # Request resources on a computing cluster - threads: # Non-default number of threads - wall_time: # Run time (minutes) - memory: # Working memory (MB) - -## -# TIME RESOLUTION ADJUSTMENT -## - -# time: -# resolution: 6 # Reduce rest of data to 6-hourly timesteps -# masks: # Look for week where CSP output is minimal -# - function: mask_extreme_week -# options: {what: min, tech: csp} -# - -## -# SUBSETS -## - -# Leave any of these empty to disable subsetting - -subset_y: [] # Subset of technologies -subset_x: [] # Subset of nodes -subset_t: ["2005-01-01", "2005-01-02"] # Subset of timesteps - -## -# MODEL SETTINGS OVERRIDE -## - -# Override anything in the model configuration - -override: - -## -# DEBUG OPTIONS -## - -debug: - keep_temp_files: false # Keep temporary files - symbolic_solver_labels: false # Use human-readable component labels? (slower) diff --git a/doc/_static/notebooks/calliope_model_object.ipynb b/doc/_static/notebooks/calliope_model_object.ipynb index 2a3f7bbd7..b76253ad8 100644 --- a/doc/_static/notebooks/calliope_model_object.ipynb +++ b/doc/_static/notebooks/calliope_model_object.ipynb @@ -6,9 +6,8 @@ "metadata": {}, "outputs": [], "source": [ - "import pandas as pd\n", - "\n", "import calliope\n", + "import pandas as pd\n", "\n", "calliope.set_log_verbosity(\"INFO\", include_solver_output=False)" ] @@ -125,7 +124,7 @@ "source": [ "# All locations now hold all information about a technology at that location\n", "\n", - "m._model_run[\"locations\"][\"X2\"][\"techs\"][\"pv\"]" + "m._model_run[\"nodes\"][\"X2\"][\"techs\"][\"pv\"]" ] }, { @@ -160,124 +159,7 @@ "source": [ "# This includes location-specific overrides, such as energy_cap_max of 50 for the pv technology at location X3\n", "\n", - "m._model_run[\"locations\"][\"X3\"][\"techs\"][\"pv\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['X3::power_lines:X1',\n", - " 'X1::supply_gas',\n", - " 'X3::boiler',\n", - " 'X3::pv',\n", - " 'X2::pv',\n", - " 'X1::supply_grid_power',\n", - " 'X1::chp',\n", - " 'X2::power_lines:X1',\n", - " 'X1::power_lines:X2',\n", - " 'X3::demand_heat',\n", - " 'X2::heat_pipes:N1',\n", - " 'X3::heat_pipes:N1',\n", - " 'X1::pv',\n", - " 'X1::power_lines:X3',\n", - " 'X1::demand_heat',\n", - " 'N1::heat_pipes:X3',\n", - " 'X2::supply_gas',\n", - " 'X2::demand_electricity',\n", - " 'N1::heat_pipes:X2',\n", - " 'X2::demand_heat',\n", - " 'X1::demand_electricity',\n", - " 'N1::heat_pipes:X1',\n", - " 'X2::boiler',\n", - " 'X1::heat_pipes:N1',\n", - " 'X3::supply_gas',\n", - " 'X3::demand_electricity']" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# All sets have also been collated.\n", - "# locations and technologies are concatenated into loc::tech sets,\n", - "# to create a dense matrix and smaller overall model size\n", - "\n", - "m._model_run[\"sets\"][\"loc_techs\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['X3::power_lines:X1',\n", - " 'X1::supply_gas',\n", - " 'X3::boiler',\n", - " 'X3::pv',\n", - " 'X2::pv',\n", - " 'X1::supply_grid_power',\n", - " 'X1::chp',\n", - " 'X2::power_lines:X1',\n", - " 'X1::power_lines:X2',\n", - " 'X3::demand_heat',\n", - " 'X2::heat_pipes:N1',\n", - " 'X3::heat_pipes:N1',\n", - " 'X1::pv',\n", - " 'X1::power_lines:X3',\n", - " 'X1::demand_heat',\n", - " 'N1::heat_pipes:X3',\n", - " 'X2::supply_gas',\n", - " 'X2::demand_electricity',\n", - " 'N1::heat_pipes:X2',\n", - " 'X2::demand_heat',\n", - " 'X1::demand_electricity',\n", - " 'N1::heat_pipes:X1',\n", - " 'X2::boiler',\n", - " 'X1::heat_pipes:N1',\n", - " 'X3::supply_gas',\n", - " 'X3::demand_electricity']" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# For every constraint, a set of loc_techs (or loc_tech_carriers) is prepared,\n", - "# so we only build the constraint over that set\n", - "\n", - "m._model_run[\"constraint_sets\"][\"loc_techs_energy_capacity_constraint\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['X2::pv', 'X3::pv', 'X1::pv']" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "m._model_run[\"constraint_sets\"][\"loc_techs_resource_area_constraint\"]" + "m._model_run[\"nodes\"][\"X3\"][\"techs\"][\"pv\"]" ] }, { @@ -3701,440 +3583,6 @@ "m._model_data.energy_cap_max.loc[{\"loc_techs\": \"X3::pv\"}]" ] }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
<xarray.DataArray 'loc_techs_energy_capacity_constraint' (\n", - " loc_techs_energy_capacity_constraint: 26)>\n", - "array(['X3::power_lines:X1', 'X1::supply_gas', 'X3::boiler', 'X3::pv',\n", - " 'X2::pv', 'X1::supply_grid_power', 'X1::chp', 'X2::power_lines:X1',\n", - " 'X1::power_lines:X2', 'X3::demand_heat', 'X2::heat_pipes:N1',\n", - " 'X3::heat_pipes:N1', 'X1::pv', 'X1::power_lines:X3', 'X1::demand_heat',\n", - " 'N1::heat_pipes:X3', 'X2::supply_gas', 'X2::demand_electricity',\n", - " 'N1::heat_pipes:X2', 'X2::demand_heat', 'X1::demand_electricity',\n", - " 'N1::heat_pipes:X1', 'X2::boiler', 'X1::heat_pipes:N1',\n", - " 'X3::supply_gas', 'X3::demand_electricity'], dtype='<U22')\n", - "Coordinates:\n", - " * loc_techs_energy_capacity_constraint (loc_techs_energy_capacity_constraint) <U22 ...
)*tI$RfChNDKw?<7FB)7vw}^G)($8l$UMxu%DX@1W4)%shhqU?8s?qvA=PBR+zcg zO`2a2h1|2(aL-SDX9U$QiEJ{=@DH*t_T6&pZDoFUAAEt{yy!lwWWx*q2}Hw^hcmY~ z`}b}GTYvN9Z*u%S1Ji?@o9%D^-z(Pt%A$YrM#9roO4SvZGXQ@6zXsro$(R{<0bmjn z0k8m=OpSp@U?u@O{&i&1QZ%tKH+B67d|3d!b(}IDS zff>O07x8abK-|A6asL-30F#=Rqd9;{$=K5TFAUglHV67)`Oi2ed2=&sV^IfBfG*I) z4B%$v0I>3~GP1F-voiAl^#2(o2Amo=$_4PZV*XVgQqB%;j{h5-e@*j`^*;<%fs;EM z+q*daozm3nKjtq0S7*0>$o%Ca21JRQyIY%@t4N9dh5UQ9D&{T@ZqBCWE`Y!BRry~M7FHbfX zFP#|E_|T+8{r0$eP;aZfW6F^9(wzTF`*`H>(OLVP&%X0UOf%=u{rNU@+5LI>cq#O8 zshHax_Og15DfRkIGphTOG37L``~5cH{o^t}t41%TdxJB`djSWD3rF+o@w<1-QsgzW zaOKi^8@6 &`zv3vsprcGE`oQs){c0TiyloR zu;>-L&er;QM00pnu-_^2kyejkC|a{@&H+_hKTT zf8Fxt)9K6&7h5?$_lS3GGqunyv`?*BJA};n+ihj=I!@C{D#*EHjxx@p`^Nfu3Xvc4 z!+2sKq3pMiUoAK0Ab0f|PQTk@e^Hc^K_Oq&ez|Gr*AFp>l~k+ka5_bv+%q1&TMb!K z`PU3&cd}B~g}IgZa)ZtsO1{mq7;BsNWc`>{gJYz!sPWy={kwXZ9Gg$zG1KPLEN5pP zb+_@#^xKlYB5`F}`Gevj&7z>)oppuo)-w;xUS-p6pZ)pCHTPyNK}kQ(tqga~+eXc@ zBG_);*fslrwCZvGxGsU4&HFBNHg8dUNR6yV+l8+d$8n8|ZmytK+YtMFL`nXE2Hm6a zLh*CFjsh`#nE(CUioB|9thW{iO AlK>H(jQATYmHsT`Tp+9s#tzsxjuB9XHk+qhn+1l=%@F=R*}&)Ct?(i>4KgM zQ95M^B!V}0h+Kx8c6oe9tYMLtWdc9x9&~(@f519bN1BHfkY_x|!ar7cW2a~;7ysGl z>=Nq}*#s{a-<`j<%>O+)LGfp 2>f|X)aQC8nL17+8D( jGw55)N?+_WebKDdQJ#4qX=|2K#SAMTiGwZLyPzschH+>MXZYPmK~(B zXGXR<7^={s@tz%+(Ny+H5uK5_OCb^rigd!X)qLlQXeV*%Q3S>dbWvpsM^r^8gUosf zUQba5i2|8$l`S5{2&1;+3&^jNMSB6kOfVzc?$@co>PyLJ2`2Zp!L5RB60!3t>97 zoHi~ >&GcJUt8ym+KOt_0u=) z__X>o2GnV3?v@uFaB;I+aYejGHGX8LZ-0hdI~Mbikq-N1)^d&u`)2okBJ|C1X^ygP zIG*Ph#?$edggaGQ52+B86ZGIgo-j>U&EAlRQ6jNI!8uyYn`d`e0x@)ugg?P=l$+gm zEKz>~2&`u(9I;_!szmoF3I7gc(tqLqf;!+~o}D~pNQ96aX!nL*0soDTf7Atbp%~rl zGKVG*UwQzY0e}qpae$Cg-QoC0k r^S1GWbS-=cc{RjqAsPC^!CwhIy0qy3N^t|?fajD&J_KRR(qP`v7 zAt-+r;M)Pe_v-3|N_@#F78Or3b8$d91A%qPl8&$rYdS5)0J<$8X+cc@W_}cbu@M3x z>MDlObJXXP09x5=FVZuJhO!ZoV{yxK2O4_IUB$7@zp<3%NCX`;=#|j3{*EJZ_=P*Z z_te0WmpP*Y0&Ab;NOEB#@eQ)Ii#nc0DLNZs7K^lw8ab ;ZX_f*{%byrdg)I1_E7~7X{lM`b}>gshhOYRC} zCcM3b-JIJVDM>tyIm+lyMhYyU{*rI#iRAr}=9 OM_PXV2umQ$mY#%t=+s#?YZuVNKJ?n8lJ$36U4V%- z;e=;8LvYy)^arT3iUVecONRr03C{*JD$p`LHIp$-5OErFz|CI_Y+z#or$s5`K>`C> zVu3z^0s0pQ=J4tN_A-Y9>>htC;@KGb>z8@5K4X>um=P`nL@HKskJ##2yKX2Y|I |0g5C%8<3%Uz_=vs`pHv|I6WDNIs+?){BMO3M6iVJ{jDzoV0QB$ ziNTCq`$LledDsnYKLES9<(d-+E<<2OfJs3<)E)@L1-0!uzN*5FTz`}T{lpal{RDoA z>xTheY>G(70W*LH$@H%u_<#IJ{o}_o-;UyM1e1VU1H^y;CmwTy-hQyeNZP&1`#~ z4(6OZOO!rm-YQ#;i>4Ci$_f#Th$u@eDdp9kvj6NqW1QNC=fh^@bs>lsx 4chz1p)4zp&|d!!yAWm4vF~Ct}}bIq-WgWoV;ZLT}-`I z+~Mg2?!?XGs0lxDJVUXbA^GoHC`LUuf^bNg$&KK?BO ixZ zKMEBWXTL=3SIznBu(oAEQB`JHuc&vj*!GH^B8sNNN^foHsB`DB7w^6=w%LK}+^AaD zue(rDmM;3(k|y{d86_xaue4Q&OW4Hq>zf+ob%?KsDvP*i4co2AZ-k*=m-DXTUcy3c zmh(Zz^fJPaN!k3{AmE;)De$bi+1HG= 5`ys{Hkw&xjfOb4wWyQ9RgORgr{Dug+q$?vPB`d$ke0%M1e;k%7b~vSv+<~9g~@C z5*Jh m^v7gl3nE)C@z%fa9Yni@~`q96O?7v z0h_TvY4AiX1Td!i#5&Ea3V3rA>7K3EK3ZXjP!XKiA(QeIF_@q_2JoLgqJT3z%)7oK z 5LCtnkq-c`C;)m~WFiuzQ;M<6T seFeTtL zW^MIu0@Qo4To-RVdpAJ#nVPn)!6u*88;rV<=nYzek;ioJ&|x*N^vt;0&18j#gY6P= z_3MX&4?z@4=ydCl17G2F>kOrkl4l{?7s^j^sP*fPgT3JC(YN7=OwA0koc(4X+jW*e zA%-{CnOc~l=NtkQjYIS_GX(E!1BmCPG*bMRG)@G;iitZ2>&DeosU;N!8)rv578Y*k z#HZl6G9QanVM4pw#p`<7v}@png|-Hmmc% X43)Q^&(RUwf-rM8({QWW zf^XX$(DNx^8WS6Ia jof*=OWt`$OK;ElR&S0oGA@9=3EKhX}U z=@s(>{lx+B1_*U;cjVW>+H*4)$z|QmW<7m{tuXX*dC+yjwD=uxavN2&_zOSMu}KxV zn4f-Sd@YFNx0Z#5+^Q(JRz2iX*{n7J-8GC2GP$mbi3#&+3WbQtx91O_b*tabr^O<^ z7zMqF@F~~gKToz8H`#|qgYDfpCt2O40Uh+nlkv6SXp0`Wtl0X!$prWiBdKgqCh%W{ ziFCyX-{e#oetHe-yM)X1?V gFYRC^R`z!y !CDtCF z$6C5T9_=92d;z_y&D~4)Bu1|m3^oI9M(4PNPZQg*deE~QMhH2*nmOxhoK2h+#~?pF zhFqMc$ zARw2abs;*5z-yR%bKTq=Z4e$Xp1h?pGCLF@UH$4V*r&9qM0(t2_c41%9LvwaYPxA( zA!~>knH<;Qi{(v%PS4&pwV($16$4M1C=r)mPThhQX`<^9P#@UR0!dHT4_|OExUh#? z!1c3YG{>OrMVHabUjyFYUIg;Lw}k!JjxgGEPko2Xz!T{@clK^Uhktx%E |aX9qdT&2fwd2PB+;|g=RZv{8^OYVbLQ@M89&QIo{rby1g#j8tJP~V(m;mK4+B% zfRxJ$hS|*|MvMNEob~Pw-&_1Avw|1$4`>n`E|)vf-6{YUKA|3ynppu{PLpNV9kbR9 zqj%c&u?#8>D+NT&|3~4Tk}+f|U!}gdzmrrdUw~X^+&!pTD~V#2fRm3zYUNMiv){F2 zaI_x&Uvq=Z5-LU71{11QYv|1QQoY6vyAn;B#6RP&BtSCGdo&fY2l{NiXb^`C2CiUN zk~Yf}t)-KVN?jNa8ranJnmSIs`zsI4c*e#I5{^yL+o{xg?&@RS-NIxY%Dy+G_JS~e zLvoG7u7T59S@!hp$~Bj3XU&Feq6Zyhs|;CKH{jrIXpoRPLu$ct+!MBHhY{@{*&4PM zlo`YzrI_0qjw|JNjffj}6?v7}^_ 7>^(?Bgpok8hUSo=r^ci;wg`DR$V{1p8_8 z6Q+Pk(8B~gflAuYY%oF_O7gq#hGRWVXOe*dH_>s0-gT-*bel9iAU>@%EZiQGCTR#n z>_>)ZTP}<=)Rj>W5+m#bY(dAqS1;99yboL~%|O1NBcNH-S_G=>+K@@I`0DI6w>9C4 zVBbo$Ff+{~9xwqF!Ro0OzGy=5aO#jy>w;Y0Mb%4ggP+&F30JVH7#?HNm#HOINVNIe zVhgK7?eW{?%ffIp5ARRV%@4dze04+Y!?b||bw7QM3+H$~U!-qRsf#j6-dl_|76|4{ zoSX;vT<6+JC9|qbSar@eC!YpWO1KEUk 6@xfv|F0UiwA8D%Kcy{jA3XHW`=v8Fol0 z2gt9Z_T^tw6tk5(_s^lEYh9X7IitY}yS5=@E0K&q(M~K4^mt|c900;P=)VNt2J`EE z-A>GSRO7Y$hn$5eZJ0krbWiDI*t fp!O4t4Y0E9$(EfKP|Mn36$;i!hlkIQRWJ z)(Be_?8hyUi*Pgt<_^!IflyQ`48Kg+nf9ltrc$NLfLOI?rd6X>m9>6?Cs2jKz>p7N zyISGLe9d9RMVqdFXmg4 3d) z_pfa_M@@G &0AeQxFG#; }O>`rZZoi2bSy%PrpiOsgii*BGrnaIDa_iLWt`;T8 z)jyIHJHPwEj8O1(gp(& ajq+fcoZSeJ$q-t%z=S-QVyzmS?-D2QQ8`i zfCr<8;YMZUBQZ>4?1|d8dlVDxiSl*xf%nADQhIp7N>Y9hOx0&@SND63P`)K-v_(+=5 ~OMAA{VKj ze9L9;fWivJD(|-9nE#;}hiD#nWHbIt((k2#Y$f_6aHm-EUBc<>H}7rB59a^|l_|y* zr)Wfplcse9>3OjG0l>HRXQ*9eBs=#?;Y8H=vhE|-l(KFJkRJ*G?KOkPA&BLZ{=Im! zD|gqfHi_X?%p^YCWWL(RDp?9+%LS8Wca^0boo!G_w~}1Lf3!7F)S|_2Smocx2tg!} z%oJ}318+cM#;@jz>4hO7q7laQ0HSJWlLf;USD4M|2+`xVhk^@n7G2L#o^8f(8eNL1 z1*y 5p<1s)I=#a11Yf&Hr zWKF?ClR!#-Y0r$Su3Jrj9LV?nV??2Fu 9_IN1~$xdny~BR K7U(ED=&vWmeIRl)U?;f{2%nDSP6FPz5vAzNqgeEY{mo7h z*KhfV=d2zx3+<2?Z!N0qy=!zLhWnk;&+$I!wqBWuk>gnK&u}KGS}}5Zc)norwt+`? z-}IgT2WsMG)v;q$Y_5n%UHP-4fApMb9OTFWNzyIOw5ufg&PKjUojnp{3@ji-!)0fg z8WOv@$IMD)?KK_BzLd&Ye8)C9|wu?)}0ykXRVt&;e*R$hGQqaLAEoFo=NJ8Ny9hJbmA8YW4DLl%WDZf z>Y7OuBO1dEwLmvc*_xE$D_xPrKgGVQK1noEFNYs^nc6ja2UlCSNW-aglSRc-dOQ|n zt6XJ<$@|OH0 nTwgwFX!2knIez&glrZqt*s~VwZ C+;jNe9dI z1aY*=VKRLD;m;V2?cQ=~>3KZylPp-VP)GC_;8mgZS_YLaJR&K9^)QTfr~aii*J;NS zO@ddHMz8%#YHl&>7_0fGD-Y*y(7rx_uf{F&jdi4Oi72iC-rX~Di;W@ozvUq|$^x;i zMe4OS5JCK*YDq+t(>KIWKXN+E$rBsJ-uAk~hveC#WLZAc^sgUHaUh@P;M!a>>_cId zK3bJizfj^6&|ecX3u!}Tq#5C^2R 0Ze~mbgKVHv2Ly25oKY51e>!%e<43f~ z3M>1c_ ;Fv4apY#p-lK_NeXl|=eEaF z3ShSrJ-e^7RW*879e_DHik1L #cpz!VV7Oy?j;u zK@Q8RnG8MProCP+-|w|C^YR^b(c0c!s0H)Eu(|ww)F6-M&Q &ZB)v{O50cw==1U6{zRqclMfsU+twEnhARl6AmG(RQ_fPE1b!-WAkf2@d zUc7*%YRgElo4fj~TuoL9FiI7Y=cl^$bJ6E2z|HMTOZTyr@XayQ`llXS^hk34Y_|@1 z;0;Y3{#9#5;3$>iyJtFKov(rsx==W(k)RYdlpr}qW`2KY#84=-vXvl(aIYOUs_n+a zutf>z ^EDR4xWtA`m!1IoY08V zZz90=ymU}EGNDB;9`1YjCZn_na#YRxvCtVfD7JMnT)nvZY%FxrgZRv{J+C%`*T>Ib z2cX3w`Kplk*l6yqTAqe8k9W1tpW2rh;afPk3E8Q`f_NCV2w>mxC18B{1#$r$Y<{t9 z)biH1j%yy~1dA<`64c&ImyK)%sOq~QDX3jL+4@e-?&twVl1L@fD-XiT)Z=rtuTAd1 zOBCD@agxqVBIv0Uwh^+F>MGUAIQ-gi;XTRK&e+3OUS@XsTRj@gD}JIb=4fmL(<^`F zrd?9=NC11P7`PQaQzO`d?e7RzeixwTzK=dIZ-N|p_=ez^TbFl?y(Y~T_Y)vn-yJ@Q zl3Ndjx4z($(NIAx{SYDX%#u gVX93M8)tpgc&!}DzFmPZ;qN@YlOo1mojG0_K_s%U7bN8%-FKC4-Po#Crz+|o+< zNB9}{+6-(zSp(JV#erOD!#C~>@ZHF~!I4M069Id62|3>M34)cW5NRk!g$~>gCbC;( z6QW-H*Nboq{__Lxg%kWeXVp(Ij{+K90}v8VCD;$BD-NiFIeB5dZ@9#G!#j}ka~a#l zpS+>Gcn}gB69T61%Y=jrIRx&?i}Ou9w9d3#psV=dk!>&Tt9TRj_EF!JyV+z!Sq>`{ zK9_MOs6+(p3to#zR1D33o;d&HDH_)iy( 4NifD`;EpX8k$yO6kGwn;2Qx#5xjFgI%hvW ieq2>i$+ zY|vxF$$vM}6N1|b>eey>6B@n?aerzfx=qbD@NLlYQgSE?WLL2V+^eEJxE1R9^=bU^ z(+wuTr4vdiuf)iSZnFmXOw|`#I0xKR=~`Hh;hV4{vP3krvs+8iys&{pbi|l048d4b zAlTqKpI0xKTs(SqtMgdzZqVR;XHf0Qwy?asUA!GKJM$)zaeFn+bWhJgx}MZdc?ei_ z)oD{H8We<%;rF|vz!RsQ6K)+@d$+)NiIHYVDyXMesh?cF&iy{NomWY0%U`yMq6ez) z7<2|Zb_8g==NzDYaM#cY@381UlpCqmJ;NW4)DL>|*lqtvEiiBV@TMS~)=>Or)4IL{ zvsi=0`OA}VPeyLM)ZMbCfi5gup5cR?2acF4IZHE~5knxYWJRrE)Nuj)XErhCLR@yc z{zhWVU9@g0$STA1SLV#_E>!9r{+rJjG6 <18Px05eZpayaxo#>VtSCR7{^9k)~>+89PEzfuLn^baWR@@`LE)HpLfnY z6^$UMKrF@igFIic$a*Q)`GYRd(9vqsCeV-KE~hl;5EKCdcvxoaE$mVSMMzeyq30o| zQ?U%j?}gNgZ-K7u1T@WJ^T;?gxJXKnE)Z2~0ri-?u#ONa!jy;37ILBlLDrvDZV%r{ zWeGr869T36>{tmvgF${a^A`~1l5O!Z@zB}F1v-#k@I$&m@HW7tiGhROgBQW?g0op> z6B{9#E>>*DxWVK#hu#}WXm6k^omk3qMGNOpF~9i~h&A0pR0;O{k`1B~rkoeGTw0VA z09EF_L8(cb=MRD-8 O9QK2O-X=9di@GeVy57wNp+HSYQS47zDQ z_!{9Z-wVdJ)gM&M9NoRMi_#+mK~Gkh_J7V!U 7*z;qor?_Tb^fX^KP8WY&m{v&2a(N~ZaOi7W8j$k!` zWpT+~{s#yM(0wllD2`BQAoxTk %vWn$Ixx@0ySqxE-tQp|OR{BLP`7FR>aLn3bRq3ql9mfby=V#9r9yWUzpzLp zp)2N=mgR7m7E!LcH~Daa*EZwYYdN>3Ed%cw1_vpn1#oM^HjGQvEn*oOC$Ud6l@e zV0UTSmuho&wPdnBu(gvGmOZ6}Bu(Pcb?|t@wi{_Hn@l@