From a3097ec55aa08953695c181f75da7605dd8fca54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D8=A8=D9=84=D8=A7=D9=84=20=D9=85=D8=B3=D9=84=D9=88=D8=A8?= Date: Wed, 17 Jul 2024 20:50:45 +0200 Subject: [PATCH] develop (#260) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Collect hydro validation errors (#2204) collect *n* hydro validation errors (10 per area ) before exiting --------- Co-authored-by: Florian Omnès * Remove actions dependencies using node js 16 (#2215) > Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: notiz-dev/github-action-json-property@release. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/. --------- Co-authored-by: Florian OMNES * Move TS number print (#2228) Purpose : moving the code that prints the TS numbers on disk : due to recent changes it was moved after the loop through MC years. So, we have to wait until simulation ends to know which TS numbers where drawn. So we move that code back. * Infeability analyzer : renaming (#2225) Purpose : as the title says This PR is attached to ticket [ticket ANT-1825](https://gopro-tickets.rte-france.com/browse/ANT-1825). Some improvements were made or tried by taking care of the ticket. The result is this PR. --------- Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com> * Always run clang-format on PR (#2230) * Add changelog for minor versions (#2229) - v8.8.6 - v8.6.8 --------- Co-authored-by: Abdoulbari Zaher <32519851+a-zakir@users.noreply.github.com> Co-authored-by: Jason Maréchal <45510813+JasonMarechal25@users.noreply.github.com> * Infeasibility analyzer : small simplifications (#2226) Purpose : as the title says This PR is attached to ticket [ticket ANT-1825](https://gopro-tickets.rte-france.com/browse/ANT-1825). Some improvements were made or tried by taking care of the ticket. The result is this PR. --------- Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com> * Fix compile, add boost header in CMakelists (#2233) * STS: Withdrawal efficiency [ANT-1862] (#2223) Add a efficiencywithdrawal property to short term storage This mimics the already existing efficiency for injection * Infeasibility analyzer : HydroPower constraint (#2227) [ANT-1825] Add constraint that can possibily lead to infeasibilities. HydroPower is the constraint on the sum of generated hydro energy over the week for a given area. The provided RHS in some cases makes the problem infeasible. --------- Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com> * Compilation warnings (#2237) close #2236 * Version 9.2 (#2240) * Fix sonarcloud job (#2246) * Adequacy Patch regression [ANT-1845] (#2235) * 9.2 rc 1 (#2247) * Keep using node js 16 on centos CI (#2248) * Infeasibility more cleaning (#2231) We're on the road to make **infeasibility analyzer** more changeable, more precisely when adding a constraint type to the list of constraints to be detected in case of infeasibity. This PR is a step towards this purpose. It contains some heterogeneous simplifications and renaming. In order to ease the review, some comments were added in this PR. --------- Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com> * Document clang-format (#2243) Add version number to avoid confusion. * Link TS generation : splitting into multiple files (#2171) This PR aims at moving some code about link TS generation, from **main** program to new source files. It follows PR #2155 To be done : - [x] move headers (*.h) to the right place - [x] update this branch with its base branch (@flomnes made a change on base branch) We may also take advantage of this PR to : - [ ] Thermal TS generation : - [ ] avoid loading the **whole study** when **no** thermal TS generation is required (this is currently the case) - [ ] move implementation details about thermal TS generation from **main** program to new source files - [ ] Separate loading / extracting data from study and generate the TS --------- Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com> Co-authored-by: Florian OMNES * Explain why MPS are named when the problem is infeasible (#2250) * Add tests in CI for version 9.2 (#2241) Co-authored-by: Florian OMNES * Rename test-platform to os for windows CI (#2253) * Fix bug hydro heuristic with mingen (ANT-1825) (#2258) Removing lines that create infeasibility and that are not necessary. --------- Co-authored-by: Juliette-Gerbaux Co-authored-by: Florian OMNES * Infeasibility anaylsis : make it more changeable (#2232) Create classes for each constraint type, with a common interface. Additional tests to come. --------- Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com> Co-authored-by: Florian Omnès * Remove manual dynamic memory usage (#2254) Replaced new[] with vectors Made RunTimeInfos static --------- Co-authored-by: Abdoulbari Zaher <32519851+a-zakir@users.noreply.github.com> Co-authored-by: Florian Omnès Co-authored-by: payetvin <113102157+payetvin@users.noreply.github.com> Co-authored-by: guilpier-code <62292552+guilpier-code@users.noreply.github.com> Co-authored-by: Florian OMNES <26088210+flomnes@users.noreply.github.com> Co-authored-by: Jason Maréchal <45510813+JasonMarechal25@users.noreply.github.com> Co-authored-by: Juliette-Gerbaux <130555142+Juliette-Gerbaux@users.noreply.github.com> Co-authored-by: Juliette-Gerbaux --- .github/workflows/centos7.yml | 1 + .github/workflows/clang-format.yml | 11 +- .github/workflows/oracle8.yml | 6 +- .github/workflows/sonarcloud.yml | 2 +- .github/workflows/ubuntu.yml | 58 ++- .github/workflows/windows-vcpkg.yml | 96 ++-- docs/developer-guide/CHANGELOG.md | 11 + .../developer-guide/continuous-integration.md | 3 +- docs/user-guide/04-migration-guides.md | 3 + docs/user-guide/solver/02-inputs.md | 1 + docs/user-guide/solver/08-command-line.md | 4 +- simtest.json | 2 +- src/CMakeLists.txt | 4 +- src/api/API.cpp | 2 +- src/libs/antares/study/CMakeLists.txt | 3 - .../parts/short-term-storage/properties.h | 9 +- .../antares/study/progression/progression.h | 10 +- .../include/antares/study/runtime/runtime.h | 12 +- .../include/antares/study/runtime/runtime.hxx | 37 -- .../study/include/antares/study/study.h | 4 +- .../parts/short-term-storage/properties.cpp | 32 +- .../antares/study/progression/progression.cpp | 2 +- src/libs/antares/study/runtime/runtime.cpp | 1 + src/libs/antares/study/study.cpp | 43 +- src/libs/antares/study/study.importprepro.cpp | 2 +- src/libs/antares/study/version.cpp | 3 +- .../utils/include/antares/utils/utils.h | 6 +- src/libs/antares/utils/utils.cpp | 12 + .../application/ScenarioBuilderOwner.cpp | 2 +- src/solver/application/application.cpp | 22 +- src/solver/hydro/CMakeLists.txt | 6 +- .../hydro/management/HydroErrorsCollector.h | 63 +++ .../hydro/management/HydroInputsChecker.h | 19 +- .../hydro/management}/finalLevelValidator.h | 25 +- .../hydro-final-reservoir-level-functions.h | 41 -- .../hydro/management/HydroErrorsCollector.cpp | 45 ++ .../hydro/management/HydroInputsChecker.cpp | 122 +++-- .../hydro/management}/finalLevelValidator.cpp | 50 +- .../hydro-final-reservoir-level-functions.cpp | 73 --- .../CMakeLists.txt | 5 +- .../constraint-slack-analysis.cpp | 73 ++- .../constraint.cpp | 220 -------- .../constraint-slack-analysis.h | 11 +- .../infeasible-problem-analysis/constraint.h | 65 --- .../infeasible-problem-analysis/report.h | 22 +- .../watched-constraints.h | 113 ++++ .../infeasible-problem-analysis/report.cpp | 112 ++-- .../watched-constraints.cpp | 191 +++++++ .../HebdoProblemToLpsTranslator.cpp | 4 +- .../constraints/ShortTermStorageLevel.cpp | 4 +- .../opt_gestion_des_couts_cas_lineaire.cpp | 4 +- .../optimisation/post_process_commands.cpp | 2 +- src/solver/simulation/adequacy.cpp | 4 +- src/solver/simulation/common-eco-adq.cpp | 16 +- src/solver/simulation/economy.cpp | 2 +- .../solver/simulation/common-eco-adq.h | 2 +- .../sim_structure_probleme_economique.h | 3 +- .../antares/solver/simulation/solver.hxx | 75 +-- .../antares/solver/simulation/solver_utils.h | 81 +-- .../simulation/sim_alloc_probleme_hebdo.cpp | 26 +- .../simulation/sim_allocation_tableaux.cpp | 4 +- .../simulation/sim_calcul_economique.cpp | 21 +- src/solver/simulation/timeseries-numbers.cpp | 23 +- src/solver/ts-generator/availability.cpp | 164 +++--- src/solver/ts-generator/hydro.cpp | 18 +- .../antares/solver/ts-generator/generator.h | 16 +- .../antares/solver/ts-generator/generator.hxx | 8 +- .../solver/variable/adequacy/overallCost.h | 2 +- .../variable/commons/spatial-aggregate.h | 2 +- .../solver/variable/economy/links/flowQuad.h | 2 +- .../variable/economy/nbOfDispatchedUnits.h | 4 +- .../economy/nbOfDispatchedUnitsByPlant.h | 6 +- .../variable/economy/nonProportionalCost.h | 4 +- .../economy/npCostByDispatchablePlant.h | 4 +- .../solver/variable/economy/operatingCost.h | 4 +- .../solver/variable/economy/overallCost.h | 4 +- .../economy/productionByDispatchablePlant.h | 2 +- .../antares/solver/variable/storage/average.h | 2 +- .../solver/variable/storage/averagedata.h | 2 +- .../antares/solver/variable/storage/raw.h | 2 +- .../antares/solver/variable/storage/rawdata.h | 2 +- src/solver/variable/state.cpp | 12 +- src/solver/variable/storage/averagedata.cpp | 8 +- src/solver/variable/storage/intermediate.cpp | 4 +- src/solver/variable/storage/rawdata.cpp | 8 +- .../variable/surveyresults/surveyresults.cpp | 20 +- .../end-to-end/simple_study/simple-study.cpp | 67 ++- src/tests/inmemory-study/in-memory-study.cpp | 2 +- src/tests/src/api_internal/test_api.cpp | 2 +- .../short-term-storage-input-output.cpp | 2 + .../src/libs/antares/study/test_study.cpp | 2 +- .../test-unfeasible-problem-analyzer.cpp | 49 +- ...-hydro-final-reservoir-level-functions.cpp | 38 +- .../test-store-timeseries-number.cpp | 7 +- .../solver/simulation/test-time_series.cpp | 7 +- .../solver/simulation/tests-ts-numbers.cpp | 39 +- src/tools/ts-generator/CMakeLists.txt | 14 +- .../tools/ts-generator/linksTSgenerator.h | 32 ++ .../tools/ts-generator/tsGenerationOptions.h | 31 ++ src/tools/ts-generator/linksTSgenerator.cpp | 347 +++++++++++++ src/tools/ts-generator/main.cpp | 485 ++---------------- .../ts-generator/tsGenerationOptions.cpp | 77 +++ 102 files changed, 1781 insertions(+), 1651 deletions(-) delete mode 100644 src/libs/antares/study/include/antares/study/runtime/runtime.hxx create mode 100644 src/solver/hydro/include/antares/solver/hydro/management/HydroErrorsCollector.h rename src/{libs/antares/study/include/antares/study/parts/hydro => solver/hydro/include/antares/solver/hydro/management}/finalLevelValidator.h (81%) delete mode 100644 src/solver/hydro/include/antares/solver/hydro/management/hydro-final-reservoir-level-functions.h create mode 100644 src/solver/hydro/management/HydroErrorsCollector.cpp rename src/{libs/antares/study/parts/hydro => solver/hydro/management}/finalLevelValidator.cpp (72%) delete mode 100644 src/solver/hydro/management/hydro-final-reservoir-level-functions.cpp delete mode 100644 src/solver/infeasible-problem-analysis/constraint.cpp delete mode 100644 src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint.h create mode 100644 src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/watched-constraints.h create mode 100644 src/solver/infeasible-problem-analysis/watched-constraints.cpp create mode 100644 src/tools/ts-generator/include/antares/tools/ts-generator/linksTSgenerator.h create mode 100644 src/tools/ts-generator/include/antares/tools/ts-generator/tsGenerationOptions.h create mode 100644 src/tools/ts-generator/linksTSgenerator.cpp create mode 100644 src/tools/ts-generator/tsGenerationOptions.cpp diff --git a/.github/workflows/centos7.yml b/.github/workflows/centos7.yml index 573163d105..5ba13ec5c6 100644 --- a/.github/workflows/centos7.yml +++ b/.github/workflows/centos7.yml @@ -23,6 +23,7 @@ env: IS_RELEASE: ${{ github.event_name == 'workflow_dispatch' }} IS_PUSH: ${{ github.event_name == 'push' }} REF: ${{ inputs.target_branch =='' && github.ref_name || inputs.target_branch}} + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true jobs: diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 9e00892851..8232f319c7 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,12 +1,7 @@ -name: Check cpp formatting +name: Check cpp formatting using clang 18.1.3 on: - push: - branches: - - develop - - feature/* - - features/* - - fix/* + pull_request: jobs: build: @@ -28,7 +23,7 @@ jobs: run: | DIFF=`git status --porcelain` if [[ $DIFF ]]; then - echo "The following files are not well formatted" + echo "The following files are not well formatted, please make sure to use clang-format 18.1.3" echo "$DIFF" exit 1 else diff --git a/.github/workflows/oracle8.yml b/.github/workflows/oracle8.yml index 92b65a9a72..01f040c5ac 100644 --- a/.github/workflows/oracle8.yml +++ b/.github/workflows/oracle8.yml @@ -142,13 +142,15 @@ jobs: cpack -G TGZ - name: Installer TGZ push - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: + name: targz path: _build/*.tar.gz - name: Installer RPM push - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: + name: rpm path: _build/*.rpm - name: Publish assets diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 4587c17919..8dbf991635 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,10 +1,10 @@ name: SonarCloud on: + pull_request: push: branches: - develop - pull_request: jobs: sonarcloud: diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 88fc65ceac..9f129f04f9 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -132,13 +132,15 @@ jobs: cd _build ctest -C Release --output-on-failure - # simtest - name: Read simtest version - id: simtest-version - uses: notiz-dev/github-action-json-property@release - with: - path: 'simtest.json' - prop_path: 'version' + run: | + echo 'SIMTEST_JSON<> $GITHUB_ENV + cat ./simtest.json >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Export simtest version + run: | + echo "SIMTEST=${{ fromJson(env.SIMTEST_JSON).version }}" >> $GITHUB_ENV - name: Init submodule run: | @@ -148,7 +150,7 @@ jobs: if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-named-mps os: ${{ env.os }} variant: "named-mps" @@ -176,7 +178,7 @@ jobs: if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-v830 os: ${{ env.os }} @@ -184,40 +186,48 @@ jobs: if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-milp variant: "milp-cbc" os: ${{ env.os }} - - name: Run tests introduced in v860 + - name: Run tests introduced in 8.6.0 if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-v860 os: ${{ env.os }} - - name: Run tests introduced in v870 + - name: Run tests introduced in 8.7.0 if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-v870 os: ${{ env.os }} - - name: Run tests introduced in v910 + - name: Run tests introduced in 9.1.0 if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-v910 os: ${{ env.os }} + - name: Run tests introduced in 9.2.0 + if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{ env.SIMTEST }} + batch-name: valid-v920 + os: ${{ env.os }} + - name: Run short-tests if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: short-tests os: ${{ env.os }} @@ -225,7 +235,7 @@ jobs: if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-mps os: ${{ env.os }} @@ -233,7 +243,7 @@ jobs: if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: adequacy-patch-CSR os: ${{ env.os }} @@ -241,7 +251,7 @@ jobs: if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-parallel os: ${{ env.os }} variant: "parallel" @@ -250,7 +260,7 @@ jobs: if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: ts-generator os: ${{ env.os }} variant: "tsgenerator" @@ -259,7 +269,7 @@ jobs: if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: medium-tests os: ${{ env.os }} @@ -267,7 +277,7 @@ jobs: if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: long-tests-1 os: ${{ env.os }} @@ -275,7 +285,7 @@ jobs: if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: long-tests-2 os: ${{ env.os }} @@ -283,7 +293,7 @@ jobs: if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: long-tests-3 os: ${{ env.os }} diff --git a/.github/workflows/windows-vcpkg.yml b/.github/workflows/windows-vcpkg.yml index 59e931e6a8..bcd2ef9e5a 100644 --- a/.github/workflows/windows-vcpkg.yml +++ b/.github/workflows/windows-vcpkg.yml @@ -37,8 +37,7 @@ jobs: # Indicates the location of the vcpkg as a Git submodule of the project repository. VCPKG_ROOT: ${{ github.workspace }}/vcpkg ORTOOLS_DIR: ${{ github.workspace }}/or-tools - os: windows-latest - test-platform: windows-2022 + os: windows-2022 vcpkgPackages: wxwidgets boost-test triplet: x64-windows-release # Caching strategy of VCPKG dependencies @@ -97,7 +96,7 @@ jobs: - name: Download pre-compiled librairies uses: ./.github/workflows/download-extract-precompiled-libraries-zip with: - os: ${{env.os}} + os: windows-latest ortools-url: ${{env.ORTOOLS_URL}} ortools-dir: ${{env.ORTOOLS_DIR}} @@ -136,21 +135,26 @@ jobs: shell: bash run: | cmake --build _build --config Release -j$(nproc) - # simtest + - name: Read simtest version - id: simtest-version - uses: notiz-dev/github-action-json-property@release - with: - path: 'simtest.json' - prop_path: 'version' + shell: bash + run: | + echo 'SIMTEST_JSON<> $GITHUB_ENV + cat ./simtest.json >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + + - name: Export simtest version + shell: bash + run: | + echo "SIMTEST=${{ fromJson(env.SIMTEST_JSON).version }}" >> $GITHUB_ENV - name: Run named mps tests if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-named-mps - os: ${{ env.test-platform }} + os: ${{ env.os }} variant: "named-mps" - name: Run unfeasibility-related tests @@ -176,116 +180,124 @@ jobs: if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: adequacy-patch-CSR - os: ${{ env.test-platform }} + os: ${{ env.os }} - name: Run tests about infinity on BCs RHS if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-v830 - os: ${{ env.test-platform }} + os: ${{ env.os }} - name: Run MILP with CBC if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-milp variant: "milp-cbc" - os: ${{ env.test-platform }} + os: ${{ env.os }} - - name: Run tests introduced in v860 + - name: Run tests introduced in 8.6.0 if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-v860 - os: ${{ env.test-platform }} + os: ${{ env.os }} - - name: Run tests introduced in v870 + - name: Run tests introduced in 8.7.0 if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-v870 - os: ${{ env.test-platform }} + os: ${{ env.os }} - - name: Run tests introduced in v910 + - name: Run tests introduced in 9.1.0 if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-v910 - os: ${{ env.test-platform }} + os: ${{ env.os }} + + - name: Run tests introduced in 9.2.0 + if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} + uses: ./.github/workflows/run-tests + with: + simtest-tag: ${{ env.SIMTEST }} + batch-name: valid-v920 + os: ${{ env.os }} - name: Run short-tests if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: short-tests - os: ${{ env.test-platform }} + os: ${{ env.os }} - name: Run mps tests if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-mps - os: ${{ env.test-platform }} + os: ${{ env.os }} - name: Run parallel tests if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: valid-parallel - os: ${{ env.test-platform }} + os: ${{ env.os }} variant: "parallel" - name: Run tests for time series generator tool if: ${{ env.RUN_SIMPLE_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: ts-generator - os: ${{ env.test-platform }} + os: ${{ env.os }} variant: "tsgenerator" - name: Run medium-tests if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: medium-tests - os: ${{ env.test-platform }} + os: ${{ env.os }} - name: Run long-tests-1 if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: long-tests-1 - os: ${{ env.test-platform }} + os: ${{ env.os }} - name: Run long-tests-2 if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: long-tests-2 - os: ${{ env.test-platform }} + os: ${{ env.os }} - name: Run long-tests-3 if: ${{ env.RUN_EXTENDED_TESTS == 'true' }} uses: ./.github/workflows/run-tests with: - simtest-tag: ${{steps.simtest-version.outputs.prop}} + simtest-tag: ${{ env.SIMTEST }} batch-name: long-tests-3 - os: ${{ env.test-platform }} + os: ${{ env.os }} - name: Solver archive creation shell: bash diff --git a/docs/developer-guide/CHANGELOG.md b/docs/developer-guide/CHANGELOG.md index d6606547da..7309cad241 100644 --- a/docs/developer-guide/CHANGELOG.md +++ b/docs/developer-guide/CHANGELOG.md @@ -83,6 +83,11 @@ toc_depth: 2 * Fix invalid index causing segfault in `test-study` test (#1902) ## Branch 8.8.x (end of support 12/2025) +### 8.8.6 (07/2024) +#### Bugfix +- Fix missing synthesis results for links (#2115) +#### Dependencies +- Update vcpkg (fix Boost) ### 8.8.5 (05/2024) #### Bugfix @@ -318,6 +323,12 @@ toc_depth: 2 * Array, logs jit and correlation in makefile (#1410) ## Branch 8.6.x (end of support 06/2025) +### 8.6.8 (07/2024) +#### Bugfix +- [UI] Remove propery storagecycle for short term storage added when saving a study (#2037) +#### Dependencies +- Update vcpkg (fix Boost) + ### 8.6.7 (05/2024) #### Bugfixes * Fix formula use in output var Profit by plant [ANT-1719] (https://github.com/AntaresSimulatorTeam/Antares_Simulator/pull/2097) diff --git a/docs/developer-guide/continuous-integration.md b/docs/developer-guide/continuous-integration.md index 6414c531a6..0f5a47fff3 100644 --- a/docs/developer-guide/continuous-integration.md +++ b/docs/developer-guide/continuous-integration.md @@ -26,8 +26,9 @@ Here is a description of workflows with their associated status. | `download-extract-precompiled-libraries-zip/action.yml` | Download and extract .zip precompiled libraries from antares-deps repository | | | `install-cmake-328/action.yml` | Installs cmake 3.28 using devtoolset 10 | | | `run-tests/action.yml` | Runs tests on the simulator using reference study batches [here](https://github.com/AntaresSimulatorTeam/SimTest) | | - +| `clang-format.yml` | Check formatting using clang-format 18.1.3 through bash script src/format-code.sh[^2] | | [^1]: all branch names must start with `feature/`, `features/`, `fix/`, `release/`, `doc/`, `issue-`, or `dependabot/`; otherwise the workflows are not run +[^2]: please note that this job must succeed in order to merge PRs [ubuntu_ci_svg]: https://github.com/AntaresSimulatorTeam/Antares_Simulator/workflows/Ubuntu%20CI%20(push%20and/or%20release)/badge.svg [ubuntu_ci_link]: https://github.com/AntaresSimulatorTeam/Antares_Simulator/actions?query=workflow%3A"Ubuntu%20CI%20(push%20and/or%20release)" diff --git a/docs/user-guide/04-migration-guides.md b/docs/user-guide/04-migration-guides.md index c423bc5346..729818c28f 100644 --- a/docs/user-guide/04-migration-guides.md +++ b/docs/user-guide/04-migration-guides.md @@ -19,6 +19,9 @@ with XXX in - number of TS to generate => generaldata.ini/General/nbtimeserieslinks (unsigned int, default value 1) +### Input +#### Short term storage: efficiency for withdrawal +In input/st-storage/area/list.ini add property: `efficiencywithdrawal` [double] in range 0-1 ## v9.1.0 ### Input diff --git a/docs/user-guide/solver/02-inputs.md b/docs/user-guide/solver/02-inputs.md index 287be4b797..8f497e8b34 100644 --- a/docs/user-guide/solver/02-inputs.md +++ b/docs/user-guide/solver/02-inputs.md @@ -286,6 +286,7 @@ The user may pick any area appearing in the area list and is then given access t - Injection (MW): the maximum injection power for the storage - withdrawal refers to the flow from the power system to the storage - Stock (MWh): the capacity of the storage in MWh - Efficiency (%): the energy efficiency of the storage, i.e. the ratio for a given volume between the energy taken from the system to be injected into the storage and the energy returned to the system during its withdrawal. This efficiency factor is applied when injecting energy into the storage. + - Efficiency Withdrawal (%): Same behavior as the previous efficiency, this factor is applied when withdrawing energy from the storage. - Initial level (%): the imposed initial filling rate of the storage at the beginning of each optimisation period. - Initial level optimal: if the parameter is activated, the "Initial level" parameter is ignored and the initial storage level is optimized by Antares for each optimization period to minimize its objective function. _Note: setting this parameter to "True" implies that there is no guarantee that the initial storage level of week N is the same as the final storage level of week N-1. However, the final level of week N is always equal to the initial level of the same week N plus/minus the injections/withdrawals occuring at the last hour of week N._ diff --git a/docs/user-guide/solver/08-command-line.md b/docs/user-guide/solver/08-command-line.md index 0aa433c33c..8ef88dfd23 100644 --- a/docs/user-guide/solver/08-command-line.md +++ b/docs/user-guide/solver/08-command-line.md @@ -41,7 +41,7 @@ hide: | --optimization-range | Force the [simplex optimization range](04-parameters.md#simplex-range) ('day' or 'week') | | --no-constraints | Ignore all binding constraints | | --no-ts-import | Do not import timeseries into the input folder (this option may be useful for running old studies without upgrade) | -| -m, --mps-export | Export anonymous MPS, weekly or daily optimal UC+dispatch linear | +| -m, --mps-export | Export anonymous MPS, weekly or daily optimal UC+dispatch linear (MPS will be named if the problem is infeasible) | | -s, --named-mps-problems | Export named MPS, weekly or daily optimal UC+dispatch linear | | --solver-logs | Print solver logs | | --solver-parameters | Set solver-specific parameters, for instance `--solver-parameters="THREADS 1 PRESOLVE 1"` for XPRESS or `--solver-parameters="parallel/maxnthreads 1, lp/presolving TRUE"` for SCIP. Syntax is solver-dependent, and only supported for SCIP & XPRESS. | @@ -54,4 +54,4 @@ hide: | -p, --pid=VALUE | Specify the file where to write the process ID | | --list-solvers | Display a list of LP solvers available through OR-Tools and exit | | -v, --version | Print the version of the solver and exit | -| -h, --help | Display this help and exit | \ No newline at end of file +| -h, --help | Display this help and exit | diff --git a/simtest.json b/simtest.json index 422fd8e3bd..f5912ade9e 100644 --- a/simtest.json +++ b/simtest.json @@ -1,3 +1,3 @@ { - "version": "v9.2.0a" + "version": "v9.2.0b" } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0148e31899..78adeffde1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,13 +2,13 @@ cmake_minimum_required(VERSION 3.14) # FetchContent_MakeAvailable # Version set(ANTARES_VERSION_HI 9) -set(ANTARES_VERSION_LO 1) +set(ANTARES_VERSION_LO 2) set(ANTARES_VERSION_REVISION 0) # Beta release set(ANTARES_BETA 0) -set(ANTARES_RC 0) +set(ANTARES_RC 1) set(ANTARES_VERSION_YEAR 2024) diff --git a/src/api/API.cpp b/src/api/API.cpp index 38e9778eab..a76eb934ec 100644 --- a/src/api/API.cpp +++ b/src/api/API.cpp @@ -78,7 +78,7 @@ SimulationResults APIInternal::execute() const study_->parameters.resultFormat, study_->folderOutput, ioQueueService, durationCollector); SimulationObserver simulationObserver; // Run the simulation - switch (study_->runtime->mode) + switch (study_->runtime.mode) { case Data::SimulationMode::Economy: case Data::SimulationMode::Expansion: diff --git a/src/libs/antares/study/CMakeLists.txt b/src/libs/antares/study/CMakeLists.txt index 65ad5398a6..e9a05827ac 100644 --- a/src/libs/antares/study/CMakeLists.txt +++ b/src/libs/antares/study/CMakeLists.txt @@ -122,8 +122,6 @@ set(SRC_STUDY_PART_HYDRO include/antares/study/parts/hydro/allocation.h include/antares/study/parts/hydro/allocation.hxx parts/hydro/allocation.cpp - include/antares/study/parts/hydro/finalLevelValidator.h - parts/hydro/finalLevelValidator.cpp include/antares/study/parts/hydro/hydromaxtimeseriesreader.h parts/hydro/hydromaxtimeseriesreader.cpp ) @@ -210,7 +208,6 @@ set(SRC_STUDY include/antares/study/load-options.h load-options.cpp include/antares/study/runtime/runtime.h - include/antares/study/runtime/runtime.hxx runtime/runtime.cpp include/antares/study/runtime.h include/antares/study/study.h diff --git a/src/libs/antares/study/include/antares/study/parts/short-term-storage/properties.h b/src/libs/antares/study/include/antares/study/parts/short-term-storage/properties.h index 11b4808e0d..ac1d37e791 100644 --- a/src/libs/antares/study/include/antares/study/parts/short-term-storage/properties.h +++ b/src/libs/antares/study/include/antares/study/parts/short-term-storage/properties.h @@ -41,12 +41,17 @@ class Properties std::optional withdrawalNominalCapacity; /// Not optional Reservoir capacity in MWh, >= 0 std::optional reservoirCapacity; + /// Initial level, <= 1 double initialLevel = initiallevelDefault; /// Bool to optimise or not initial level bool initialLevelOptim = false; - /// Efficiency factor between 0 and 1 - double efficiencyFactor = 1; + + /// Efficiency factor for injection between 0 and 1 + double injectionEfficiency = 1; + /// Efficiency factor for withdrawal between 0 and 1 + double withdrawalEfficiency = 1; + // Used to sort outputs std::string groupName = "OTHER1"; /// cluster name diff --git a/src/libs/antares/study/include/antares/study/progression/progression.h b/src/libs/antares/study/include/antares/study/progression/progression.h index bcdeca82ac..5e013df957 100644 --- a/src/libs/antares/study/include/antares/study/progression/progression.h +++ b/src/libs/antares/study/include/antares/study/progression/progression.h @@ -76,11 +76,11 @@ class Progression final public: //! The total number of ticks to achieve - int maxTickCount; + unsigned maxTickCount; //! The current number of ticks - std::atomic tickCount; + std::atomic tickCount; //! The last number of ticks, to reduce the log verbosity - int lastTickCount; + unsigned lastTickCount; // Caption to use when displaying logs // Example: 'year: 10000, task: thermal' Yuni::CString<40, false> caption; @@ -104,7 +104,7 @@ class Progression final return *this; } - Task& operator+=(int value) + Task& operator+=(unsigned value) { pPart.tickCount += value; return *this; @@ -138,7 +138,7 @@ class Progression final ** \internal The number of ticks should remain an `int` because ** we can not use unsigned atomic integer */ - void add(uint year, Section section, int nbTicks); + void add(uint year, Section section, unsigned nbTicks); void add(Section section, int nbTicks); diff --git a/src/libs/antares/study/include/antares/study/runtime/runtime.h b/src/libs/antares/study/include/antares/study/runtime/runtime.h index 0ea07ce3bc..d87761674f 100644 --- a/src/libs/antares/study/include/antares/study/runtime/runtime.h +++ b/src/libs/antares/study/include/antares/study/runtime/runtime.h @@ -25,11 +25,13 @@ #include #include -#include "antares/study/study.h" +#include namespace Antares::Data { +class Study; + enum RangeLimitsIndex { rangeBegin = 0, @@ -139,8 +141,12 @@ class StudyRuntimeInfos void checkThermalTSGeneration(Study& study); }; // struct StudyRuntimeInfos -} // namespace Antares::Data +#ifdef NDEBUG +inline void StudyRangeLimits::checkIntegrity() const +{ +} +#endif -#include "runtime.hxx" +} // namespace Antares::Data #endif // __ANTARES_LIBS_STUDY_RUNTIME_RUNTIME_INFOS_H__ diff --git a/src/libs/antares/study/include/antares/study/runtime/runtime.hxx b/src/libs/antares/study/include/antares/study/runtime/runtime.hxx deleted file mode 100644 index 0e04ed3b93..0000000000 --- a/src/libs/antares/study/include/antares/study/runtime/runtime.hxx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2007-2024, RTE (https://www.rte-france.com) - * See AUTHORS.txt - * SPDX-License-Identifier: MPL-2.0 - * This file is part of Antares-Simulator, - * Adequacy and Performance assessment for interconnected energy networks. - * - * Antares_Simulator is free software: you can redistribute it and/or modify - * it under the terms of the Mozilla Public Licence 2.0 as published by - * the Mozilla Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Antares_Simulator is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Mozilla Public Licence 2.0 for more details. - * - * You should have received a copy of the Mozilla Public Licence 2.0 - * along with Antares_Simulator. If not, see . - */ -#ifndef __ANTARES_LIBS_STUDY_RUNTIME_RUNTIME_INFOS_HXX__ -#define __ANTARES_LIBS_STUDY_RUNTIME_RUNTIME_INFOS_HXX__ - -namespace Antares -{ -namespace Data -{ -#ifdef NDEBUG -inline void StudyRangeLimits::checkIntegrity() const -{ -} -#endif - -} // namespace Data -} // namespace Antares - -#endif // __ANTARES_LIBS_STUDY_RUNTIME_RUNTIME_INFOS_HXX__ diff --git a/src/libs/antares/study/include/antares/study/study.h b/src/libs/antares/study/include/antares/study/study.h index 03e2df8bed..65f8f531fd 100644 --- a/src/libs/antares/study/include/antares/study/study.h +++ b/src/libs/antares/study/include/antares/study/study.h @@ -31,6 +31,7 @@ #include #include +#include #include #include "antares/antares/antares.h" #include "antares/study/binding_constraint/BindingConstraintGroupRepository.h" @@ -596,7 +597,7 @@ class Study: public Yuni::NonCopyable, public LayerData ** ** These informations are only needed when a study is processed. */ - StudyRuntimeInfos* runtime = nullptr; + StudyRuntimeInfos runtime; // Antares::Solver::Variable::State* state; @@ -690,7 +691,6 @@ YString StudyCreateOutputPath(SimulationMode mode, int64_t startTime); } // namespace Antares::Data -#include "runtime.h" #include "study.hxx" #endif /* __ANTARES_LIBS_STUDY_STUDY_H__ */ diff --git a/src/libs/antares/study/parts/short-term-storage/properties.cpp b/src/libs/antares/study/parts/short-term-storage/properties.cpp index 546717588e..b4592105fe 100644 --- a/src/libs/antares/study/parts/short-term-storage/properties.cpp +++ b/src/libs/antares/study/parts/short-term-storage/properties.cpp @@ -61,7 +61,12 @@ bool Properties::loadKey(const IniFile::Property* p) if (p->key == "efficiency") { - return p->value.to(this->efficiencyFactor); + return p->value.to(this->injectionEfficiency); + } + + if (p->key == "efficiencywithdrawal") + { + return p->value.to(this->withdrawalEfficiency); } if (p->key == "name") @@ -105,7 +110,8 @@ void Properties::save(IniFile& ini) const s->add("injectionnominalcapacity", this->injectionNominalCapacity); s->add("withdrawalnominalcapacity", this->withdrawalNominalCapacity); - s->add("efficiency", this->efficiencyFactor); + s->add("efficiency", this->injectionEfficiency); + s->add("efficiencyWithdrawal", this->withdrawalEfficiency); s->add("initialleveloptim", this->initialLevelOptim); s->add("enabled", this->enabled); } @@ -157,16 +163,30 @@ bool Properties::validate() return false; } - if (efficiencyFactor < 0) + if (injectionEfficiency < 0) { logs.warning() << "Property efficiency must be >= 0 " << "for short term storage " << name; - efficiencyFactor = 0; + injectionEfficiency = 0; } - if (efficiencyFactor > 1) + if (injectionEfficiency > 1) { logs.warning() << "Property efficiency must be <= 1 " << "for short term storage " << name; - efficiencyFactor = 1; + injectionEfficiency = 1; + } + + if (withdrawalEfficiency < 0) + { + logs.warning() << "Property efficiencyWithdrawal must be >= 0 " << "for short term storage " + << name; + withdrawalEfficiency = 0; + } + + if (withdrawalEfficiency > 1) + { + logs.warning() << "Property efficiencyWithdrawal must be <= 1 " << "for short term storage " + << name; + withdrawalEfficiency = 1; } if (initialLevel < 0) diff --git a/src/libs/antares/study/progression/progression.cpp b/src/libs/antares/study/progression/progression.cpp index a78c86959f..28c1d3288e 100644 --- a/src/libs/antares/study/progression/progression.cpp +++ b/src/libs/antares/study/progression/progression.cpp @@ -42,7 +42,7 @@ Progression::Task::Task(const Antares::Data::Study& study, uint year, Section se assert(&pProgression); } -void Progression::add(uint year, Section section, int nbTicks) +void Progression::add(uint year, Section section, unsigned nbTicks) { // This section is not thread-safe because always called before really launching // the simulation diff --git a/src/libs/antares/study/runtime/runtime.cpp b/src/libs/antares/study/runtime/runtime.cpp index 1bb7e46386..61be13d6cf 100644 --- a/src/libs/antares/study/runtime/runtime.cpp +++ b/src/libs/antares/study/runtime/runtime.cpp @@ -21,6 +21,7 @@ #include "antares/study/runtime/runtime.h" +#include #include #include "antares/antares/fatal-error.h" #include "antares/study/area/scratchpad.h" diff --git a/src/libs/antares/study/study.cpp b/src/libs/antares/study/study.cpp index 5b1dde8c5f..01d91b3abb 100644 --- a/src/libs/antares/study/study.cpp +++ b/src/libs/antares/study/study.cpp @@ -104,8 +104,6 @@ Study::~Study() void Study::clear() { - // Releasing runtime infos - FreeAndNil(runtime); FreeAndNil(scenarioRules); FreeAndNil(uiinfo); @@ -504,9 +502,7 @@ void Study::getNumberOfCores(const bool forceParallel, const uint nbYearsParalle bool Study::initializeRuntimeInfos() { - delete runtime; - runtime = new StudyRuntimeInfos(); - return runtime->loadFromStudy(*this); + return runtime.loadFromStudy(*this); } void Study::performTransformationsBeforeLaunchingSimulation() @@ -1164,25 +1160,24 @@ struct TS final void Study::initializeProgressMeter(bool tsGeneratorOnly) { - uint years = tsGeneratorOnly ? 1 : (runtime->rangeLimits.year[rangeEnd] + 1); - assert(runtime); + uint years = tsGeneratorOnly ? 1 : (runtime.rangeLimits.year[rangeEnd] + 1); - int ticksPerYear = 0; - int ticksPerOutput = 0; + unsigned ticksPerYear = 0; + unsigned ticksPerOutput = 0; if (not tsGeneratorOnly) { // One tick at the begining and 2 at the end of the year // Output - Areas - ticksPerOutput += (int)areas.size(); + ticksPerOutput += areas.size(); // Output - Links - ticksPerOutput += (int)runtime->interconnectionsCount(); + ticksPerOutput += runtime.interconnectionsCount(); // Output - digest ticksPerOutput += 1; ticksPerYear = 1; } - int n; + unsigned n; for (uint y = 0; y != years; ++y) { @@ -1191,7 +1186,7 @@ void Study::initializeProgressMeter(bool tsGeneratorOnly) n = parameters.nbTimeSeriesLoad * areas.size() * 365; if (0 != (timeSeriesLoad & parameters.timeSeriesToArchive)) { - n += (int)areas.size(); + n += areas.size(); } progression.add(y, Solver::Progression::sectTSGLoad, n); } @@ -1200,7 +1195,7 @@ void Study::initializeProgressMeter(bool tsGeneratorOnly) n = parameters.nbTimeSeriesSolar * areas.size() * 365; if (0 != (timeSeriesSolar & parameters.timeSeriesToArchive)) { - n += (int)areas.size(); + n += areas.size(); } progression.add(y, Solver::Progression::sectTSGSolar, n); } @@ -1209,7 +1204,7 @@ void Study::initializeProgressMeter(bool tsGeneratorOnly) n = parameters.nbTimeSeriesWind * areas.size() * 365; if (0 != (timeSeriesWind & parameters.timeSeriesToArchive)) { - n += (int)areas.size(); + n += areas.size(); } progression.add(y, Solver::Progression::sectTSGWind, n); } @@ -1219,17 +1214,17 @@ void Study::initializeProgressMeter(bool tsGeneratorOnly) n = parameters.nbTimeSeriesHydro; if (0 != (timeSeriesHydro & parameters.timeSeriesToArchive)) { - n += (int)areas.size(); + n += areas.size(); } progression.add(y, Solver::Progression::sectTSGHydro, n); } if (TS::IsNeeded(*this, y)) { - n = runtime->thermalPlantTotalCount; + n = runtime.thermalPlantTotalCount; if (0 != (timeSeriesThermal & parameters.timeSeriesToArchive)) { - n += (int)runtime->thermalPlantTotalCount; - n += (int)runtime->thermalPlantTotalCountMustRun; + n += runtime.thermalPlantTotalCount; + n += runtime.thermalPlantTotalCountMustRun; } progression.add(y, Solver::Progression::sectTSGThermal, n); } @@ -1249,23 +1244,23 @@ void Study::initializeProgressMeter(bool tsGeneratorOnly) n = 0; if (0 != (timeSeriesLoad & parameters.exportTimeSeriesInInput)) { - n += (int)areas.size(); + n += areas.size(); } if (0 != (timeSeriesSolar & parameters.exportTimeSeriesInInput)) { - n += (int)areas.size(); + n += areas.size(); } if (0 != (timeSeriesWind & parameters.exportTimeSeriesInInput)) { - n += (int)areas.size(); + n += areas.size(); } if (0 != (timeSeriesHydro & parameters.exportTimeSeriesInInput)) { - n += (int)areas.size(); + n += areas.size(); } if (0 != (timeSeriesThermal & parameters.exportTimeSeriesInInput)) { - n += (int)areas.size(); + n += areas.size(); } if (n) { diff --git a/src/libs/antares/study/study.importprepro.cpp b/src/libs/antares/study/study.importprepro.cpp index f7c8082050..69a0fe058b 100644 --- a/src/libs/antares/study/study.importprepro.cpp +++ b/src/libs/antares/study/study.importprepro.cpp @@ -33,7 +33,7 @@ bool Study::importTimeseriesIntoInput() { // Special case: some thermal clusters may force TS generation const bool importThermal = parameters.haveToImport(timeSeriesThermal) - && runtime->thermalTSRefresh; + && runtime.thermalTSRefresh; // Something to import ? if ((parameters.exportTimeSeriesInInput && parameters.timeSeriesToGenerate) || importThermal) { diff --git a/src/libs/antares/study/version.cpp b/src/libs/antares/study/version.cpp index 5459ed5d63..8fad2748fd 100644 --- a/src/libs/antares/study/version.cpp +++ b/src/libs/antares/study/version.cpp @@ -43,7 +43,8 @@ constexpr auto supportedVersions = std::to_array({ StudyVersion(8, 7), StudyVersion(8, 8), StudyVersion(9, 0), - StudyVersion(9, 1) + StudyVersion(9, 1), + StudyVersion(9, 2) // Add new versions here }); diff --git a/src/libs/antares/utils/include/antares/utils/utils.h b/src/libs/antares/utils/include/antares/utils/utils.h index 1043e7ca4e..a2f50a2e54 100644 --- a/src/libs/antares/utils/include/antares/utils/utils.h +++ b/src/libs/antares/utils/include/antares/utils/utils.h @@ -36,9 +36,10 @@ namespace Antares */ template void TransformNameIntoID(const AnyString& name, StringT& out); - std::string transformNameIntoID(const std::string& name); +std::string FormattedTime(const std::string& format); + /*! ** \brief Beautify a name, for renaming an area for example */ @@ -51,11 +52,8 @@ std::vector> splitStringIntoPairs(const std: namespace Utils { - bool isZero(double d); - double round(double d, unsigned precision); - } // namespace Utils } // namespace Antares diff --git a/src/libs/antares/utils/utils.cpp b/src/libs/antares/utils/utils.cpp index 31992d6020..e188b2b13a 100644 --- a/src/libs/antares/utils/utils.cpp +++ b/src/libs/antares/utils/utils.cpp @@ -96,6 +96,18 @@ void BeautifyName(std::string& out, const std::string& oldname) out = yuniOut.c_str(); } +std::string FormattedTime(const std::string& format) +{ + using namespace std::chrono; + auto time = system_clock::to_time_t(system_clock::now()); + std::tm local_time = *std::localtime(&time); + + char time_buffer[256]; + std::strftime(time_buffer, sizeof(time_buffer), format.c_str(), &local_time); + + return std::string(time_buffer); +} + std::vector> splitStringIntoPairs(const std::string& s, char delimiter1, char delimiter2) diff --git a/src/solver/application/ScenarioBuilderOwner.cpp b/src/solver/application/ScenarioBuilderOwner.cpp index 69f9d11799..f2f0c90c7e 100644 --- a/src/solver/application/ScenarioBuilderOwner.cpp +++ b/src/solver/application/ScenarioBuilderOwner.cpp @@ -39,7 +39,7 @@ void Antares::Solver::ScenarioBuilderOwner::callScenarioBuilder() // We will resize all matrix related to the time-series numbers // This operation can be done once since the number of years is constant // for a single simulation - study_.resizeAllTimeseriesNumbers(1 + study_.runtime->rangeLimits.year[Data::rangeEnd]); + study_.resizeAllTimeseriesNumbers(1 + study_.runtime.rangeLimits.year[Data::rangeEnd]); if (not TimeSeriesNumbers::CheckNumberOfColumns(study_.areas)) { throw FatalError( diff --git a/src/solver/application/application.cpp b/src/solver/application/application.cpp index a55873ea09..334e8f86d7 100644 --- a/src/solver/application/application.cpp +++ b/src/solver/application/application.cpp @@ -224,7 +224,6 @@ void Application::readDataForTheStudy(Data::StudyLoadOptions& options) writeComment(study); } - // Runtime data dedicated for the solver if (!study.initializeRuntimeInfos()) { throw Error::RuntimeInfoInitialization(); @@ -391,7 +390,7 @@ void Application::execute() pStudy->computePThetaInfForThermalClusters(); // Run the simulation - switch (pStudy->runtime->mode) + switch (pStudy->runtime.mode) { case Data::SimulationMode::Economy: case Data::SimulationMode::Expansion: @@ -435,20 +434,6 @@ void Application::runSimulationInAdequacyMode() observer); } -static std::string timeToString() -{ - using namespace std::chrono; - auto time = system_clock::to_time_t(system_clock::now()); - std::tm local_time = *std::localtime(&time); - - char time_buffer[256]; - std::strftime(time_buffer, sizeof(time_buffer), "%Y%m%d-%H%M%S", &local_time); - - std::string currentTime = time_buffer; - - return currentTime; -} - void Application::resetLogFilename() const { fs::path logfile = fs::path(pSettings.studyFolder.c_str()) / "logs"; @@ -459,8 +444,9 @@ void Application::resetLogFilename() const + ". Aborting now."); } - logfile /= "solver-"; // append the filename - logfile += timeToString() + ".log"; // complete filename with timestamp and extension + logfile /= "solver-"; // append the filename + logfile += FormattedTime("%Y%m%d-%H%M%S") + + ".log"; // complete filename with timestamp and extension // Assigning the log filename logs.logfile(logfile.string()); diff --git a/src/solver/hydro/CMakeLists.txt b/src/solver/hydro/CMakeLists.txt index 657c52e17a..3ba25e80d4 100644 --- a/src/solver/hydro/CMakeLists.txt +++ b/src/solver/hydro/CMakeLists.txt @@ -58,8 +58,10 @@ set(SRC_MANAGEMENT management/MinGenerationScaling.cpp include/antares/solver/hydro/management/HydroInputsChecker.h management/HydroInputsChecker.cpp - include/antares/solver/hydro/management/hydro-final-reservoir-level-functions.h - management/hydro-final-reservoir-level-functions.cpp + include/antares/solver/hydro/management/HydroErrorsCollector.h + management/HydroErrorsCollector.cpp + include/antares/solver/hydro/management/finalLevelValidator.h + management/finalLevelValidator.cpp ) diff --git a/src/solver/hydro/include/antares/solver/hydro/management/HydroErrorsCollector.h b/src/solver/hydro/include/antares/solver/hydro/management/HydroErrorsCollector.h new file mode 100644 index 0000000000..d6054f12b6 --- /dev/null +++ b/src/solver/hydro/include/antares/solver/hydro/management/HydroErrorsCollector.h @@ -0,0 +1,63 @@ +/* +** Copyright 2007-2024, RTE (https://www.rte-france.com) +** See AUTHORS.txt +** SPDX-License-Identifier: MPL-2.0 +** This file is part of Antares-Simulator, +** Adequacy and Performance assessment for interconnected energy networks. +** +** Antares_Simulator is free software: you can redistribute it and/or modify +** it under the terms of the Mozilla Public Licence 2.0 as published by +** the Mozilla Foundation, either version 2 of the License, or +** (at your option) any later version. +** +** Antares_Simulator is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** Mozilla Public Licence 2.0 for more details. +** +** You should have received a copy of the Mozilla Public Licence 2.0 +** along with Antares_Simulator. If not, see . +*/ + +#pragma once +#include +#include +#include +#include + +namespace Antares +{ + +class HydroErrorsCollector +{ +public: + class AreaReference + { + public: + AreaReference(HydroErrorsCollector* collector, const std::string& name); + template + AreaReference& operator<<(const T& msg); + + private: + std::string& areaSingleErrorMessage_; + }; + + AreaReference operator()(const std::string& name); + HydroErrorsCollector() = default; + void CheckForErrors() const; + +private: + std::map> areasErrorMap_; + std::string& CurrentMessage(const std::string& name); +}; + +template +HydroErrorsCollector::AreaReference& HydroErrorsCollector::AreaReference::operator<<(const T& msg) +{ + std::ostringstream strfy; + strfy << msg; + areaSingleErrorMessage_ += strfy.str(); + return *this; +} + +} // namespace Antares diff --git a/src/solver/hydro/include/antares/solver/hydro/management/HydroInputsChecker.h b/src/solver/hydro/include/antares/solver/hydro/management/HydroInputsChecker.h index 0308779170..af9049a97e 100644 --- a/src/solver/hydro/include/antares/solver/hydro/management/HydroInputsChecker.h +++ b/src/solver/hydro/include/antares/solver/hydro/management/HydroInputsChecker.h @@ -21,6 +21,7 @@ #pragma once #include #include "antares/date/date.h" +#include "antares/solver/hydro/management/HydroErrorsCollector.h" #include "antares/solver/hydro/management/MinGenerationScaling.h" #include "antares/solver/hydro/management/PrepareInflows.h" #include "antares/study/study.h" @@ -33,31 +34,31 @@ class HydroInputsChecker public: explicit HydroInputsChecker(Antares::Data::Study& study); void Execute(uint year); + void CheckForErrors() const; + void CheckFinalReservoirLevelsConfiguration(uint year); private: Data::AreaList& areas_; const Data::Parameters& parameters_; const Date::Calendar& calendar_; - Data::SimulationMode simulationMode_; - const uint firstYear_; - const uint endYear_; PrepareInflows prepareInflows_; MinGenerationScaling minGenerationScaling_; const Data::TimeSeries::TS& scenarioInitialHydroLevels_; const Data::TimeSeries::TS& scenarioFinalHydroLevels_; + HydroErrorsCollector errorCollector_; //! return false if checkGenerationPowerConsistency or checkMinGeneration returns false - bool checkMonthlyMinGeneration(uint year, const Data::Area& area) const; + bool checkMonthlyMinGeneration(uint year, const Data::Area& area); //! check Yearly minimum generation is lower than available inflows - bool checkYearlyMinGeneration(uint year, const Data::Area& area) const; + bool checkYearlyMinGeneration(uint year, const Data::Area& area); //! check Weekly minimum generation is lower than available inflows - bool checkWeeklyMinGeneration(uint year, const Data::Area& area) const; + bool checkWeeklyMinGeneration(uint year, const Data::Area& area); //! check Hourly minimum generation is lower than available inflows - bool checkGenerationPowerConsistency(uint year) const; + bool checkGenerationPowerConsistency(uint year); //! return false if checkGenerationPowerConsistency or checkMinGeneration returns false - bool checksOnGenerationPowerBounds(uint year) const; + bool checksOnGenerationPowerBounds(uint year); //! check minimum generation is lower than available inflows - bool checkMinGeneration(uint year) const; + bool checkMinGeneration(uint year); }; } // namespace Antares diff --git a/src/libs/antares/study/include/antares/study/parts/hydro/finalLevelValidator.h b/src/solver/hydro/include/antares/solver/hydro/management/finalLevelValidator.h similarity index 81% rename from src/libs/antares/study/include/antares/study/parts/hydro/finalLevelValidator.h rename to src/solver/hydro/include/antares/solver/hydro/management/finalLevelValidator.h index 518e29d40b..ecbcd8ed31 100644 --- a/src/libs/antares/study/include/antares/study/parts/hydro/finalLevelValidator.h +++ b/src/solver/hydro/include/antares/solver/hydro/management/finalLevelValidator.h @@ -26,23 +26,30 @@ */ #pragma once +#include "antares/solver/hydro/management/HydroErrorsCollector.h" #include "antares/study/parts/hydro/container.h" -namespace Antares::Data +namespace Antares +{ +namespace Data { class PartHydro; +} +namespace Solver +{ class FinalLevelValidator { public: - FinalLevelValidator(PartHydro& hydro, + FinalLevelValidator(Antares::Data::PartHydro& hydro, unsigned int areaIndex, - const AreaName areaName, + const Antares::Data::AreaName areaName, double initialLevel, double finalLevel, const unsigned int year, const unsigned int lastSimulationDay, - const unsigned int firstMonthOfSimulation); + const unsigned int firstMonthOfSimulation, + HydroErrorsCollector& errorCollector); bool check(); bool finalLevelFineForUse(); @@ -62,12 +69,16 @@ class FinalLevelValidator unsigned int firstMonthOfSimulation_ = 0; // Data from area - PartHydro& hydro_; + Antares::Data::PartHydro& hydro_; unsigned int areaIndex_; - const AreaName areaName_; + const Antares::Data::AreaName areaName_; double initialLevel_; double finalLevel_; bool finalLevelFineForUse_ = false; + + // area input errors + HydroErrorsCollector& errorCollector_; }; -} // namespace Antares::Data +} // namespace Solver +} // namespace Antares diff --git a/src/solver/hydro/include/antares/solver/hydro/management/hydro-final-reservoir-level-functions.h b/src/solver/hydro/include/antares/solver/hydro/management/hydro-final-reservoir-level-functions.h deleted file mode 100644 index 9ded556947..0000000000 --- a/src/solver/hydro/include/antares/solver/hydro/management/hydro-final-reservoir-level-functions.h +++ /dev/null @@ -1,41 +0,0 @@ -/* -** Copyright 2007-2023 RTE -** Authors: RTE-international / Redstork / Antares_Simulator Team -** -** This file is part of Antares_Simulator. -** -** Antares_Simulator is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** There are special exceptions to the terms and conditions of the -** license as they are applied to this software. View the full text of -** the exceptions in file COPYING.txt in the directory of this software -** distribution -** -** Antares_Simulator is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with Antares_Simulator. If not, see . -** -** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions -*/ -#ifndef __SOLVER_SIMULATION_HYDRO_FINAL_RESERVOIR_PRE_CHECKS_H__ -#define __SOLVER_SIMULATION_HYDRO_FINAL_RESERVOIR_PRE_CHECKS_H__ - -#include "antares/study/study.h" - -namespace Antares::Solver -{ -void CheckFinalReservoirLevelsConfiguration(Data::AreaList& areas, - const Data::Parameters& parameters, - const Data::TimeSeries::TS& scenarioInitialHydroLevels, - const Data::TimeSeries::TS& scenarioFinalHydroLevels, - uint year); -} // namespace Antares::Solver - -#endif // __SOLVER_SIMULATION_HYDRO_FINAL_RESERVOIR_PRE_CHECKS_H__ diff --git a/src/solver/hydro/management/HydroErrorsCollector.cpp b/src/solver/hydro/management/HydroErrorsCollector.cpp new file mode 100644 index 0000000000..729726e14e --- /dev/null +++ b/src/solver/hydro/management/HydroErrorsCollector.cpp @@ -0,0 +1,45 @@ +#include "antares/solver/hydro/management/HydroErrorsCollector.h" + +#include +#include + +#include + +#include "antares/antares/fatal-error.h" + +namespace Antares +{ + +void HydroErrorsCollector::CheckForErrors() const +{ + if (!areasErrorMap_.empty()) + { + for (const auto& [key, values]: areasErrorMap_) + { + for (const auto& value: values | std::views::take(10)) + { + logs.error() << "In Area " << key << ": " << value << " "; + } + } + + throw FatalError("Hydro validation has failed !"); + } +} + +HydroErrorsCollector::AreaReference::AreaReference(HydroErrorsCollector* collector, + const std::string& name): + areaSingleErrorMessage_(collector->CurrentMessage(name)) +{ +} + +HydroErrorsCollector::AreaReference HydroErrorsCollector::operator()(const std::string& name) +{ + return AreaReference(this, name); +} + +std::string& HydroErrorsCollector::CurrentMessage(const std::string& name) +{ + auto& msgs = areasErrorMap_[name]; + return *msgs.insert(msgs.end(), ""); +} +} // namespace Antares diff --git a/src/solver/hydro/management/HydroInputsChecker.cpp b/src/solver/hydro/management/HydroInputsChecker.cpp index c249cdabe3..d9956e94a2 100644 --- a/src/solver/hydro/management/HydroInputsChecker.cpp +++ b/src/solver/hydro/management/HydroInputsChecker.cpp @@ -23,8 +23,7 @@ #include #include -#include "antares/antares/fatal-error.h" -#include "antares/solver/hydro/management/hydro-final-reservoir-level-functions.h" +#include "antares/solver/hydro/management/finalLevelValidator.h" #include "antares/solver/hydro/monthly/h2o_m_donnees_annuelles.h" #include "antares/solver/hydro/monthly/h2o_m_fonctions.h" #include "antares/solver/simulation/common-eco-adq.h" @@ -36,9 +35,6 @@ HydroInputsChecker::HydroInputsChecker(Antares::Data::Study& study): areas_(study.areas), parameters_(study.parameters), calendar_(study.calendar), - simulationMode_(study.runtime->mode), - firstYear_(0), - endYear_(1 + study.runtime->rangeLimits.year[Data::rangeEnd]), prepareInflows_(study.areas, study.calendar), minGenerationScaling_(study.areas, study.calendar), scenarioInitialHydroLevels_(study.scenarioInitialHydroLevels), @@ -50,27 +46,22 @@ void HydroInputsChecker::Execute(uint year) { prepareInflows_.Run(year); minGenerationScaling_.Run(year); - if (!checksOnGenerationPowerBounds(year)) { - throw FatalError("hydro inputs checks: invalid minimum generation"); + logs.error() << "hydro inputs checks: invalid minimum generation in year " << year; } if (parameters_.useCustomScenario) { - CheckFinalReservoirLevelsConfiguration(areas_, - parameters_, - scenarioInitialHydroLevels_, - scenarioFinalHydroLevels_, - year); + CheckFinalReservoirLevelsConfiguration(year); } } -bool HydroInputsChecker::checksOnGenerationPowerBounds(uint year) const +bool HydroInputsChecker::checksOnGenerationPowerBounds(uint year) { return checkMinGeneration(year) && checkGenerationPowerConsistency(year); } -bool HydroInputsChecker::checkMinGeneration(uint year) const +bool HydroInputsChecker::checkMinGeneration(uint year) { bool ret = true; areas_.each( @@ -103,11 +94,12 @@ bool HydroInputsChecker::checkMinGeneration(uint year) const return ret; } -bool HydroInputsChecker::checkWeeklyMinGeneration(uint year, const Data::Area& area) const +bool HydroInputsChecker::checkWeeklyMinGeneration(uint year, const Data::Area& area) { const auto& srcinflows = area.hydro.series->storage.getColumn(year); const auto& srcmingen = area.hydro.series->mingen.getColumn(year); // Weekly minimum generation <= Weekly inflows for each week + bool ret = true; for (uint week = 0; week < calendar_.maxWeeksInYear - 1; ++week) { double totalWeekMingen = 0.0; @@ -127,57 +119,60 @@ bool HydroInputsChecker::checkWeeklyMinGeneration(uint year, const Data::Area& a } if (totalWeekMingen > totalWeekInflows) { - logs.error() << "In Area " << area.name << " the minimum generation of " - << totalWeekMingen << " MW in week " << week + 1 << " of TS-" - << area.hydro.series->mingen.getSeriesIndex(year) + 1 - << " is incompatible with the inflows of " << totalWeekInflows << " MW."; - return false; + errorCollector_(area.name) + << " the minimum generation of " << totalWeekMingen << " MW in week " << week + 1 + << " of TS-" << area.hydro.series->mingen.getSeriesIndex(year) + 1 + << " is incompatible with the inflows of " << totalWeekInflows << " MW."; + ret = false; } } - return true; + return ret; } -bool HydroInputsChecker::checkYearlyMinGeneration(uint year, const Data::Area& area) const +bool HydroInputsChecker::checkYearlyMinGeneration(uint year, const Data::Area& area) { const auto& data = area.hydro.managementData.at(year); + bool ret = true; if (data.totalYearMingen > data.totalYearInflows) { // Yearly minimum generation <= Yearly inflows - logs.error() << "In Area " << area.name << " the minimum generation of " - << data.totalYearMingen << " MW of TS-" - << area.hydro.series->mingen.getSeriesIndex(year) + 1 - << " is incompatible with the inflows of " << data.totalYearInflows << " MW."; - return false; + errorCollector_(area.name) + << " the minimum generation of " << data.totalYearMingen << " MW of TS-" + << area.hydro.series->mingen.getSeriesIndex(year) + 1 + << " is incompatible with the inflows of " << data.totalYearInflows << " MW."; + ret = false; } - return true; + return ret; } -bool HydroInputsChecker::checkMonthlyMinGeneration(uint year, const Data::Area& area) const +bool HydroInputsChecker::checkMonthlyMinGeneration(uint year, const Data::Area& area) { const auto& data = area.hydro.managementData.at(year); + bool ret = true; for (uint month = 0; month != 12; ++month) { uint realmonth = calendar_.months[month].realmonth; // Monthly minimum generation <= Monthly inflows for each month if (data.totalMonthMingen[realmonth] > data.totalMonthInflows[realmonth]) { - logs.error() << "In Area " << area.name << " the minimum generation of " - << data.totalMonthMingen[realmonth] << " MW in month " << month + 1 - << " of TS-" << area.hydro.series->mingen.getSeriesIndex(year) + 1 - << " is incompatible with the inflows of " - << data.totalMonthInflows[realmonth] << " MW."; - return false; + errorCollector_(area.name) + << " the minimum generation of " << data.totalMonthMingen[realmonth] + << " MW in month " << month + 1 << " of TS-" + << area.hydro.series->mingen.getSeriesIndex(year) + 1 + << " is incompatible with the inflows of " << data.totalMonthInflows[realmonth] + << " MW."; + ret = false; } } - return true; + return ret; } -bool HydroInputsChecker::checkGenerationPowerConsistency(uint year) const +bool HydroInputsChecker::checkGenerationPowerConsistency(uint year) { bool ret = true; areas_.each( - [&ret, &year](const Data::Area& area) + [this, &ret, &year](const Data::Area& area) { const auto& srcmingen = area.hydro.series->mingen.getColumn(year); const auto& srcmaxgen = area.hydro.series->maxHourlyGenPower.getColumn(year); @@ -192,11 +187,11 @@ bool HydroInputsChecker::checkGenerationPowerConsistency(uint year) const if (max < min) { - logs.error() << "In area: " << area.name << " [hourly] minimum generation of " - << min << " MW in timestep " << h + 1 << " of TS-" << tsIndexMin + 1 - << " is incompatible with the maximum generation of " << max - << " MW in timestep " << h + 1 << " of TS-" << tsIndexMax + 1 - << " MW."; + errorCollector_(area.name) + << "In area: " << area.name << " [hourly] minimum generation of " << min + << " MW in timestep " << h + 1 << " of TS-" << tsIndexMin + 1 + << " is incompatible with the maximum generation of " << max + << " MW in timestep " << h + 1 << " of TS-" << tsIndexMax + 1 << " MW."; ret = false; return; } @@ -205,4 +200,45 @@ bool HydroInputsChecker::checkGenerationPowerConsistency(uint year) const return ret; } + +void HydroInputsChecker::CheckFinalReservoirLevelsConfiguration(uint year) +{ + if (!parameters_.yearsFilter.at(year)) + { + return; + } + + areas_.each( + [this, year](Data::Area& area) + { + double initialLevel = scenarioInitialHydroLevels_.entry[area.index][year]; + double finalLevel = scenarioFinalHydroLevels_.entry[area.index][year]; + + Antares::Solver::FinalLevelValidator validator(area.hydro, + area.index, + area.name, + initialLevel, + finalLevel, + year, + parameters_.simulationDays.end, + parameters_.firstMonthInYear, + errorCollector_); + if (!validator.check()) + { + errorCollector_(area.name) + << "hydro final level : infeasibility for area " << area.name + << " please check the corresponding final level data (scenario-builder)"; + } + if (validator.finalLevelFineForUse()) + { + area.hydro.deltaBetweenFinalAndInitialLevels[year] = finalLevel - initialLevel; + } + }); +} // End function CheckFinalReservoirLevelsConfiguration + +void HydroInputsChecker::CheckForErrors() const +{ + errorCollector_.CheckForErrors(); +} + } // namespace Antares diff --git a/src/libs/antares/study/parts/hydro/finalLevelValidator.cpp b/src/solver/hydro/management/finalLevelValidator.cpp similarity index 72% rename from src/libs/antares/study/parts/hydro/finalLevelValidator.cpp rename to src/solver/hydro/management/finalLevelValidator.cpp index c0834441a1..343a086416 100644 --- a/src/libs/antares/study/parts/hydro/finalLevelValidator.cpp +++ b/src/solver/hydro/management/finalLevelValidator.cpp @@ -25,19 +25,21 @@ ** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions */ -#include "antares/study/parts/hydro/finalLevelValidator.h" +#include "antares/solver/hydro/management/finalLevelValidator.h" -namespace Antares::Data +namespace Antares::Solver { -FinalLevelValidator::FinalLevelValidator(PartHydro& hydro, - unsigned int areaIndex, - const AreaName areaName, // gp : to std::string - double initialLevel, - double finalLevel, - const unsigned int year, - const unsigned int lastSimulationDay, - const unsigned int firstMonthOfSimulation): +FinalLevelValidator::FinalLevelValidator( + Antares::Data::PartHydro& hydro, + unsigned int areaIndex, + const Antares::Data::AreaName areaName, // gp : to std::string + double initialLevel, + double finalLevel, + const unsigned int year, + const unsigned int lastSimulationDay, + const unsigned int firstMonthOfSimulation, + HydroErrorsCollector& errorCollector): year_(year), lastSimulationDay_(lastSimulationDay), firstMonthOfSimulation_(firstMonthOfSimulation), @@ -45,7 +47,8 @@ FinalLevelValidator::FinalLevelValidator(PartHydro& hydro, areaIndex_(areaIndex), areaName_(areaName), initialLevel_(initialLevel), - finalLevel_(finalLevel) + finalLevel_(finalLevel), + errorCollector_(errorCollector) { } @@ -112,9 +115,10 @@ bool FinalLevelValidator::hydroAllocationStartMatchesSimulation() const return true; } - logs.error() << "Year " << year_ + 1 << ", area '" << areaName_ - << "' : " << "Hydro allocation must start on the 1st simulation month and " - << "simulation last a whole year"; + errorCollector_(areaName_) << "Year " << year_ + 1 << ": " + << "Hydro allocation must start on the 1st simulation month and " + << "simulation last a whole year"; + return false; } @@ -125,10 +129,10 @@ bool FinalLevelValidator::isFinalLevelReachable() const if ((finalLevel_ - initialLevel_) * reservoirCapacity > totalYearInflows) { - logs.error() << "Year: " << year_ + 1 << ". Area: " << areaName_ - << ". Incompatible total inflows: " << totalYearInflows - << " with initial: " << initialLevel_ << " and final: " << finalLevel_ - << " reservoir levels."; + errorCollector_(areaName_) + << "Year: " << year_ + 1 << " Incompatible total inflows: " << totalYearInflows + << " with initial: " << initialLevel_ << " and final: " << finalLevel_ + << " reservoir levels."; return false; } return true; @@ -154,10 +158,10 @@ bool FinalLevelValidator::isBetweenRuleCurves() const if (finalLevel_ < lowLevelLastDay || finalLevel_ > highLevelLastDay) { - logs.error() << "Year: " << year_ + 1 << ". Area: " << areaName_ - << ". Specifed final reservoir level: " << finalLevel_ - << " is incompatible with reservoir level rule curve [" << lowLevelLastDay - << " , " << highLevelLastDay << "]"; + errorCollector_(areaName_) + << "Year: " << year_ + 1 << " Specifed final reservoir level: " << finalLevel_ + << " is incompatible with reservoir level rule curve [" << lowLevelLastDay << " , " + << highLevelLastDay << "]"; return false; } return true; @@ -168,4 +172,4 @@ bool FinalLevelValidator::finalLevelFineForUse() return finalLevelFineForUse_; } -} // namespace Antares::Data +} // namespace Antares::Solver diff --git a/src/solver/hydro/management/hydro-final-reservoir-level-functions.cpp b/src/solver/hydro/management/hydro-final-reservoir-level-functions.cpp deleted file mode 100644 index 6e9a53ce95..0000000000 --- a/src/solver/hydro/management/hydro-final-reservoir-level-functions.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* -** Copyright 2007-2023 RTE -** Authors: RTE-international / Redstork / Antares_Simulator Team -** -** This file is part of Antares_Simulator. -** -** Antares_Simulator is free software: you can redistribute it and/or modify -** it under the terms of the GNU General Public License as published by -** the Free Software Foundation, either version 3 of the License, or -** (at your option) any later version. -** -** There are special exceptions to the terms and conditions of the -** license as they are applied to this software. View the full text of -** the exceptions in file COPYING.txt in the directory of this software -** distribution -** -** Antares_Simulator is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with Antares_Simulator. If not, see . -** -** SPDX-License-Identifier: licenceRef-GPL3_WITH_RTE-Exceptions -*/ - -#include "antares/solver/hydro/management/hydro-final-reservoir-level-functions.h" - -#include -#include "antares/study/parts/hydro/finalLevelValidator.h" - -namespace Antares::Solver -{ - -void CheckFinalReservoirLevelsConfiguration(Data::AreaList& areas, - const Data::Parameters& parameters, - const Data::TimeSeries::TS& scenarioInitialHydroLevels, - const Data::TimeSeries::TS& scenarioFinalHydroLevels, - uint year) -{ - if (!parameters.yearsFilter.at(year)) - { - return; - } - - areas.each( - [&areas, ¶meters, &scenarioInitialHydroLevels, &scenarioFinalHydroLevels, year]( - Data::Area& area) - { - double initialLevel = scenarioInitialHydroLevels.entry[area.index][year]; - double finalLevel = scenarioFinalHydroLevels.entry[area.index][year]; - - Data::FinalLevelValidator validator(area.hydro, - area.index, - area.name, - initialLevel, - finalLevel, - year, - parameters.simulationDays.end, - parameters.firstMonthInYear); - if (!validator.check()) - { - throw FatalError("hydro final level : infeasibility"); - } - if (validator.finalLevelFineForUse()) - { - area.hydro.deltaBetweenFinalAndInitialLevels[year] = finalLevel - initialLevel; - } - }); -} // End function CheckFinalReservoirLevelsConfiguration - -} // namespace Antares::Solver diff --git a/src/solver/infeasible-problem-analysis/CMakeLists.txt b/src/solver/infeasible-problem-analysis/CMakeLists.txt index 720842fd6a..72b93ad153 100644 --- a/src/solver/infeasible-problem-analysis/CMakeLists.txt +++ b/src/solver/infeasible-problem-analysis/CMakeLists.txt @@ -10,14 +10,15 @@ set(SRC_INFEASIBLE_PROBLEM_ANALYSIS include/antares/solver/infeasible-problem-analysis/unfeasible-pb-analyzer.h include/antares/solver/infeasible-problem-analysis/report.h report.cpp - include/antares/solver/infeasible-problem-analysis/constraint.h - constraint.cpp + include/antares/solver/infeasible-problem-analysis/watched-constraints.h + watched-constraints.cpp ) add_library(infeasible_problem_analysis ${SRC_INFEASIBLE_PROBLEM_ANALYSIS}) target_link_libraries(infeasible_problem_analysis PRIVATE ortools::ortools + Boost::headers Antares::logs ) target_include_directories(infeasible_problem_analysis diff --git a/src/solver/infeasible-problem-analysis/constraint-slack-analysis.cpp b/src/solver/infeasible-problem-analysis/constraint-slack-analysis.cpp index 785d4670f8..099ac70db6 100644 --- a/src/solver/infeasible-problem-analysis/constraint-slack-analysis.cpp +++ b/src/solver/infeasible-problem-analysis/constraint-slack-analysis.cpp @@ -31,12 +31,23 @@ using namespace operations_research; +namespace +{ +bool compareSlackSolutions(const MPVariable* a, const MPVariable* b) +{ + return a->solution_value() > b->solution_value(); +} + +constexpr unsigned int nbMaxSlackVarsToKeep = 10; +} // namespace + namespace Antares::Optimization { void ConstraintSlackAnalysis::run(MPSolver* problem) { - addSlackVariables(problem); + selectConstraintsToWatch(problem); + addSlackVariablesToConstraints(problem); if (slackVariables_.empty()) { logs.error() << title() << " : no constraints have been selected"; @@ -53,9 +64,21 @@ void ConstraintSlackAnalysis::run(MPSolver* problem) } hasDetectedInfeasibilityCause_ = true; + + sortSlackVariablesByValue(); + trimSlackVariables(); +} + +void ConstraintSlackAnalysis::selectConstraintsToWatch(MPSolver* problem) +{ + ConstraintsFactory factory; + std::regex rgx = factory.constraintsFilter(); + std::ranges::copy_if(problem->constraints(), + std::back_inserter(constraintsToWatch_), + [&rgx](auto* c) { return std::regex_search(c->name(), rgx); }); } -void ConstraintSlackAnalysis::addSlackVariables(MPSolver* problem) +void ConstraintSlackAnalysis::addSlackVariablesToConstraints(MPSolver* problem) { /* Optimization: We assess that less than 1 every 3 constraint will match @@ -64,29 +87,21 @@ void ConstraintSlackAnalysis::addSlackVariables(MPSolver* problem) */ const unsigned int selectedConstraintsInverseRatio = 3; slackVariables_.reserve(problem->NumConstraints() / selectedConstraintsInverseRatio); - std::regex rgx(constraint_name_pattern); const double infinity = MPSolver::infinity(); - for (MPConstraint* constraint: problem->constraints()) + for (MPConstraint* c: constraintsToWatch_) { - if (std::regex_search(constraint->name(), rgx)) + if (c->lb() > -infinity) { - if (constraint->lb() != -infinity) - { - const MPVariable* slack = problem->MakeNumVar(0, - infinity, - constraint->name() + "::low"); - constraint->SetCoefficient(slack, 1.); - slackVariables_.push_back(slack); - } - - if (constraint->ub() != infinity) - { - const MPVariable* slack = problem->MakeNumVar(0, - infinity, - constraint->name() + "::up"); - constraint->SetCoefficient(slack, -1.); - slackVariables_.push_back(slack); - } + const MPVariable* slack = problem->MakeNumVar(0, infinity, c->name() + "::low"); + c->SetCoefficient(slack, 1.); + slackVariables_.push_back(slack); + } + + if (c->ub() < infinity) + { + const MPVariable* slack = problem->MakeNumVar(0, infinity, c->name() + "::up"); + c->SetCoefficient(slack, -1.); + slackVariables_.push_back(slack); } } } @@ -104,10 +119,22 @@ void ConstraintSlackAnalysis::buildObjective(MPSolver* problem) const objective->SetMinimization(); } +void ConstraintSlackAnalysis::sortSlackVariablesByValue() +{ + std::sort(std::begin(slackVariables_), std::end(slackVariables_), ::compareSlackSolutions); +} + +void ConstraintSlackAnalysis::trimSlackVariables() +{ + unsigned int nbSlackVars = slackVariables_.size(); + slackVariables_.resize(std::min(nbMaxSlackVarsToKeep, nbSlackVars)); +} + void ConstraintSlackAnalysis::printReport() const { InfeasibleProblemReport report(slackVariables_); - report.prettyPrint(); + report.logSuspiciousConstraints(); + report.logInfeasibilityCauses(); } } // namespace Antares::Optimization diff --git a/src/solver/infeasible-problem-analysis/constraint.cpp b/src/solver/infeasible-problem-analysis/constraint.cpp deleted file mode 100644 index 0e95848ad9..0000000000 --- a/src/solver/infeasible-problem-analysis/constraint.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2007-2024, RTE (https://www.rte-france.com) - * See AUTHORS.txt - * SPDX-License-Identifier: MPL-2.0 - * This file is part of Antares-Simulator, - * Adequacy and Performance assessment for interconnected energy networks. - * - * Antares_Simulator is free software: you can redistribute it and/or modify - * it under the terms of the Mozilla Public Licence 2.0 as published by - * the Mozilla Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Antares_Simulator is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Mozilla Public Licence 2.0 for more details. - * - * You should have received a copy of the Mozilla Public Licence 2.0 - * along with Antares_Simulator. If not, see . - */ -#include "antares/solver/infeasible-problem-analysis/constraint.h" - -#include -#include -#include -#include - -namespace -{ -const std::string kUnknown = ""; -} - -namespace Antares::Optimization -{ -Constraint::Constraint(const std::string& input, const double slackValue): - mInput(input), - mSlackValue(slackValue) -{ -} - -std::size_t Constraint::extractItems() -{ - const auto beg = mInput.begin(); - const auto end = mInput.end(); - std::size_t newPos = 0; - const std::size_t sepSize = 2; - const std::size_t inputSize = mInput.size(); - for (std::size_t pos = 0; pos < inputSize; pos = newPos + sepSize) - { - newPos = mInput.find("::", pos); - if (newPos == std::string::npos) - { - mItems.emplace_back(beg + pos, end); - break; - } - if (newPos > pos) - { - mItems.emplace_back(beg + pos, beg + newPos); - } - } - return mItems.size(); -} - -double Constraint::getSlackValue() const -{ - return mSlackValue; -} - -class StringIsNotWellFormated: public std::runtime_error -{ -public: - StringIsNotWellFormated(const std::string& error_message): - std::runtime_error(error_message) - { - } -}; - -std::string StringBetweenAngleBrackets(const std::string& str) -{ - const auto& begin = str.begin(); - const auto& end = str.end(); - - auto left = std::find(begin, end, '<'); - - if (left == end) - { - std::ostringstream stream; - stream << std::string("Error the string: ") << std::quoted(str) - << " does not contains the left angle bracket " << std::quoted("<"); - throw StringIsNotWellFormated(stream.str()); - } - - auto right = std::find(begin, end, '>'); - if (right == end) - { - std::ostringstream stream; - stream << std::string("Error the string: ") << std::quoted(str) - << " does not contains the right angle bracket " << std::quoted(">"); - throw StringIsNotWellFormated(stream.str()); - } - - if (std::distance(left, right) <= 1) - { - std::ostringstream stream; - stream << std::string("Error the string: ") << std::quoted(str) << " must be of format " - << std::quoted("**"); - throw StringIsNotWellFormated(stream.str()); - } - return std::string(left + 1, right); -} - -std::string Constraint::getAreaName() const -{ - if ((getType() == ConstraintType::binding_constraint_hourly) - || (getType() == ConstraintType::binding_constraint_daily) - || (getType() == ConstraintType::binding_constraint_weekly)) - { - return ""; - } - return StringBetweenAngleBrackets(mItems.at(1)); -} - -std::string Constraint::getTimeStepInYear() const -{ - switch (getType()) - { - case ConstraintType::binding_constraint_hourly: - case ConstraintType::binding_constraint_daily: - case ConstraintType::fictitious_load: - case ConstraintType::hydro_reservoir_level: - case ConstraintType::short_term_storage_level: - return StringBetweenAngleBrackets(mItems.at(mItems.size() - 2)); - default: - return kUnknown; - } -} - -ConstraintType Constraint::getType() const -{ - assert(mItems.size() > 1); - if (mItems.at(1) == "hourly") - { - return ConstraintType::binding_constraint_hourly; - } - if (mItems.at(1) == "daily") - { - return ConstraintType::binding_constraint_daily; - } - if (mItems.at(1) == "weekly") - { - return ConstraintType::binding_constraint_weekly; - } - if (mItems.at(0) == "FictiveLoads") - { - return ConstraintType::fictitious_load; - } - if (mItems.at(0) == "AreaHydroLevel") - { - return ConstraintType::hydro_reservoir_level; - } - if (mItems.at(0) == "Level") - { - return ConstraintType::short_term_storage_level; - } - return ConstraintType::none; -} - -std::string Constraint::getBindingConstraintName() const -{ - switch (getType()) - { - case ConstraintType::binding_constraint_hourly: - case ConstraintType::binding_constraint_daily: - case ConstraintType::binding_constraint_weekly: - return mItems.at(0); - default: - return kUnknown; - } -} - -std::string Constraint::getSTSName() const -{ - if (getType() == ConstraintType::short_term_storage_level) - { - return StringBetweenAngleBrackets(mItems.at(2)); - } - else - { - return kUnknown; - } -} - -std::string Constraint::prettyPrint() const -{ - switch (getType()) - { - case ConstraintType::binding_constraint_hourly: - return "Hourly binding constraint '" + getBindingConstraintName() + "' at hour " - + getTimeStepInYear(); - case ConstraintType::binding_constraint_daily: - return "Daily binding constraint '" + getBindingConstraintName() + "' at day " - + getTimeStepInYear(); - case ConstraintType::binding_constraint_weekly: - return "Weekly binding constraint '" + getBindingConstraintName(); - - case ConstraintType::fictitious_load: - return "Last resort shedding status at area '" + getAreaName() + "' at hour " - + getTimeStepInYear(); - case ConstraintType::hydro_reservoir_level: - return "Hydro reservoir constraint at area '" + getAreaName() + "' at hour " - + getTimeStepInYear(); - case ConstraintType::short_term_storage_level: - return "Short-term-storage reservoir constraint at area '" + getAreaName() + "' in STS '" - + getSTSName() + "' at hour " + getTimeStepInYear(); - - default: - return kUnknown; - } -} -} // namespace Antares::Optimization diff --git a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint-slack-analysis.h b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint-slack-analysis.h index a298b6b026..462d5f965f 100644 --- a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint-slack-analysis.h +++ b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint-slack-analysis.h @@ -23,9 +23,11 @@ #include #include "unfeasibility-analysis.h" +#include "watched-constraints.h" namespace operations_research { +class MPConstraint; class MPVariable; class MPSolver; } // namespace operations_research @@ -40,7 +42,6 @@ namespace Antares::Optimization class ConstraintSlackAnalysis: public UnfeasibilityAnalysis { public: - ConstraintSlackAnalysis() = default; ~ConstraintSlackAnalysis() override = default; void run(operations_research::MPSolver* problem) override; @@ -52,12 +53,14 @@ class ConstraintSlackAnalysis: public UnfeasibilityAnalysis } private: + void selectConstraintsToWatch(operations_research::MPSolver* problem); + void addSlackVariablesToConstraints(operations_research::MPSolver* problem); void buildObjective(operations_research::MPSolver* problem) const; - void addSlackVariables(operations_research::MPSolver* problem); + void sortSlackVariablesByValue(); + void trimSlackVariables(); + std::vector constraintsToWatch_; std::vector slackVariables_; - const std::string constraint_name_pattern = "^AreaHydroLevel::|::hourly::|::daily::|::weekly::|" - "^FictiveLoads::|^Level::"; }; } // namespace Antares::Optimization diff --git a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint.h b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint.h deleted file mode 100644 index aba82e7f21..0000000000 --- a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/constraint.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2007-2024, RTE (https://www.rte-france.com) - * See AUTHORS.txt - * SPDX-License-Identifier: MPL-2.0 - * This file is part of Antares-Simulator, - * Adequacy and Performance assessment for interconnected energy networks. - * - * Antares_Simulator is free software: you can redistribute it and/or modify - * it under the terms of the Mozilla Public Licence 2.0 as published by - * the Mozilla Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * Antares_Simulator is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Mozilla Public Licence 2.0 for more details. - * - * You should have received a copy of the Mozilla Public Licence 2.0 - * along with Antares_Simulator. If not, see . - */ -#pragma once - -#include -#include - -namespace Antares::Optimization -{ -enum class ConstraintType -{ - binding_constraint_hourly, - binding_constraint_daily, - binding_constraint_weekly, - fictitious_load, - hydro_reservoir_level, - short_term_storage_level, - none -}; - -class Constraint -{ -public: - // Construct object - Constraint() = default; - Constraint(const std::string& input, const double slackValue); - - // Raw members - double getSlackValue() const; - - // Extract items, check consistency - std::size_t extractItems(); - std::string prettyPrint() const; - ConstraintType getType() const; - -private: - std::string mInput; - std::vector mItems; - double mSlackValue; - - // Get specific items - std::string getAreaName() const; - std::string getSTSName() const; - std::string getTimeStepInYear() const; - std::string getBindingConstraintName() const; -}; -} // namespace Antares::Optimization diff --git a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/report.h b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/report.h index 6ebb919fb1..ef242b1f36 100644 --- a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/report.h +++ b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/report.h @@ -21,10 +21,12 @@ #pragma once #include +#include #include #include -#include "constraint.h" +#include "ortools/linear_solver/linear_solver.h" +#include "watched-constraints.h" namespace operations_research { @@ -37,20 +39,14 @@ class InfeasibleProblemReport { public: InfeasibleProblemReport() = delete; - explicit InfeasibleProblemReport( - const std::vector& slackVariables); - void prettyPrint(); + explicit InfeasibleProblemReport(const std::vector&); + void logSuspiciousConstraints(); + void logInfeasibilityCauses(); private: - void turnSlackVarsIntoConstraints( - const std::vector& slackVariables); - void sortConstraints(); - void trimConstraints(); - void extractItems(); - void logSuspiciousConstraints(); + void buildConstraintsFromSlackVars(const std::vector&); + void filterConstraintsToOneByType(); - std::vector mConstraints; - std::map mTypes; - const unsigned int nbVariables = 10; + std::vector> constraints_; }; } // namespace Antares::Optimization diff --git a/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/watched-constraints.h b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/watched-constraints.h new file mode 100644 index 0000000000..1c1c533d71 --- /dev/null +++ b/src/solver/infeasible-problem-analysis/include/antares/solver/infeasible-problem-analysis/watched-constraints.h @@ -0,0 +1,113 @@ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Antares::Optimization +{ +class WatchedConstraint +{ +public: + explicit WatchedConstraint(const std::string& name, const double slackValue); + virtual ~WatchedConstraint() = default; + virtual std::string infeasibility() = 0; + virtual std::string infeasibilityCause() = 0; + double slackValue() const; + +protected: + const std::vector& splitName() const; + +private: + std::vector splitName_; + double slack_value_; +}; + +class HourlyBC: public WatchedConstraint +{ + using WatchedConstraint::WatchedConstraint; + +public: + ~HourlyBC() override = default; + std::string infeasibility() override; + std::string infeasibilityCause() override; +}; + +class DailyBC: public WatchedConstraint +{ + using WatchedConstraint::WatchedConstraint; + +public: + ~DailyBC() override = default; + std::string infeasibility() override; + std::string infeasibilityCause() override; +}; + +class WeeklyBC: public WatchedConstraint +{ + using WatchedConstraint::WatchedConstraint; + +public: + ~WeeklyBC() override = default; + std::string infeasibility() override; + std::string infeasibilityCause() override; +}; + +class FictitiousLoad: public WatchedConstraint +{ + using WatchedConstraint::WatchedConstraint; + +public: + ~FictitiousLoad() override = default; + std::string infeasibility() override; + std::string infeasibilityCause() override; +}; + +class HydroLevel: public WatchedConstraint +{ + using WatchedConstraint::WatchedConstraint; + +public: + ~HydroLevel() override = default; + std::string infeasibility() override; + std::string infeasibilityCause() override; +}; + +class STS: public WatchedConstraint +{ + using WatchedConstraint::WatchedConstraint; + +public: + ~STS() override = default; + std::string infeasibility() override; + std::string infeasibilityCause() override; +}; + +class HydroProduction: public WatchedConstraint +{ + using WatchedConstraint::WatchedConstraint; + +public: + ~HydroProduction() override = default; + std::string infeasibility() override; + std::string infeasibilityCause() override; +}; + +class ConstraintsFactory +{ +public: + explicit ConstraintsFactory(); + std::unique_ptr create(const std::string&, const double) const; + std::regex constraintsFilter(); + +private: + std::map(const std::string&, const double)>> + regex_to_ctypes_; +}; + +} // namespace Antares::Optimization diff --git a/src/solver/infeasible-problem-analysis/report.cpp b/src/solver/infeasible-problem-analysis/report.cpp index 28e72109bf..dc43da140a 100644 --- a/src/solver/infeasible-problem-analysis/report.cpp +++ b/src/solver/infeasible-problem-analysis/report.cpp @@ -21,105 +21,79 @@ #include "antares/solver/infeasible-problem-analysis/report.h" #include +#include +#include #include -#include "antares/solver/infeasible-problem-analysis/constraint.h" -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#include "ortools/linear_solver/linear_solver.h" -#pragma GCC diagnostic pop - -using namespace operations_research; - -static bool compareSlackSolutions(const Antares::Optimization::Constraint& a, - const Antares::Optimization::Constraint& b) -{ - return a.getSlackValue() > b.getSlackValue(); -} namespace Antares::Optimization { InfeasibleProblemReport::InfeasibleProblemReport( - const std::vector& slackVariables) + const std::vector& slackVariables) { - turnSlackVarsIntoConstraints(slackVariables); - sortConstraints(); - trimConstraints(); + buildConstraintsFromSlackVars(slackVariables); } -void InfeasibleProblemReport::turnSlackVarsIntoConstraints( - const std::vector& slackVariables) +void InfeasibleProblemReport::buildConstraintsFromSlackVars( + const std::vector& slackVariables) { - for (const MPVariable* slack: slackVariables) + const ConstraintsFactory constraintsFactory; + for (const auto* slackVar: slackVariables) { - mConstraints.emplace_back(slack->name(), slack->solution_value()); + auto constraint = constraintsFactory.create(slackVar->name(), slackVar->solution_value()); + if (constraint) + { + constraints_.push_back(std::move(constraint)); + } } } -void InfeasibleProblemReport::sortConstraints() +bool lessTypeName(const std::shared_ptr a, + const std::shared_ptr b) { - std::sort(std::begin(mConstraints), std::end(mConstraints), ::compareSlackSolutions); + return std::type_index(typeid(*a)) < std::type_index(typeid(*b)); } -void InfeasibleProblemReport::trimConstraints() +bool sameType(const std::shared_ptr a, + const std::shared_ptr b) { - if (nbVariables <= mConstraints.size()) - { - mConstraints.resize(nbVariables); - } + return std::type_index(typeid(*a)) == std::type_index(typeid(*b)); } -void InfeasibleProblemReport::extractItems() +bool greaterValue(const std::shared_ptr a, std::shared_ptr b) { - for (auto& c: mConstraints) - { - if (c.extractItems() == 0) - { - return; - } - mTypes[c.getType()]++; - } + return a->slackValue() > b->slackValue(); } -void InfeasibleProblemReport::logSuspiciousConstraints() +void InfeasibleProblemReport::filterConstraintsToOneByType() { - Antares::logs.error() << "The following constraints are suspicious (first = most suspicious)"; - for (const auto& c: mConstraints) - { - Antares::logs.error() << c.prettyPrint(); - } - Antares::logs.error() << "Possible causes of infeasibility:"; - if (mTypes[ConstraintType::hydro_reservoir_level] > 0) - { - Antares::logs.error() << "* Hydro reservoir impossible to manage with cumulative options " - "\"hard bounds without heuristic\""; - } - if (mTypes[ConstraintType::fictitious_load] > 0) - { - Antares::logs.error() << "* Last resort shedding status,"; - } - if (mTypes[ConstraintType::short_term_storage_level] > 0) - { - Antares::logs.error() - << "* Short-term storage reservoir level impossible to manage. Please check inflows, " - "lower & upper curves and initial level (if prescribed),"; - } + // 1. Grouping constraints by C++ type (inside a group, order of instances remains unchanged) + std::ranges::stable_sort(constraints_, lessTypeName); + // 2. Keeping the first instances of each group, and rejecting others (= duplicates) to the end + // of vector + auto duplicates = std::ranges::unique(constraints_, sameType); + // 3. Removing trailing duplicates + constraints_.erase(duplicates.begin(), duplicates.end()); + // 4. Sorting remaining constraints by slack value (in descending order) + std::ranges::sort(constraints_, greaterValue); +} - const unsigned int bcCount = mTypes[ConstraintType::binding_constraint_hourly] - + mTypes[ConstraintType::binding_constraint_daily] - + mTypes[ConstraintType::binding_constraint_weekly]; - if (bcCount > 0) +void InfeasibleProblemReport::logSuspiciousConstraints() +{ + for (const auto& c: constraints_) { - Antares::logs.error() << "* Binding constraints,"; + Antares::logs.error() << c->infeasibility(); } - - Antares::logs.error() << "* Negative hurdle costs on lines with infinite capacity (rare)."; } -void InfeasibleProblemReport::prettyPrint() +void InfeasibleProblemReport::logInfeasibilityCauses() { - extractItems(); - logSuspiciousConstraints(); + filterConstraintsToOneByType(); + Antares::logs.error() << "Possible causes of infeasibility:"; + for (const auto& c: constraints_) + { + Antares::logs.error() << c->infeasibilityCause(); + } } } // namespace Antares::Optimization diff --git a/src/solver/infeasible-problem-analysis/watched-constraints.cpp b/src/solver/infeasible-problem-analysis/watched-constraints.cpp new file mode 100644 index 0000000000..e054f49f03 --- /dev/null +++ b/src/solver/infeasible-problem-analysis/watched-constraints.cpp @@ -0,0 +1,191 @@ +#include "antares/solver/infeasible-problem-analysis/watched-constraints.h" + +#include + +#include +#include +#include + +class StringIsNotWellFormated: public std::runtime_error +{ +public: + StringIsNotWellFormated(const std::string& error_message): + std::runtime_error(error_message) + { + } +}; + +std::string StringBetweenAngleBrackets(const std::string& constraintName) +{ + std::vector split_name; + boost::split(split_name, constraintName, boost::is_any_of("<>")); + + std::string err_msg = "Error: "; + if (split_name.size() < 3) + { + err_msg += "constraint name '" + constraintName + "' misses '<' and/or '>' bracket"; + throw StringIsNotWellFormated(err_msg); + } + if (split_name[1].empty()) + { + err_msg += "constraint name '" + constraintName + "' must be of format '**'"; + throw StringIsNotWellFormated(err_msg); + } + return split_name[1]; +} + +std::string timeStep(std::vector splitName) +{ + return StringBetweenAngleBrackets(splitName.at(splitName.size() - 2)); +} + +std::string shortName(std::vector splitName) +{ + return splitName.at(0); +} + +std::string areaName(std::vector splitName) +{ + return StringBetweenAngleBrackets(splitName.at(1)); +} + +std::string STSname(std::vector splitName) +{ + return StringBetweenAngleBrackets(splitName.at(2)); +} + +namespace Antares::Optimization +{ + +// --- Generic constraint --- +WatchedConstraint::WatchedConstraint(const std::string& name, const double slackValue): + slack_value_(slackValue) +{ + boost::algorithm::split_regex(splitName_, name, boost::regex("::")); +} + +const std::vector& WatchedConstraint::splitName() const +{ + return splitName_; +} + +double WatchedConstraint::slackValue() const +{ + return slack_value_; +} + +std::string HourlyBC::infeasibility() +{ + return "Hourly BC '" + shortName(splitName()) + "' at hour " + timeStep(splitName()); +} + +std::string HourlyBC::infeasibilityCause() +{ + return "* Hourly binding constraints."; +} + +// --- Daily BC constraint --- +std::string DailyBC::infeasibility() +{ + return "Daily BC '" + shortName(splitName()) + "' at day " + timeStep(splitName()); +} + +std::string DailyBC::infeasibilityCause() +{ + return "* Daily binding constraints,"; +} + +// --- Weekly BC constraint --- +std::string WeeklyBC::infeasibility() +{ + return "Weekly BC '" + shortName(splitName()); +} + +std::string WeeklyBC::infeasibilityCause() +{ + return "* Weekly binding constraints."; +} + +// --- Fictitious load constraint --- +std::string FictitiousLoad::infeasibility() +{ + return "Last resort shedding status at area '" + areaName(splitName()) + "' at hour " + + timeStep(splitName()); +} + +std::string FictitiousLoad::infeasibilityCause() +{ + return "* Last resort shedding status."; +} + +// --- Hydro level constraint --- +std::string HydroLevel::infeasibility() +{ + return "Hydro level constraint at area '" + areaName(splitName()) + "' at hour " + + timeStep(splitName()); +} + +std::string HydroLevel::infeasibilityCause() +{ + return "* Hydro reservoir impossible to manage with cumulative options " + "\"hard bounds without heuristic\""; +} + +// --- Short term storage constraint --- +std::string STS::infeasibility() +{ + return "Short-term-storage reservoir constraint at area '" + areaName(splitName()) + + "' in STS '" + STSname(splitName()) + "' at hour " + timeStep(splitName()); +} + +std::string STS::infeasibilityCause() +{ + return "* Short-term storage reservoir level impossible to manage. Please check inflows, " + "lower & upper curves and initial level (if prescribed),"; +} + +// --- Hydro production constraint --- +std::string HydroProduction::infeasibility() +{ + return "Hydro weekly production at area '" + areaName(splitName()) + "'"; +} + +std::string HydroProduction::infeasibilityCause() +{ + return "* impossible to generate exactly the weekly hydro target"; +} + +// --- Constraints factory --- +ConstraintsFactory::ConstraintsFactory() +{ + regex_to_ctypes_ = { + {"::hourly::", std::make_unique}, + {"::daily::", std::make_unique}, + {"::weekly::", std::make_unique}, + {"^FictiveLoads::", std::make_unique}, + {"^AreaHydroLevel::", std::make_unique}, + {"^Level::", std::make_unique}, + {"^HydroPower::", std::make_unique}}; +} + +std::unique_ptr ConstraintsFactory::create(const std::string& name, + const double value) const +{ + auto it = std::ranges::find_if(regex_to_ctypes_, + [&name](auto& pair) + { return std::regex_search(name, std::regex(pair.first)); }); + if (it != regex_to_ctypes_.end()) + { + return it->second(name, value); + } + return nullptr; +} + +std::regex ConstraintsFactory::constraintsFilter() +{ + auto keyView = std::views::keys(regex_to_ctypes_); + std::vector regex_ids = {keyView.begin(), keyView.end()}; + return std::regex(boost::algorithm::join(regex_ids, "|")); +} + +} // namespace Antares::Optimization diff --git a/src/solver/optimisation/HebdoProblemToLpsTranslator.cpp b/src/solver/optimisation/HebdoProblemToLpsTranslator.cpp index d4c6e4b6b0..9c67012c70 100644 --- a/src/solver/optimisation/HebdoProblemToLpsTranslator.cpp +++ b/src/solver/optimisation/HebdoProblemToLpsTranslator.cpp @@ -85,13 +85,13 @@ ConstantDataFromAntares HebdoProblemToLpsTranslator::commonProblemData( throw WeeklyProblemTranslationException("ConstraintesCount must be strictly positive"); } - if (problem->NombreDeContraintes > problem->IndicesDebutDeLigne.size()) + if (problem->NombreDeContraintes > (int)problem->IndicesDebutDeLigne.size()) { throw WeeklyProblemTranslationException( "ConstraintesCount exceed IndicesDebutDeLigne size"); } - if (problem->NombreDeContraintes > problem->NombreDeTermesDesLignes.size()) + if (problem->NombreDeContraintes > (int)problem->NombreDeTermesDesLignes.size()) { throw WeeklyProblemTranslationException( "ConstraintesCount exceed NombreDeTermesDesLignes size"); diff --git a/src/solver/optimisation/constraints/ShortTermStorageLevel.cpp b/src/solver/optimisation/constraints/ShortTermStorageLevel.cpp index 0faa565a8a..00a70e8dd9 100644 --- a/src/solver/optimisation/constraints/ShortTermStorageLevel.cpp +++ b/src/solver/optimisation/constraints/ShortTermStorageLevel.cpp @@ -42,8 +42,8 @@ void ShortTermStorageLevel::add(int pdt, int pays) -1.0, -1, builder.data.NombreDePasDeTempsPourUneOptimisation) - .ShortTermStorageInjection(index, -1.0 * storage.efficiency) - .ShortTermStorageWithdrawal(index, 1.0) + .ShortTermStorageInjection(index, -storage.injectionEfficiency) + .ShortTermStorageWithdrawal(index, storage.withdrawalEfficiency) .equalTo() .build(); } diff --git a/src/solver/optimisation/opt_gestion_des_couts_cas_lineaire.cpp b/src/solver/optimisation/opt_gestion_des_couts_cas_lineaire.cpp index c2d1859106..857561f616 100644 --- a/src/solver/optimisation/opt_gestion_des_couts_cas_lineaire.cpp +++ b/src/solver/optimisation/opt_gestion_des_couts_cas_lineaire.cpp @@ -61,7 +61,7 @@ static void shortTermStorageCost( pdtJour); varInjection >= 0) { - linearCost[varInjection] = cost; + linearCost[varInjection] = storage.withdrawalEfficiency * cost; } if (const int varWithdrawal = variableManager.ShortTermStorageWithdrawal( @@ -69,7 +69,7 @@ static void shortTermStorageCost( pdtJour); varWithdrawal >= 0) { - linearCost[varWithdrawal] = storage.efficiency * cost; + linearCost[varWithdrawal] = storage.injectionEfficiency * cost; } } } diff --git a/src/solver/optimisation/post_process_commands.cpp b/src/solver/optimisation/post_process_commands.cpp index 1b170ae353..3ced34ba4a 100644 --- a/src/solver/optimisation/post_process_commands.cpp +++ b/src/solver/optimisation/post_process_commands.cpp @@ -272,7 +272,7 @@ double CurtailmentSharingPostProcessCmd::calculateDensNewAndTotalLmrViolation() // write down densNew values for all the hours problemeHebdo_->ResultatsHoraires[Area].ValeursHorairesDENS[hour] = std::max( 0.0, - densNew - dtgMrg); + densNew); ; // copy spilled Energy values into spilled Energy values after CSR problemeHebdo_->ResultatsHoraires[Area].ValeursHorairesSpilledEnergyAfterCSR[hour] diff --git a/src/solver/simulation/adequacy.cpp b/src/solver/simulation/adequacy.cpp index f4759a0ab1..eed49029e8 100644 --- a/src/solver/simulation/adequacy.cpp +++ b/src/solver/simulation/adequacy.cpp @@ -258,7 +258,7 @@ bool Adequacy::year(Progression::Task& progression, state.resSpilled.zero(); auto nbAreas = study.areas.size(); - auto& runtime = *(study.runtime); + auto& runtime = study.runtime; for (uint i = 0; i != nbHoursInAWeek; ++i) { @@ -399,7 +399,7 @@ static std::vector retrieveBalance( void Adequacy::simulationEnd() { - if (!preproOnly && study.runtime->interconnectionsCount() > 0) + if (!preproOnly && study.runtime.interconnectionsCount() > 0) { auto balance = retrieveBalance(study, variables); ComputeFlowQuad(study, pProblemesHebdo[0], balance, pNbWeeks); diff --git a/src/solver/simulation/common-eco-adq.cpp b/src/solver/simulation/common-eco-adq.cpp index 27d2cdae6c..bfc697372a 100644 --- a/src/solver/simulation/common-eco-adq.cpp +++ b/src/solver/simulation/common-eco-adq.cpp @@ -60,9 +60,9 @@ static void RecalculDesEchangesMoyens(Data::Study& study, std::vector avgDirect; std::vector avgIndirect; - for (uint j = 0; j < study.runtime->interconnectionsCount(); ++j) + for (uint j = 0; j < study.runtime.interconnectionsCount(); ++j) { - auto* link = study.runtime->areaLink[j]; + auto* link = study.runtime.areaLink[j]; int ret = retrieveAverageNTC(study, link->directCapacities.timeSeries, link->timeseriesNumbers, @@ -100,7 +100,7 @@ static void RecalculDesEchangesMoyens(Data::Study& study, } catch (Data::UnfeasibleProblemError&) { - study.runtime->quadraticOptimizationHasFailed = true; + study.runtime.quadraticOptimizationHasFailed = true; } for (uint i = 0; i < (uint)problem.NombreDePasDeTemps; ++i) @@ -108,7 +108,7 @@ static void RecalculDesEchangesMoyens(Data::Study& study, const uint indx = i + PasDeTempsDebut; auto& ntcValues = problem.ValeursDeNTC[i]; - for (uint j = 0; j < study.runtime->interconnectionsCount(); ++j) + for (uint j = 0; j < study.runtime.interconnectionsCount(); ++j) { transitMoyenInterconnexionsRecalculQuadratique[j][indx] = ntcValues.ValeurDuFlux[j]; } @@ -123,9 +123,9 @@ bool ShouldUseQuadraticOptimisation(const Data::Study& study) return false; } - for (uint j = 0; j < study.runtime->interconnectionsCount(); ++j) + for (uint j = 0; j < study.runtime.interconnectionsCount(); ++j) { - auto& lnk = *(study.runtime->areaLink[j]); + auto& lnk = *(study.runtime.areaLink[j]); auto& impedances = lnk.parameters[Data::fhlImpedances]; for (uint hour = 0; hour < HOURS_PER_YEAR; ++hour) @@ -162,7 +162,7 @@ void ComputeFlowQuad(Data::Study& study, { logs.info() << " The quadratic optimisation has been skipped"; - for (uint j = 0; j < study.runtime->interconnectionsCount(); ++j) + for (uint j = 0; j < study.runtime.interconnectionsCount(); ++j) { for (uint w = 0; w != nbWeeks; ++w) { @@ -360,7 +360,7 @@ void SetInitialHydroLevel(Data::Study& study, void BuildThermalPartOfWeeklyProblem(Data::Study& study, PROBLEME_HEBDO& problem, const int PasDeTempsDebut, - double** thermalNoises, + std::vector>& thermalNoises, unsigned int year) { int hourInYear = PasDeTempsDebut; diff --git a/src/solver/simulation/economy.cpp b/src/solver/simulation/economy.cpp index f14d4cabf0..269fcd26ff 100644 --- a/src/solver/simulation/economy.cpp +++ b/src/solver/simulation/economy.cpp @@ -263,7 +263,7 @@ static std::vector retrieveBalance( void Economy::simulationEnd() { - if (!preproOnly && study.runtime->interconnectionsCount() > 0) + if (!preproOnly && study.runtime.interconnectionsCount() > 0) { auto balance = retrieveBalance(study, variables); ComputeFlowQuad(study, pProblemesHebdo[0], balance, pNbWeeks); diff --git a/src/solver/simulation/include/antares/solver/simulation/common-eco-adq.h b/src/solver/simulation/include/antares/solver/simulation/common-eco-adq.h index 0bf24cd71d..1139bb9ff9 100644 --- a/src/solver/simulation/include/antares/solver/simulation/common-eco-adq.h +++ b/src/solver/simulation/include/antares/solver/simulation/common-eco-adq.h @@ -63,7 +63,7 @@ void SetInitialHydroLevel(Data::Study& study, void BuildThermalPartOfWeeklyProblem(Data::Study& study, PROBLEME_HEBDO& problem, const int PasDeTempsDebut, - double** thermalNoises, + std::vector>& thermalNoises, unsigned int year); /*! diff --git a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h index 66400ed678..7805218061 100644 --- a/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h +++ b/src/solver/simulation/include/antares/solver/simulation/sim_structure_probleme_economique.h @@ -169,7 +169,8 @@ struct PROPERTIES double reservoirCapacity; double injectionNominalCapacity; double withdrawalNominalCapacity; - double efficiency; + double injectionEfficiency; + double withdrawalEfficiency; double initialLevel; bool initialLevelOptim; diff --git a/src/solver/simulation/include/antares/solver/simulation/solver.hxx b/src/solver/simulation/include/antares/solver/simulation/solver.hxx index 21e0aeb7a1..539c1d298e 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver.hxx +++ b/src/solver/simulation/include/antares/solver/simulation/solver.hxx @@ -146,10 +146,9 @@ public: // Getting random tables for this year yearRandomNumbers& randomForCurrentYear = randomForParallelYears.pYears[indexYear]; - double* randomReservoirLevel = nullptr; // 1 - Applying random levels for current year - randomReservoirLevel = randomForCurrentYear.pReservoirLevels; + auto randomReservoirLevel = randomForCurrentYear.pReservoirLevels; // 2 - Preparing the Time-series numbers // removed @@ -159,7 +158,7 @@ public: // 4 - Hydraulic ventilation pDurationCollector("hydro_ventilation") << [this, &randomReservoirLevel] - { hydroManagement.makeVentilation(randomReservoirLevel, y, scratchmap); }; + { hydroManagement.makeVentilation(randomReservoirLevel.data(), y, scratchmap); }; // Updating the state state.year = y; @@ -312,6 +311,9 @@ void ISimulation::run() } else { + // Export ts-numbers into output + TimeSeriesNumbers::StoreTimeSeriesNumbersIntoOuput(study, pResultWriter); + if (not ImplementationType::simulationBegin()) { return; @@ -323,9 +325,9 @@ void ISimulation::run() logs.info(); // Launching the simulation for all years - logs.info() << "MC-Years : [" << (study.runtime->rangeLimits.year[Data::rangeBegin] + 1) - << " .. " << (1 + study.runtime->rangeLimits.year[Data::rangeEnd]) - << "], total: " << study.runtime->rangeLimits.year[Data::rangeCount]; + logs.info() << "MC-Years : [" << (study.runtime.rangeLimits.year[Data::rangeBegin] + 1) + << " .. " << (1 + study.runtime.rangeLimits.year[Data::rangeEnd]) + << "], total: " << study.runtime.rangeLimits.year[Data::rangeCount]; // Current state std::vector state(pNbMaxPerformedYearsInParallel, Variable::State(study)); @@ -335,7 +337,7 @@ void ISimulation::run() ImplementationType::initializeState(state[numSpace], numSpace); } - uint finalYear = 1 + study.runtime->rangeLimits.year[Data::rangeEnd]; + uint finalYear = 1 + study.runtime.rangeLimits.year[Data::rangeEnd]; { pDurationCollector("mc_years") << [finalYear, &state, this] { loopThroughYears(0, finalYear, state); }; @@ -349,9 +351,6 @@ void ISimulation::run() ImplementationType::variables.simulationEnd(); - // Export ts-numbers into output - TimeSeriesNumbers::StoreTimeSeriesNumbersIntoOuput(study, pResultWriter); - // Spatial clusters // Notifying all variables to perform the final spatial clusters. // This must be done only when all variables have finished to compute their @@ -454,15 +453,24 @@ void ISimulation::regenerateTimeSeries(uint year) if (refreshTSonCurrentYear) { auto clusters = getAllClustersToGen(study.areas, pData.haveToRefreshTSThermal); -#define SEP Yuni::IO::Separator - const std::string savePath = std::string("ts-generator") + SEP + "thermal" + SEP + "mc-" - + std::to_string(year); -#undef SEP - generateThermalTimeSeries(study, clusters, pResultWriter, savePath); + generateThermalTimeSeries(study, + clusters, + study.runtime.random[Data::seedTsGenThermal]); + + bool archive = study.parameters.timeSeriesToArchive & Data::timeSeriesThermal; + bool doWeWrite = archive && !study.parameters.noOutput; + if (doWeWrite) + { + fs::path savePath = fs::path(study.folderOutput.to()) / "ts-generator" + / "thermal" / "mc-" / std::to_string(year); + writeThermalTimeSeries(clusters, savePath); + } // apply the spinning if we generated some in memory clusters for (auto* cluster: clusters) + { cluster->calculationOfSpinning(); + } } }; } @@ -503,7 +511,7 @@ uint ISimulation::buildSetsOfParallelYears( // Some thermal clusters may override the global parameter. // Therefore, we may want to refresh TS even if pData.haveToRefreshTSThermal == false bool haveToRefreshTSThermal = pData.haveToRefreshTSThermal - || study.runtime->thermalTSRefresh; + || study.runtime.thermalTSRefresh; refreshing = refreshing || (haveToRefreshTSThermal && (y % pData.refreshIntervalThermal == 0)); @@ -597,43 +605,43 @@ void ISimulation::allocateMemoryForRandomNumbers( { // General : randomForParallelYears.pYears[y].setNbAreas(nbAreas); - randomForParallelYears.pYears[y].pNbClustersByArea = new size_t[nbAreas]; + randomForParallelYears.pYears[y].pNbClustersByArea.resize(nbAreas); // Thermal noises : - randomForParallelYears.pYears[y].pThermalNoisesByArea = new double*[nbAreas]; + randomForParallelYears.pYears[y].pThermalNoisesByArea.resize(nbAreas); for (uint a = 0; a != nbAreas; ++a) { // logs.info() << " area : " << a << " :"; auto& area = *(study.areas.byIndex[a]); size_t nbClusters = area.thermal.list.allClustersCount(); - randomForParallelYears.pYears[y].pThermalNoisesByArea[a] = new double[nbClusters]; + randomForParallelYears.pYears[y].pThermalNoisesByArea[a].resize(nbClusters); randomForParallelYears.pYears[y].pNbClustersByArea[a] = nbClusters; } // Reservoir levels - randomForParallelYears.pYears[y].pReservoirLevels = new double[nbAreas]; + randomForParallelYears.pYears[y].pReservoirLevels.resize(nbAreas); // Noises on unsupplied and spilled energy - randomForParallelYears.pYears[y].pUnsuppliedEnergy = new double[nbAreas]; - randomForParallelYears.pYears[y].pSpilledEnergy = new double[nbAreas]; + randomForParallelYears.pYears[y].pUnsuppliedEnergy.resize(nbAreas); + randomForParallelYears.pYears[y].pSpilledEnergy.resize(nbAreas); // Hydro costs noises switch (study.parameters.power.fluctuations) { case Data::lssFreeModulations: { - randomForParallelYears.pYears[y].pHydroCostsByArea_freeMod = new double*[nbAreas]; + randomForParallelYears.pYears[y].pHydroCostsByArea_freeMod.resize(nbAreas); for (uint a = 0; a != nbAreas; ++a) { - randomForParallelYears.pYears[y].pHydroCostsByArea_freeMod[a] = new double[8784]; + randomForParallelYears.pYears[y].pHydroCostsByArea_freeMod[a].resize(8784); } break; } case Data::lssMinimizeRamping: case Data::lssMinimizeExcursions: { - randomForParallelYears.pYears[y].pHydroCosts_rampingOrExcursion = new double[nbAreas]; + randomForParallelYears.pYears[y].pHydroCosts_rampingOrExcursion.resize(nbAreas); break; } case Data::lssUnknown: @@ -652,8 +660,6 @@ void ISimulation::computeRandomNumbers( std::map& isYearPerformed, MersenneTwister& randomHydroGenerator) { - auto& runtime = *study.runtime; - uint indexYear = 0; std::vector::iterator ity; @@ -678,7 +684,7 @@ void ISimulation::computeRandomNumbers( for (auto& cluster: area.thermal.list.all()) { uint clusterIndex = cluster->areaWideIndex; - double thermalNoise = runtime.random[Data::seedThermalCosts].next(); + double thermalNoise = study.runtime.random[Data::seedThermalCosts].next(); if (isPerformed) { randomForYears.pYears[indexYear].pThermalNoisesByArea[a][clusterIndex] @@ -737,8 +743,8 @@ void ISimulation::computeRandomNumbers( // ... Unsupplied and spilled energy costs noises (french : bruits sur la defaillance // positive et negatives) ... references to the random number generators - auto& randomUnsupplied = study.runtime->random[Data::seedUnsuppliedEnergyCosts]; - auto& randomSpilled = study.runtime->random[Data::seedSpilledEnergyCosts]; + auto& randomUnsupplied = study.runtime.random[Data::seedUnsuppliedEnergyCosts]; + auto& randomSpilled = study.runtime.random[Data::seedSpilledEnergyCosts]; int currentSpilledEnergySeed = study.parameters.seed[Data::seedSpilledEnergyCosts]; int defaultSpilledEnergySeed = Data::antaresSeedDefaultValue @@ -778,7 +784,7 @@ void ISimulation::computeRandomNumbers( }); // each area // ... Hydro costs noises ... - auto& randomHydro = study.runtime->random[Data::seedHydroCosts]; + auto& randomHydro = study.runtime.random[Data::seedHydroCosts]; Data::PowerFluctuations powerFluctuations = study.parameters.power.fluctuations; switch (powerFluctuations) @@ -794,8 +800,8 @@ void ISimulation::computeRandomNumbers( { for (auto i = study.areas.begin(); i != end; ++i) { - double* noise = randomForYears.pYears[indexYear] - .pHydroCostsByArea_freeMod[areaIndex]; + auto& noise = randomForYears.pYears[indexYear] + .pHydroCostsByArea_freeMod[areaIndex]; std::set setHydroCostsNoises; for (uint j = 0; j != 8784; ++j) { @@ -972,6 +978,7 @@ void ISimulation::loopThroughYears(uint firstYear, hydroInputsChecker.Execute(year); } } + hydroInputsChecker.CheckForErrors(); logs.info() << " Starting the simulation"; @@ -996,6 +1003,8 @@ void ISimulation::loopThroughYears(uint firstYear, { // for each year not handled earlier hydroInputsChecker.Execute(y); + hydroInputsChecker.CheckForErrors(); + bool performCalculations = batch.isYearPerformed[y]; unsigned int numSpace = 999999; if (performCalculations) diff --git a/src/solver/simulation/include/antares/solver/simulation/solver_utils.h b/src/solver/simulation/include/antares/solver/simulation/solver_utils.h index 363b7839f8..d0d8748034 100644 --- a/src/solver/simulation/include/antares/solver/simulation/solver_utils.h +++ b/src/solver/simulation/include/antares/solver/simulation/solver_utils.h @@ -118,52 +118,10 @@ class yearRandomNumbers public: yearRandomNumbers() { - pThermalNoisesByArea = nullptr; - pNbClustersByArea = nullptr; pNbAreas = 0; } - ~yearRandomNumbers() - { - // General - delete[] pNbClustersByArea; - - // Thermal noises - for (uint a = 0; a != pNbAreas; a++) - { - delete[] pThermalNoisesByArea[a]; - } - delete[] pThermalNoisesByArea; - - // Reservoir levels, spilled and unsupplied energy - delete[] pReservoirLevels; - delete[] pUnsuppliedEnergy; - delete[] pSpilledEnergy; - - // Hydro costs noises - switch (pPowerFluctuations) - { - case Data::lssFreeModulations: - { - for (uint a = 0; a != pNbAreas; a++) - { - delete[] pHydroCostsByArea_freeMod[a]; - } - delete[] pHydroCostsByArea_freeMod; - break; - } - - case Data::lssMinimizeRamping: - case Data::lssMinimizeExcursions: - { - delete[] pHydroCosts_rampingOrExcursion; - break; - } - - case Data::lssUnknown: - break; - } - } + ~yearRandomNumbers() = default; void setNbAreas(uint nbAreas) { @@ -178,18 +136,18 @@ class yearRandomNumbers void reset() { // General - memset(pNbClustersByArea, 0, pNbAreas * sizeof(size_t)); + pNbClustersByArea.assign(pNbAreas, 0); // Thermal noises for (uint a = 0; a != pNbAreas; a++) { - memset(pThermalNoisesByArea[a], 0, pNbClustersByArea[a] * sizeof(double)); + pThermalNoisesByArea[a].assign(pNbClustersByArea[a], 0); } // Reservoir levels, spilled and unsupplied energy costs - memset(pReservoirLevels, 0, pNbAreas * sizeof(double)); - memset(pUnsuppliedEnergy, 0, pNbAreas * sizeof(double)); - memset(pSpilledEnergy, 0, pNbAreas * sizeof(double)); + pReservoirLevels.assign(pNbAreas, 0); + pUnsuppliedEnergy.assign(pNbAreas, 0); + pSpilledEnergy.assign(pNbAreas, 0); // Hydro costs noises switch (pPowerFluctuations) @@ -198,7 +156,7 @@ class yearRandomNumbers { for (uint a = 0; a != pNbAreas; a++) { - memset(pHydroCostsByArea_freeMod[a], 0, 8784 * sizeof(double)); + pHydroCostsByArea_freeMod[a].assign(8784, 0); } break; } @@ -206,7 +164,7 @@ class yearRandomNumbers case Data::lssMinimizeRamping: case Data::lssMinimizeExcursions: { - memset(pHydroCosts_rampingOrExcursion, 0, pNbAreas * sizeof(double)); + pHydroCosts_rampingOrExcursion.assign(pNbAreas, 0); break; } @@ -220,19 +178,19 @@ class yearRandomNumbers Data::PowerFluctuations pPowerFluctuations; // Data for thermal noises - double** pThermalNoisesByArea; - size_t* pNbClustersByArea; + std::vector> pThermalNoisesByArea; + std::vector pNbClustersByArea; // Data for reservoir levels - double* pReservoirLevels; + std::vector pReservoirLevels; // Data for unsupplied and spilled energy costs - double* pUnsuppliedEnergy; - double* pSpilledEnergy; + std::vector pUnsuppliedEnergy; + std::vector pSpilledEnergy; // Hydro costs noises - double** pHydroCostsByArea_freeMod; - double* pHydroCosts_rampingOrExcursion; + std::vector> pHydroCostsByArea_freeMod; + std::vector pHydroCosts_rampingOrExcursion; }; class randomNumbers @@ -242,7 +200,7 @@ class randomNumbers pMaxNbPerformedYears(maxNbPerformedYearsInAset) { // Allocate a table of parallel years structures - pYears = new yearRandomNumbers[maxNbPerformedYearsInAset]; + pYears.resize(maxNbPerformedYearsInAset); // Tells these structures their power fluctuations mode for (uint y = 0; y < maxNbPerformedYearsInAset; ++y) @@ -251,10 +209,7 @@ class randomNumbers } } - ~randomNumbers() - { - delete[] pYears; - } + ~randomNumbers() = default; void reset() { @@ -267,7 +222,7 @@ class randomNumbers } uint pMaxNbPerformedYears; - yearRandomNumbers* pYears; + std::vector pYears; // Associates : // year number (0, ..., total nb of years to compute - 1) --> index of the year's space diff --git a/src/solver/simulation/sim_alloc_probleme_hebdo.cpp b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp index 03126fa752..936a8e5ac0 100644 --- a/src/solver/simulation/sim_alloc_probleme_hebdo.cpp +++ b/src/solver/simulation/sim_alloc_probleme_hebdo.cpp @@ -39,7 +39,7 @@ void SIM_AllocationProblemeHebdo(const Data::Study& study, { SIM_AllocationProblemeDonneesGenerales(problem, study, NombreDePasDeTemps); SIM_AllocationProblemePasDeTemps(problem, study, NombreDePasDeTemps); - SIM_AllocationLinks(problem, study.runtime->interconnectionsCount(), NombreDePasDeTemps); + SIM_AllocationLinks(problem, study.runtime.interconnectionsCount(), NombreDePasDeTemps); SIM_AllocationConstraints(problem, study, NombreDePasDeTemps); SIM_AllocateAreas(problem, study, NombreDePasDeTemps); } @@ -55,7 +55,7 @@ void SIM_AllocationProblemeDonneesGenerales(PROBLEME_HEBDO& problem, { uint nbPays = study.areas.size(); - const uint linkCount = study.runtime->interconnectionsCount(); + const uint linkCount = study.runtime.interconnectionsCount(); problem.DefaillanceNegativeUtiliserPMinThermique.assign(nbPays, false); problem.DefaillanceNegativeUtiliserHydro.assign(nbPays, false); @@ -132,8 +132,8 @@ void SIM_AllocationProblemePasDeTemps(PROBLEME_HEBDO& problem, { uint nbPays = study.areas.size(); - const uint linkCount = study.runtime->interconnectionsCount(); - const uint shortTermStorageCount = study.runtime->shortTermStorageCount; + const uint linkCount = study.runtime.interconnectionsCount(); + const uint shortTermStorageCount = study.runtime.shortTermStorageCount; auto activeConstraints = study.bindingConstraints.activeConstraints(); @@ -159,7 +159,7 @@ void SIM_AllocationProblemePasDeTemps(PROBLEME_HEBDO& problem, 0); variablesMapping.NumeroDeVariableDuPalierThermique - .assign(study.runtime->thermalPlantTotalCount, 0); + .assign(study.runtime.thermalPlantTotalCount, 0); variablesMapping.NumeroDeVariablesDeLaProdHyd.assign(nbPays, 0); variablesMapping.NumeroDeVariablesDePompage.assign(nbPays, 0); variablesMapping.NumeroDeVariablesDeNiveau.assign(nbPays, 0); @@ -172,13 +172,13 @@ void SIM_AllocationProblemePasDeTemps(PROBLEME_HEBDO& problem, variablesMapping.NumeroDeVariablesVariationHydALaHausse.assign(nbPays, 0); variablesMapping.NumeroDeVariableDuNombreDeGroupesEnMarcheDuPalierThermique - .assign(study.runtime->thermalPlantTotalCount, 0); + .assign(study.runtime.thermalPlantTotalCount, 0); variablesMapping.NumeroDeVariableDuNombreDeGroupesQuiDemarrentDuPalierThermique - .assign(study.runtime->thermalPlantTotalCount, 0); + .assign(study.runtime.thermalPlantTotalCount, 0); variablesMapping.NumeroDeVariableDuNombreDeGroupesQuiSArretentDuPalierThermique - .assign(study.runtime->thermalPlantTotalCount, 0); + .assign(study.runtime.thermalPlantTotalCount, 0); variablesMapping.NumeroDeVariableDuNombreDeGroupesQuiTombentEnPanneDuPalierThermique - .assign(study.runtime->thermalPlantTotalCount, 0); + .assign(study.runtime.thermalPlantTotalCount, 0); variablesMapping.SIM_ShortTermStorage.InjectionVariable.assign(shortTermStorageCount, 0); variablesMapping.SIM_ShortTermStorage.WithdrawalVariable.assign(shortTermStorageCount, 0); @@ -205,14 +205,14 @@ void SIM_AllocationProblemePasDeTemps(PROBLEME_HEBDO& problem, problem.CorrespondanceCntNativesCntOptim[k] .NumeroDeContrainteDesContraintesDeDureeMinDeMarche - .assign(study.runtime->thermalPlantTotalCount, 0); + .assign(study.runtime.thermalPlantTotalCount, 0); problem.CorrespondanceCntNativesCntOptim[k] .NumeroDeContrainteDesContraintesDeDureeMinDArret - .assign(study.runtime->thermalPlantTotalCount, 0); + .assign(study.runtime.thermalPlantTotalCount, 0); problem.CorrespondanceCntNativesCntOptim[k] .NumeroDeLaDeuxiemeContrainteDesContraintesDesGroupesQuiTombentEnPanne - .assign(study.runtime->thermalPlantTotalCount, 0); + .assign(study.runtime.thermalPlantTotalCount, 0); problem.VariablesDualesDesContraintesDeNTC[k] .VariableDualeParInterconnexion.assign(linkCount, 0.); @@ -278,7 +278,7 @@ void SIM_AllocationConstraints(PROBLEME_HEBDO& problem, problem.MatriceDesContraintesCouplantes[constraintIndex] .PaysDuPalierDispatch.assign(bc->clusterCount(), 0); - // TODO : create a numberOfTimeSteps method in class of runtime->bindingConstraint + // TODO : create a numberOfTimeSteps method in class of runtime.bindingConstraint unsigned int nbTimeSteps; switch (bc->type()) { diff --git a/src/solver/simulation/sim_allocation_tableaux.cpp b/src/solver/simulation/sim_allocation_tableaux.cpp index 4b8674639d..ef788dfedd 100644 --- a/src/solver/simulation/sim_allocation_tableaux.cpp +++ b/src/solver/simulation/sim_allocation_tableaux.cpp @@ -29,9 +29,9 @@ using namespace Antares; void SIM_AllocationTableaux(const Data::Study& study) { - transitMoyenInterconnexionsRecalculQuadratique.resize(study.runtime->interconnectionsCount()); + transitMoyenInterconnexionsRecalculQuadratique.resize(study.runtime.interconnectionsCount()); - for (uint i = 0; i != study.runtime->interconnectionsCount(); i++) + for (uint i = 0; i != study.runtime.interconnectionsCount(); i++) { transitMoyenInterconnexionsRecalculQuadratique[i].assign(HOURS_PER_YEAR, 0.); } diff --git a/src/solver/simulation/sim_calcul_economique.cpp b/src/solver/simulation/sim_calcul_economique.cpp index 7fb3336599..9fd210f1fd 100644 --- a/src/solver/simulation/sim_calcul_economique.cpp +++ b/src/solver/simulation/sim_calcul_economique.cpp @@ -53,7 +53,8 @@ static void importShortTermStorages( // Properties toInsert.reservoirCapacity = st.properties.reservoirCapacity.value(); - toInsert.efficiency = st.properties.efficiencyFactor; + toInsert.injectionEfficiency = st.properties.injectionEfficiency; + toInsert.withdrawalEfficiency = st.properties.withdrawalEfficiency; toInsert.injectionNominalCapacity = st.properties.injectionNominalCapacity.value(); toInsert.withdrawalNominalCapacity = st.properties.withdrawalNominalCapacity.value(); toInsert.initialLevel = st.properties.initialLevel; @@ -86,7 +87,7 @@ void SIM_InitialisationProblemeHebdo(Data::Study& study, { problem.adequacyPatchRuntimeData = std::make_shared( study.areas, - study.runtime->areaLink); + study.runtime.areaLink); } problem.WaterValueAccurate = (study.parameters.hydroPricing.hpMode @@ -100,9 +101,9 @@ void SIM_InitialisationProblemeHebdo(Data::Study& study, problem.NombreDePays = study.areas.size(); - problem.NombreDInterconnexions = study.runtime->interconnectionsCount(); + problem.NombreDInterconnexions = study.runtime.interconnectionsCount(); - problem.NumberOfShortTermStorages = study.runtime->shortTermStorageCount; + problem.NumberOfShortTermStorages = study.runtime.shortTermStorageCount; auto activeConstraints = study.bindingConstraints.activeConstraints(); problem.NombreDeContraintesCouplantes = activeConstraints.size(); @@ -218,9 +219,9 @@ void SIM_InitialisationProblemeHebdo(Data::Study& study, importShortTermStorages(study.areas, problem.ShortTermStorage); - for (uint i = 0; i < study.runtime->interconnectionsCount(); ++i) + for (uint i = 0; i < study.runtime.interconnectionsCount(); ++i) { - auto& link = *(study.runtime->areaLink[i]); + auto& link = *(study.runtime.areaLink[i]); problem.PaysOrigineDeLInterconnexion[i] = link.from->index; problem.PaysExtremiteDeLInterconnexion[i] = link.with->index; } @@ -410,7 +411,7 @@ void SIM_RenseignementProblemeHebdo(const Study& study, { const auto& parameters = study.parameters; - auto& studyruntime = *study.runtime; + auto& studyruntime = study.runtime; const uint nbPays = study.areas.size(); const size_t pasDeTempsSizeDouble = problem.NombreDePasDeTemps * sizeof(double); @@ -836,12 +837,6 @@ void SIM_RenseignementProblemeHebdo(const Study& study, } marginGen = weekGenerationTarget; - - if (problem.CaracteristiquesHydrauliques[k].NiveauInitialReservoir - < weekTarget_tmp) - { - marginGen = problem.CaracteristiquesHydrauliques[k].NiveauInitialReservoir; - } } if (not problem.CaracteristiquesHydrauliques[k].TurbinageEntreBornes) diff --git a/src/solver/simulation/timeseries-numbers.cpp b/src/solver/simulation/timeseries-numbers.cpp index 446a0efa08..fecb8ba4eb 100644 --- a/src/solver/simulation/timeseries-numbers.cpp +++ b/src/solver/simulation/timeseries-numbers.cpp @@ -475,7 +475,7 @@ void drawAndStoreTSnumbersForNOTintraModal(const array& i if (!isTSintramodal[indexTS]) { area.load.series.timeseriesNumbers[year] = (uint32_t)(floor( - study.runtime->random[seedTimeseriesNumbers].next() + study.runtime.random[seedTimeseriesNumbers].next() * area.load.series.timeSeries.width)); } @@ -487,7 +487,7 @@ void drawAndStoreTSnumbersForNOTintraModal(const array& i if (!isTSintramodal[indexTS]) { area.solar.series.timeseriesNumbers[year] = (uint32_t)(floor( - study.runtime->random[seedTimeseriesNumbers].next() + study.runtime.random[seedTimeseriesNumbers].next() * area.solar.series.timeSeries.width)); } @@ -499,7 +499,7 @@ void drawAndStoreTSnumbersForNOTintraModal(const array& i if (!isTSintramodal[indexTS]) { area.wind.series.timeseriesNumbers[year] = (uint32_t)(floor( - study.runtime->random[seedTimeseriesNumbers].next() + study.runtime.random[seedTimeseriesNumbers].next() * area.wind.series.timeSeries.width)); } @@ -511,8 +511,7 @@ void drawAndStoreTSnumbersForNOTintraModal(const array& i if (!isTSintramodal[indexTS]) { area.hydro.series->timeseriesNumbers[year] = (uint32_t)(floor( - study.runtime->random[seedTimeseriesNumbers].next() - * area.hydro.series->TScount())); + study.runtime.random[seedTimeseriesNumbers].next() * area.hydro.series->TScount())); } // ------------- @@ -524,14 +523,14 @@ void drawAndStoreTSnumbersForNOTintraModal(const array& i { if (!cluster->enabled) { - study.runtime->random[seedTimeseriesNumbers].next(); + study.runtime.random[seedTimeseriesNumbers].next(); } else { if (!isTSintramodal[indexTS]) { cluster->series.timeseriesNumbers[year] = (uint32_t)(floor( - study.runtime->random[seedTimeseriesNumbers].next() + study.runtime.random[seedTimeseriesNumbers].next() * cluster->series.timeSeries.width)); } } @@ -549,7 +548,7 @@ void drawAndStoreTSnumbersForNOTintraModal(const array& i // There is no TS generation for renewable clusters uint nbTimeSeries = cluster->series.timeSeries.width; cluster->series.timeseriesNumbers[year] = (uint32_t)(floor( - study.runtime->random[seedTimeseriesNumbers].next() * nbTimeSeries)); + study.runtime.random[seedTimeseriesNumbers].next() * nbTimeSeries)); } } @@ -567,7 +566,7 @@ void drawAndStoreTSnumbersForNOTintraModal(const array& i if (nbTimeSeries > 1) { link.timeseriesNumbers[year] = (uint32_t)(floor( - study.runtime->random[seedTimeseriesNumbers].next() * nbTimeSeries)); + study.runtime.random[seedTimeseriesNumbers].next() * nbTimeSeries)); } } } @@ -579,7 +578,7 @@ void drawAndStoreTSnumbersForNOTintraModal(const array& i auto& groupTsNumber = group->timeseriesNumbers[year]; if (nbTimeSeries > 1) { - groupTsNumber = (uint32_t)(floor(study.runtime->random[seedTimeseriesNumbers].next() + groupTsNumber = (uint32_t)(floor(study.runtime.random[seedTimeseriesNumbers].next() * nbTimeSeries)); } } @@ -753,7 +752,7 @@ bool TimeSeriesNumbers::Generate(Study& study) return GenerateDeratedMode(study); } - const uint years = 1 + study.runtime->rangeLimits.year[rangeEnd]; + const uint years = 1 + study.runtime.rangeLimits.year[rangeEnd]; const array isTSintramodal = { (bool)(timeSeriesLoad & parameters.intraModal), @@ -783,7 +782,7 @@ bool TimeSeriesNumbers::Generate(Study& study) drawTSnumbersForIntraModal(intramodal_draws, isTSintramodal, nbTimeseriesByMode, - study.runtime->random); + study.runtime.random); storeTSnumbersForIntraModal(intramodal_draws, isTSintramodal, year, study.areas); // NOT intra-modal TS : draw and store TS numbers diff --git a/src/solver/ts-generator/availability.cpp b/src/solver/ts-generator/availability.cpp index efb5991093..a94fd99e46 100644 --- a/src/solver/ts-generator/availability.cpp +++ b/src/solver/ts-generator/availability.cpp @@ -19,8 +19,6 @@ ** along with Antares_Simulator. If not, see . */ -#include -#include #include #include // For Antares::IO::fileSetContent @@ -28,10 +26,6 @@ #include #include #include -#include -#include "antares/study/simulation.h" - -#define SEP Yuni::IO::Separator constexpr double FAILURE_RATE_EQ_1 = 0.999; @@ -45,14 +39,12 @@ AvailabilityTSGeneratorData::AvailabilityTSGeneratorData(Data::ThermalCluster* c forcedLaw(cluster->forcedLaw), plannedLaw(cluster->plannedLaw), prepro(cluster->prepro), - series(cluster->series.timeSeries), modulationCapacity(cluster->modulation[Data::thermalModulationCapacity]), name(cluster->name()) { } AvailabilityTSGeneratorData::AvailabilityTSGeneratorData(LinkTSgenerationParams& source, - Data::TimeSeries& capacity, Matrix<>& modulation, const std::string& areaDestName): unitCount(source.unitCount), @@ -62,7 +54,6 @@ AvailabilityTSGeneratorData::AvailabilityTSGeneratorData(LinkTSgenerationParams& forcedLaw(source.forcedLaw), plannedLaw(source.plannedLaw), prepro(source.prepro.get()), - series(capacity.timeSeries), modulationCapacity(modulation[0]), name(areaDestName) { @@ -70,20 +61,19 @@ AvailabilityTSGeneratorData::AvailabilityTSGeneratorData(LinkTSgenerationParams& namespace { -class GeneratorTempData final +class AvailabilityTSgenerator final { public: - explicit GeneratorTempData(Data::Study&, unsigned, MersenneTwister&); - explicit GeneratorTempData(bool, unsigned, MersenneTwister&); + explicit AvailabilityTSgenerator(bool, unsigned, MersenneTwister&); - void generateTS(AvailabilityTSGeneratorData&) const; + Matrix run(AvailabilityTSGeneratorData&) const; private: bool derated; uint nbOfSeriesToGen_; - MersenneTwister& rndgenerator; + MersenneTwister& randomGenerator_; static constexpr int Log_size = 4000; @@ -101,30 +91,21 @@ class GeneratorTempData final const T& duration) const; }; -GeneratorTempData::GeneratorTempData(Data::Study& study, - unsigned nbOfSeriesToGen, - MersenneTwister& rndGenerator): - derated(study.parameters.derated), - nbOfSeriesToGen_(nbOfSeriesToGen), - rndgenerator(rndGenerator) -{ -} - -GeneratorTempData::GeneratorTempData(bool derated, - unsigned int nbOfSeriesToGen, - MersenneTwister& rndGenerator): +AvailabilityTSgenerator::AvailabilityTSgenerator(bool derated, + unsigned int nbOfSeriesToGen, + MersenneTwister& randomGenerator): derated(derated), nbOfSeriesToGen_(nbOfSeriesToGen), - rndgenerator(rndGenerator) + randomGenerator_(randomGenerator) { } template -void GeneratorTempData::prepareIndispoFromLaw(Data::StatisticalLaw law, - double volatility, - std::array& A, - std::array& B, - const T& duration) const +void AvailabilityTSgenerator::prepareIndispoFromLaw(Data::StatisticalLaw law, + double volatility, + std::array& A, + std::array& B, + const T& duration) const { switch (law) { @@ -165,18 +146,18 @@ void GeneratorTempData::prepareIndispoFromLaw(Data::StatisticalLaw law, } } -int GeneratorTempData::durationGenerator(Data::StatisticalLaw law, - int expec, - double volat, - double a, - double b) const +int AvailabilityTSgenerator::durationGenerator(Data::StatisticalLaw law, + int expec, + double volat, + double a, + double b) const { if (volat == 0 || expec == 1) { return expec; } - double rndnumber = rndgenerator.next(); + double rndnumber = randomGenerator_.next(); switch (law) { @@ -197,13 +178,16 @@ int GeneratorTempData::durationGenerator(Data::StatisticalLaw law, return 0; } -void GeneratorTempData::generateTS(AvailabilityTSGeneratorData& tsGenerationData) const +Matrix AvailabilityTSgenerator::run(AvailabilityTSGeneratorData& tsGenerationData) const { assert(tsGenerationData.prepro); + Matrix to_return; + to_return.reset(nbOfSeriesToGen_, 24 * DAYS_PER_YEAR); + if (0 == tsGenerationData.unitCount || 0 == tsGenerationData.nominalCapacity) { - return; + return to_return; } const auto& preproData = *(tsGenerationData.prepro); @@ -317,18 +301,11 @@ void GeneratorTempData::generateTS(AvailabilityTSGeneratorData& tsGenerationData double last = 0; auto& modulation = tsGenerationData.modulationCapacity; - double* dstSeries = nullptr; const uint tsCount = nbOfSeriesToGen_ + 2; for (uint tsIndex = 0; tsIndex != tsCount; ++tsIndex) { uint hour = 0; - - if (tsIndex > 1) - { - dstSeries = tsGenerationData.series[tsIndex - 2]; - } - for (uint dayInTheYear = 0; dayInTheYear < DAYS_PER_YEAR; ++dayInTheYear) { assert(dayInTheYear < 366); @@ -388,7 +365,7 @@ void GeneratorTempData::generateTS(AvailabilityTSGeneratorData& tsGenerationData if (lf[dayInTheYear] > 0. && lf[dayInTheYear] <= FAILURE_RATE_EQ_1) { - A = rndgenerator.next(); + A = randomGenerator_.next(); last = FPOW[dayInTheYear][AUN]; if (A > last) @@ -424,7 +401,7 @@ void GeneratorTempData::generateTS(AvailabilityTSGeneratorData& tsGenerationData } last = PPOW[dayInTheYear][AUN_app]; - A = rndgenerator.next(); + A = randomGenerator_.next(); if (A > last) { @@ -578,7 +555,7 @@ void GeneratorTempData::generateTS(AvailabilityTSGeneratorData& tsGenerationData double AVPDayInTheYear = AVP[dayInTheYear]; for (uint h = 0; h != 24; ++h) { - dstSeries[hour] = std::round(AVPDayInTheYear * modulation[hour]); + to_return[tsIndex - 2][hour] = std::round(AVPDayInTheYear * modulation[hour]); ++hour; } } @@ -587,8 +564,9 @@ void GeneratorTempData::generateTS(AvailabilityTSGeneratorData& tsGenerationData if (derated) { - tsGenerationData.series.averageTimeseries(); + to_return.averageTimeseries(); } + return to_return; } } // namespace @@ -612,22 +590,7 @@ std::vector getAllClustersToGen(const Data::AreaList& are return clusters; } -static void writeResultsToDisk(const Data::Study& study, - Solver::IResultWriter& writer, - const Matrix<>& series, - const std::string& savePath) -{ - if (study.parameters.noOutput) - { - return; - } - - std::string buffer; - series.saveToBuffer(buffer, 0); - writer.addEntryFromBuffer(savePath, buffer); -} - -static void writeResultsToDisk(const Matrix<>& series, const std::filesystem::path& savePath) +void writeTStoDisk(const Matrix<>& series, const std::filesystem::path savePath) { std::string buffer; series.saveToBuffer(buffer, 0); @@ -642,45 +605,48 @@ static void writeResultsToDisk(const Matrix<>& series, const std::filesystem::pa bool generateThermalTimeSeries(Data::Study& study, const std::vector& clusters, - Solver::IResultWriter& writer, - const std::string& savePath) + MersenneTwister& thermalRandom) { logs.info(); logs.info() << "Generating the thermal time-series"; - bool archive = study.parameters.timeSeriesToArchive & Data::timeSeriesThermal; - - auto generator = GeneratorTempData(study, - study.parameters.nbTimeSeriesThermal, - study.runtime->random[Data::seedTsGenThermal]); + auto generator = AvailabilityTSgenerator(study.parameters.derated, + study.parameters.nbTimeSeriesThermal, + thermalRandom); for (auto* cluster: clusters) { AvailabilityTSGeneratorData tsGenerationData(cluster); - generator.generateTS(tsGenerationData); - - if (archive) // For compatibilty with in memory thermal TS generation - { - std::string filePath = savePath + SEP + cluster->parentArea->id + SEP + cluster->id() - + ".txt"; - writeResultsToDisk(study, writer, cluster->series.timeSeries, filePath); - } + cluster->series.timeSeries = generator.run(tsGenerationData); } return true; } +void writeThermalTimeSeries(const std::vector& clusters, + const fs::path& savePath) +{ + for (auto* cluster: clusters) + { + auto areaName = cluster->parentArea->id.to(); + auto clusterName = cluster->id(); + auto filePath = savePath / areaName / clusterName += ".txt"; + + writeTStoDisk(cluster->series.timeSeries, filePath); + } +} + // gp : we should try to add const identifiers before args here bool generateLinkTimeSeries(std::vector& links, StudyParamsForLinkTS& generalParams, - const std::string& savePath) + const fs::path& savePath) { logs.info(); logs.info() << "Generation of links time-series"; - auto generator = GeneratorTempData(generalParams.derated, - generalParams.nbLinkTStoGenerate, - generalParams.random); + auto generator = AvailabilityTSgenerator(generalParams.derated, + generalParams.nbLinkTStoGenerate, + generalParams.random); for (auto& link: links) { if (!link.hasValidData) @@ -695,33 +661,23 @@ bool generateLinkTimeSeries(std::vector& links, continue; // Skipping the link } - Data::TimeSeriesNumbers fakeTSnumbers; // gp : to quickly get rid of - Data::TimeSeries ts(fakeTSnumbers); - ts.resize(generalParams.nbLinkTStoGenerate, HOURS_PER_YEAR); - - // DIRECT + // === DIRECT ======================= AvailabilityTSGeneratorData tsConfigDataDirect(link, - ts, link.modulationCapacityDirect, link.namesPair.second); + auto generated_ts = generator.run(tsConfigDataDirect); - generator.generateTS(tsConfigDataDirect); + auto filePath = savePath / link.namesPair.first / link.namesPair.second += "_direct.txt"; + writeTStoDisk(generated_ts, filePath); - std::string filePath = savePath + SEP + link.namesPair.first + SEP + link.namesPair.second - + "_direct.txt"; - writeResultsToDisk(ts.timeSeries, filePath); - - // INDIRECT + // === INDIRECT ======================= AvailabilityTSGeneratorData tsConfigDataIndirect(link, - ts, link.modulationCapacityIndirect, link.namesPair.second); + generated_ts = generator.run(tsConfigDataIndirect); - generator.generateTS(tsConfigDataIndirect); - - filePath = savePath + SEP + link.namesPair.first + SEP + link.namesPair.second - + "_indirect.txt"; - writeResultsToDisk(ts.timeSeries, filePath); + filePath = savePath / link.namesPair.first / link.namesPair.second += "_indirect.txt"; + writeTStoDisk(generated_ts, filePath); } return true; diff --git a/src/solver/ts-generator/hydro.cpp b/src/solver/ts-generator/hydro.cpp index 2d31f08b22..168b290dfb 100644 --- a/src/solver/ts-generator/hydro.cpp +++ b/src/solver/ts-generator/hydro.cpp @@ -63,7 +63,7 @@ bool GenerateHydroTimeSeries(Data::Study& study, uint currentYear, Solver::IResu Solver::Progression::Task progression(study, currentYear, Solver::Progression::sectTSGHydro); - auto& studyRTI = *(study.runtime); + auto& studyRTI = study.runtime; auto& calendar = study.calendar; uint DIM = MONTHS_PER_YEAR * study.areas.size(); @@ -72,7 +72,7 @@ bool GenerateHydroTimeSeries(Data::Study& study, uint currentYear, Solver::IResu Matrix CHSKY; CHSKY.reset(DIM, DIM); - double* QCHOLTemp = new double[DIM]; + std::vector QCHOLTemp(DIM); Matrix B; B.reset(DIM, DIM); @@ -84,7 +84,7 @@ bool GenerateHydroTimeSeries(Data::Study& study, uint currentYear, Solver::IResu B.entry, nullmatrx, study.areas.size(), - QCHOLTemp, + QCHOLTemp.data(), true)) { throw FatalError("TS Generator: Hydro: Invalid correlation matrix"); @@ -122,7 +122,7 @@ bool GenerateHydroTimeSeries(Data::Study& study, uint currentYear, Solver::IResu B.entry, nullmatrx, DIM, - QCHOLTemp, + QCHOLTemp.data(), true); if (r < 1.) { @@ -134,15 +134,13 @@ bool GenerateHydroTimeSeries(Data::Study& study, uint currentYear, Solver::IResu } } - Solver::Cholesky(CHSKY.entry, B.entry, DIM, QCHOLTemp); + Solver::Cholesky(CHSKY.entry, B.entry, DIM, QCHOLTemp.data()); B.clear(); CORRE.clear(); + QCHOLTemp.clear(); - delete[] QCHOLTemp; - QCHOLTemp = nullptr; - - double* NORM = new double[DIM]; + std::vector NORM(DIM); for (uint i = 0; i != DIM; ++i) { NORM[i] = 0.; @@ -308,8 +306,6 @@ bool GenerateHydroTimeSeries(Data::Study& study, uint currentYear, Solver::IResu } } - delete[] NORM; - return true; } diff --git a/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h b/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h index bfb2901bfa..5ffea85100 100644 --- a/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h +++ b/src/solver/ts-generator/include/antares/solver/ts-generator/generator.h @@ -24,15 +24,14 @@ #include #include -#include #include #include #include #include -#include #include "xcast/xcast.h" +namespace fs = std::filesystem; using LinkPair = std::pair; using LinkPairs = std::vector; @@ -52,7 +51,7 @@ struct LinkTSgenerationParams { LinkPair namesPair; - unsigned unitCount = 0; + unsigned unitCount = 1; double nominalCapacity = 0; double forcedVolatility = 0.; @@ -76,7 +75,6 @@ class AvailabilityTSGeneratorData explicit AvailabilityTSGeneratorData(Data::ThermalCluster*); AvailabilityTSGeneratorData(LinkTSgenerationParams&, - Data::TimeSeries&, Matrix<>& modulation, const std::string& name); @@ -91,8 +89,6 @@ class AvailabilityTSGeneratorData Data::PreproAvailability* prepro; - Matrix<>& series; - Matrix<>::ColumnType& modulationCapacity; const std::string& name; @@ -110,12 +106,14 @@ bool GenerateTimeSeries(Data::Study& study, uint year, IResultWriter& writer); bool generateThermalTimeSeries(Data::Study& study, const std::vector& clusters, - Solver::IResultWriter& writer, - const std::string& savePath); + MersenneTwister& thermalRandom); + +void writeThermalTimeSeries(const std::vector& clusters, + const fs::path& savePath); bool generateLinkTimeSeries(std::vector& links, StudyParamsForLinkTS&, - const std::string& savePath); + const fs::path& savePath); std::vector getAllClustersToGen(const Data::AreaList& areas, bool globalThermalTSgeneration); diff --git a/src/solver/ts-generator/include/antares/solver/ts-generator/generator.hxx b/src/solver/ts-generator/include/antares/solver/ts-generator/generator.hxx index 6596a4f7ab..7455c1accc 100644 --- a/src/solver/ts-generator/include/antares/solver/ts-generator/generator.hxx +++ b/src/solver/ts-generator/include/antares/solver/ts-generator/generator.hxx @@ -58,16 +58,16 @@ bool GenerateTimeSeries(Data::Study& study, uint year, IResultWriter& writer) switch (T) { case Data::timeSeriesLoad: - xcast->random = &(study.runtime->random[Data::seedTsGenLoad]); + xcast->random = &(study.runtime.random[Data::seedTsGenLoad]); break; case Data::timeSeriesSolar: - xcast->random = &(study.runtime->random[Data::seedTsGenSolar]); + xcast->random = &(study.runtime.random[Data::seedTsGenSolar]); break; case Data::timeSeriesWind: - xcast->random = &(study.runtime->random[Data::seedTsGenWind]); + xcast->random = &(study.runtime.random[Data::seedTsGenWind]); break; case Data::timeSeriesHydro: - xcast->random = &(study.runtime->random[Data::seedTsGenHydro]); + xcast->random = &(study.runtime.random[Data::seedTsGenHydro]); break; default: xcast->random = nullptr; diff --git a/src/solver/variable/include/antares/solver/variable/adequacy/overallCost.h b/src/solver/variable/include/antares/solver/variable/adequacy/overallCost.h index f730a3d865..3bdcee903b 100644 --- a/src/solver/variable/include/antares/solver/variable/adequacy/overallCost.h +++ b/src/solver/variable/include/antares/solver/variable/adequacy/overallCost.h @@ -194,7 +194,7 @@ class OverallCost: public Variable::IVariable, NextT, VCardOv void yearEndBuildForEachThermalCluster(State& state, uint year, unsigned int numSpace) { // Get end year calculations - for (unsigned int i = 0; i < state.study.runtime->rangeLimits.hour[Data::rangeCount]; ++i) + for (unsigned int i = 0; i < state.study.runtime.rangeLimits.hour[Data::rangeCount]; ++i) { pValuesForTheCurrentYear[numSpace][i] += state.thermalClusterOperatingCostForYear[i]; } diff --git a/src/solver/variable/include/antares/solver/variable/commons/spatial-aggregate.h b/src/solver/variable/include/antares/solver/variable/commons/spatial-aggregate.h index ea966c4977..851dbb9999 100644 --- a/src/solver/variable/include/antares/solver/variable/commons/spatial-aggregate.h +++ b/src/solver/variable/include/antares/solver/variable/commons/spatial-aggregate.h @@ -221,7 +221,7 @@ class SpatialAggregate VariableAccessorType::InitializeAndReset(pValuesForTheCurrentYear[numSpace], study); } - auto& limits = study.runtime->rangeLimits; + auto& limits = study.runtime.rangeLimits; pRatioYear = 100. / (double)limits.year[Data::rangeCount]; pRatioDay = 100. / (double)limits.day[Data::rangeCount]; diff --git a/src/solver/variable/include/antares/solver/variable/economy/links/flowQuad.h b/src/solver/variable/include/antares/solver/variable/economy/links/flowQuad.h index b7d00a3fb8..b6de8329ae 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/links/flowQuad.h +++ b/src/solver/variable/include/antares/solver/variable/economy/links/flowQuad.h @@ -129,7 +129,7 @@ class FlowQuad: public Variable::IVariable, NextT, VCardFlowQuad void initializeFromStudy(Data::Study& study) { // Average on all years - pNbHours = study.runtime->rangeLimits.hour[Data::rangeEnd] + 1; + pNbHours = study.runtime.rangeLimits.hour[Data::rangeEnd] + 1; AncestorType::pResults.initializeFromStudy(study); AncestorType::pResults.reset(); diff --git a/src/solver/variable/include/antares/solver/variable/economy/nbOfDispatchedUnits.h b/src/solver/variable/include/antares/solver/variable/economy/nbOfDispatchedUnits.h index a2af682a67..173343f61f 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/nbOfDispatchedUnits.h +++ b/src/solver/variable/include/antares/solver/variable/economy/nbOfDispatchedUnits.h @@ -198,8 +198,8 @@ class NbOfDispatchedUnits void yearEndBuildForEachThermalCluster(State& state, uint year, unsigned int numSpace) { // Get end year calculations - for (unsigned int i = state.study.runtime->rangeLimits.hour[Data::rangeBegin]; - i <= state.study.runtime->rangeLimits.hour[Data::rangeEnd]; + for (unsigned int i = state.study.runtime.rangeLimits.hour[Data::rangeBegin]; + i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; ++i) { pValuesForTheCurrentYear[numSpace][i] += state.thermalClusterDispatchedUnitsCountForYear diff --git a/src/solver/variable/include/antares/solver/variable/economy/nbOfDispatchedUnitsByPlant.h b/src/solver/variable/include/antares/solver/variable/economy/nbOfDispatchedUnitsByPlant.h index 2bc519a94c..37f19ea3d6 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/nbOfDispatchedUnitsByPlant.h +++ b/src/solver/variable/include/antares/solver/variable/economy/nbOfDispatchedUnitsByPlant.h @@ -234,7 +234,7 @@ class NbOfDispatchedUnitsByPlant: public Variable::IVariablerangeLimits.hour[Data::rangeEnd]; ++i) + for (unsigned int i = 0; i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; ++i) { state.thermalClusterDispatchedUnitsCountForYear[i] += static_cast( pValuesForTheCurrentYear[numSpace][state.thermalCluster->areaWideIndex].hour[i]); @@ -247,8 +247,8 @@ class NbOfDispatchedUnitsByPlant: public Variable::IVariablerangeLimits.hour[Data::rangeBegin]; - i <= state.study.runtime->rangeLimits.hour[Data::rangeEnd]; + for (unsigned int i = state.study.runtime.rangeLimits.hour[Data::rangeBegin]; + i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; ++i) { pValuesForTheCurrentYear[numSpace][state.thermalCluster->areaWideIndex].hour[i] diff --git a/src/solver/variable/include/antares/solver/variable/economy/nonProportionalCost.h b/src/solver/variable/include/antares/solver/variable/economy/nonProportionalCost.h index be8436d29a..8d26594739 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/nonProportionalCost.h +++ b/src/solver/variable/include/antares/solver/variable/economy/nonProportionalCost.h @@ -198,8 +198,8 @@ class NonProportionalCost void yearEndBuildForEachThermalCluster(State& state, uint year, unsigned int numSpace) { // Get end year calculations - for (unsigned int i = state.study.runtime->rangeLimits.hour[Data::rangeBegin]; - i <= state.study.runtime->rangeLimits.hour[Data::rangeEnd]; + for (unsigned int i = state.study.runtime.rangeLimits.hour[Data::rangeBegin]; + i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; ++i) { pValuesForTheCurrentYear[numSpace][i] += state diff --git a/src/solver/variable/include/antares/solver/variable/economy/npCostByDispatchablePlant.h b/src/solver/variable/include/antares/solver/variable/economy/npCostByDispatchablePlant.h index b0587d51a2..79304684b3 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/npCostByDispatchablePlant.h +++ b/src/solver/variable/include/antares/solver/variable/economy/npCostByDispatchablePlant.h @@ -236,8 +236,8 @@ class NonProportionalCostByDispatchablePlant void yearEndBuildForEachThermalCluster(State& state, uint year, unsigned int numSpace) { // Get end year calculations - for (unsigned int i = state.study.runtime->rangeLimits.hour[Data::rangeBegin]; - i <= state.study.runtime->rangeLimits.hour[Data::rangeEnd]; + for (unsigned int i = state.study.runtime.rangeLimits.hour[Data::rangeBegin]; + i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; ++i) { pValuesForTheCurrentYear[numSpace][state.thermalCluster->areaWideIndex].hour[i] diff --git a/src/solver/variable/include/antares/solver/variable/economy/operatingCost.h b/src/solver/variable/include/antares/solver/variable/economy/operatingCost.h index 0df88e6711..df95f6d65c 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/operatingCost.h +++ b/src/solver/variable/include/antares/solver/variable/economy/operatingCost.h @@ -196,8 +196,8 @@ class OperatingCost: public Variable::IVariable, NextT, VCa void yearEndBuildForEachThermalCluster(State& state, uint year, unsigned int numSpace) { // Get end year calculations - for (unsigned int i = state.study.runtime->rangeLimits.hour[Data::rangeBegin]; - i <= state.study.runtime->rangeLimits.hour[Data::rangeEnd]; + for (unsigned int i = state.study.runtime.rangeLimits.hour[Data::rangeBegin]; + i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; ++i) { pValuesForTheCurrentYear[numSpace][i] += state.thermalClusterOperatingCostForYear[i]; diff --git a/src/solver/variable/include/antares/solver/variable/economy/overallCost.h b/src/solver/variable/include/antares/solver/variable/economy/overallCost.h index 43c13f6620..ccb6632e9e 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/overallCost.h +++ b/src/solver/variable/include/antares/solver/variable/economy/overallCost.h @@ -194,8 +194,8 @@ class OverallCost: public Variable::IVariable, NextT, VCardOv void yearEndBuildForEachThermalCluster(State& state, uint year, unsigned int numSpace) { // Get end year calculations - for (unsigned int i = state.study.runtime->rangeLimits.hour[Data::rangeBegin]; - i <= state.study.runtime->rangeLimits.hour[Data::rangeEnd]; + for (unsigned int i = state.study.runtime.rangeLimits.hour[Data::rangeBegin]; + i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; ++i) { pValuesForTheCurrentYear[numSpace][i] += state.thermalClusterOperatingCostForYear[i]; diff --git a/src/solver/variable/include/antares/solver/variable/economy/productionByDispatchablePlant.h b/src/solver/variable/include/antares/solver/variable/economy/productionByDispatchablePlant.h index 2e401844eb..b54245b644 100644 --- a/src/solver/variable/include/antares/solver/variable/economy/productionByDispatchablePlant.h +++ b/src/solver/variable/include/antares/solver/variable/economy/productionByDispatchablePlant.h @@ -255,7 +255,7 @@ class ProductionByDispatchablePlant uint year, unsigned int numSpace) { - for (unsigned int i = 0; i <= state.study.runtime->rangeLimits.hour[Data::rangeEnd]; ++i) + for (unsigned int i = 0; i <= state.study.runtime.rangeLimits.hour[Data::rangeEnd]; ++i) { state.thermalClusterProductionForYear[i] += pValuesForTheCurrentYear [numSpace] diff --git a/src/solver/variable/include/antares/solver/variable/storage/average.h b/src/solver/variable/include/antares/solver/variable/storage/average.h index 380915e284..fbeaf16a5b 100644 --- a/src/solver/variable/include/antares/solver/variable/storage/average.h +++ b/src/solver/variable/include/antares/solver/variable/storage/average.h @@ -116,7 +116,7 @@ struct Average: public NextT avgdata.monthly); break; case Category::annual: - InternalExportValues<1, VCardT, Category::annual>(report, avgdata.year); + InternalExportValues<1, VCardT, Category::annual>(report, avgdata.year.data()); break; } } diff --git a/src/solver/variable/include/antares/solver/variable/storage/averagedata.h b/src/solver/variable/include/antares/solver/variable/storage/averagedata.h index 0ed11ae239..132b70317d 100644 --- a/src/solver/variable/include/antares/solver/variable/storage/averagedata.h +++ b/src/solver/variable/include/antares/solver/variable/storage/averagedata.h @@ -62,7 +62,7 @@ class AverageData double weekly[WEEKS_PER_YEAR]; double daily[DAYS_PER_YEAR]; Antares::Memory::Stored::Type hourly; - double* year; + std::vector year; unsigned int nbYearsCapacity; mutable double allYears; // FIX MEEE - Remove the mutable as soon as possible std::vector yearsWeight; diff --git a/src/solver/variable/include/antares/solver/variable/storage/raw.h b/src/solver/variable/include/antares/solver/variable/storage/raw.h index e71356953f..fe6c444840 100644 --- a/src/solver/variable/include/antares/solver/variable/storage/raw.h +++ b/src/solver/variable/include/antares/solver/variable/storage/raw.h @@ -120,7 +120,7 @@ struct Raw: public NextT rawdata.monthly); break; case Category::annual: - InternalExportValues(report, rawdata.year); + InternalExportValues(report, rawdata.year.data()); break; } } diff --git a/src/solver/variable/include/antares/solver/variable/storage/rawdata.h b/src/solver/variable/include/antares/solver/variable/storage/rawdata.h index 6a2c118c05..a0c60cc89d 100644 --- a/src/solver/variable/include/antares/solver/variable/storage/rawdata.h +++ b/src/solver/variable/include/antares/solver/variable/storage/rawdata.h @@ -60,7 +60,7 @@ class RawData double weekly[WEEKS_PER_YEAR]; double daily[DAYS_PER_YEAR]; Antares::Memory::Stored::Type hourly; - double* year; + std::vector year; mutable double allYears; unsigned int nbYearsCapacity; diff --git a/src/solver/variable/state.cpp b/src/solver/variable/state.cpp index ca8d5b2677..6657a5a23b 100644 --- a/src/solver/variable/state.cpp +++ b/src/solver/variable/state.cpp @@ -230,9 +230,9 @@ void State::yearEndBuildFromThermalClusterIndex(const uint clusterAreaWideIndex) uint maxDurationON; // nombre d'heures de fonctionnement d'un groupe au delà duquel un // arrêt/redémarrage est préférable uint maxUnitNeeded = 0; - uint startHourForCurrentYear = study.runtime->rangeLimits.hour[Data::rangeBegin]; + uint startHourForCurrentYear = study.runtime.rangeLimits.hour[Data::rangeBegin]; uint endHourForCurrentYear = startHourForCurrentYear - + study.runtime->rangeLimits.hour[Data::rangeCount]; + + study.runtime.rangeLimits.hour[Data::rangeCount]; assert(endHourForCurrentYear <= HOURS_PER_YEAR); @@ -377,9 +377,9 @@ void State::yearEndBuildThermalClusterCalculateStartupCosts( const std::array& ON_opt, const Data::ThermalCluster* currentCluster) { - uint startHourForCurrentYear = study.runtime->rangeLimits.hour[Data::rangeBegin]; + uint startHourForCurrentYear = study.runtime.rangeLimits.hour[Data::rangeBegin]; uint endHourForCurrentYear = startHourForCurrentYear - + study.runtime->rangeLimits.hour[Data::rangeCount]; + + study.runtime.rangeLimits.hour[Data::rangeCount]; for (uint hour = startHourForCurrentYear; hour < endHourForCurrentYear; ++hour) { @@ -427,9 +427,9 @@ std::array State::computeEconomicallyOptimalNbClustersONfo const std::array& ON_min, const std::array& ON_max) const { - uint startHourForCurrentYear = study.runtime->rangeLimits.hour[Data::rangeBegin]; + uint startHourForCurrentYear = study.runtime.rangeLimits.hour[Data::rangeBegin]; uint endHourForCurrentYear = startHourForCurrentYear - + study.runtime->rangeLimits.hour[Data::rangeCount]; + + study.runtime.rangeLimits.hour[Data::rangeCount]; // Nombre de groupes économiquement optimal en fonctionnement à l'heure h std::array ON_opt; diff --git a/src/solver/variable/storage/averagedata.cpp b/src/solver/variable/storage/averagedata.cpp index 4a88945bb6..fac0080bf1 100644 --- a/src/solver/variable/storage/averagedata.cpp +++ b/src/solver/variable/storage/averagedata.cpp @@ -31,7 +31,6 @@ namespace Antares::Solver::Variable::R::AllYears { AverageData::AverageData(): hourly(nullptr), - year(nullptr), nbYearsCapacity(0), allYears(0.) { @@ -40,7 +39,6 @@ AverageData::AverageData(): AverageData::~AverageData() { Antares::Memory::Release(hourly); - delete[] year; } void AverageData::reset() @@ -49,14 +47,14 @@ void AverageData::reset() (void)::memset(monthly, 0, sizeof(double) * MONTHS_PER_YEAR); (void)::memset(weekly, 0, sizeof(double) * WEEKS_PER_YEAR); (void)::memset(daily, 0, sizeof(double) * DAYS_PER_YEAR); - (void)::memset(year, 0, sizeof(double) * nbYearsCapacity); + year.assign(nbYearsCapacity, 0); } void AverageData::initializeFromStudy(Data::Study& study) { Antares::Memory::Allocate(hourly, HOURS_PER_YEAR); - nbYearsCapacity = study.runtime->rangeLimits.year[Data::rangeEnd] + 1; - year = new double[nbYearsCapacity]; + nbYearsCapacity = study.runtime.rangeLimits.year[Data::rangeEnd] + 1; + year.resize(nbYearsCapacity); yearsWeight = study.parameters.getYearsWeight(); yearsWeightSum = study.parameters.getYearsWeightSum(); diff --git a/src/solver/variable/storage/intermediate.cpp b/src/solver/variable/storage/intermediate.cpp index 4799085aaf..a796b9676d 100644 --- a/src/solver/variable/storage/intermediate.cpp +++ b/src/solver/variable/storage/intermediate.cpp @@ -44,9 +44,9 @@ IntermediateValues::IntermediateValues(): void IntermediateValues::initializeFromStudy(Data::Study& study) { - pRange = &study.runtime->rangeLimits; + pRange = &study.runtime.rangeLimits; calendar = &study.calendarOutput; - pRuntimeInfo = study.runtime; + pRuntimeInfo = &study.runtime; } void IntermediateValues::computeStatisticsAdequacyForTheCurrentYear() diff --git a/src/solver/variable/storage/rawdata.cpp b/src/solver/variable/storage/rawdata.cpp index 4baf8531bd..a1b4c2fecf 100644 --- a/src/solver/variable/storage/rawdata.cpp +++ b/src/solver/variable/storage/rawdata.cpp @@ -29,7 +29,6 @@ namespace Antares::Solver::Variable::R::AllYears { RawData::RawData(): hourly(nullptr), - year(nullptr), allYears(0.) { } @@ -37,14 +36,13 @@ RawData::RawData(): RawData::~RawData() { Antares::Memory::Release(hourly); - delete[] year; } void RawData::initializeFromStudy(const Data::Study& study) { Antares::Memory::Allocate(hourly, HOURS_PER_YEAR); - nbYearsCapacity = study.runtime->rangeLimits.year[Data::rangeEnd] + 1; - year = new double[nbYearsCapacity]; + nbYearsCapacity = study.runtime.rangeLimits.year[Data::rangeEnd] + 1; + year.resize(nbYearsCapacity); } void RawData::reset() @@ -54,7 +52,7 @@ void RawData::reset() (void)::memset(monthly, 0, sizeof(double) * MONTHS_PER_YEAR); (void)::memset(weekly, 0, sizeof(double) * WEEKS_PER_YEAR); (void)::memset(daily, 0, sizeof(double) * DAYS_PER_YEAR); - (void)::memset(year, 0, sizeof(double) * nbYearsCapacity); + year.assign(nbYearsCapacity, 0); } void RawData::merge(unsigned int y, const IntermediateValues& rhs) diff --git a/src/solver/variable/surveyresults/surveyresults.cpp b/src/solver/variable/surveyresults/surveyresults.cpp index bb763a1b80..8df87678e4 100644 --- a/src/solver/variable/surveyresults/surveyresults.cpp +++ b/src/solver/variable/surveyresults/surveyresults.cpp @@ -231,17 +231,16 @@ namespace Variable { static inline uint GetRangeLimit(const Data::Study& study, int precisionLevel, int index) { - assert(study.runtime && "invalid runtime data"); switch (precisionLevel) { case Category::hourly: - return study.runtime->rangeLimits.hour[index]; + return study.runtime.rangeLimits.hour[index]; case Category::daily: - return study.runtime->rangeLimits.day[index]; + return study.runtime.rangeLimits.day[index]; case Category::weekly: - return study.runtime->rangeLimits.week[index]; + return study.runtime.rangeLimits.week[index]; case Category::monthly: - return study.runtime->rangeLimits.month[index]; + return study.runtime.rangeLimits.month[index]; case Category::annual: return 0; default: @@ -357,13 +356,10 @@ inline void SurveyResults::AppendDoubleValue(uint& error, if (std::isnan(v)) { buffer.append("\tNaN", 4); - if (++error == 1) + // We should disabled errors on NaN if the quadratic optimization has failed + if (++error == 1 && !data.study.runtime.quadraticOptimizationHasFailed) { - // We should disabled errors on NaN if the quadratic optimization has failed - if (not data.study.runtime->quadraticOptimizationHasFailed) - { - logs.error() << "'NaN' value detected"; - } + logs.error() << "'NaN' value detected"; } } else @@ -608,7 +604,7 @@ void SurveyResults::exportDigestAllYears(std::string& buffer) { // Main Header { - const unsigned int nbLinks = data.study.runtime->interconnectionsCount(); + const unsigned int nbLinks = data.study.runtime.interconnectionsCount(); buffer.append("\tdigest\n\tVARIABLES\tAREAS\tLINKS\n") .append("\t") .append(std::to_string(data.columnIndex)) diff --git a/src/tests/end-to-end/simple_study/simple-study.cpp b/src/tests/end-to-end/simple_study/simple-study.cpp index 7005057209..2664dc97d0 100644 --- a/src/tests/end-to-end/simple_study/simple-study.cpp +++ b/src/tests/end-to-end/simple_study/simple-study.cpp @@ -308,7 +308,8 @@ BOOST_FIXTURE_TEST_CASE(STS_initial_level_is_also_weekly_final_level, StudyFixtu props.injectionNominalCapacity = 10; props.withdrawalNominalCapacity = 10; props.reservoirCapacity = 100; - props.efficiencyFactor = .9; + props.injectionEfficiency = .9; + props.withdrawalEfficiency = .8; props.initialLevel = .443; props.groupName = std::string("Some STS group"); // Default values for series @@ -317,17 +318,16 @@ BOOST_FIXTURE_TEST_CASE(STS_initial_level_is_also_weekly_final_level, StudyFixtu storages.push_back(sts); // Fatal gen at h=1 + auto& windTS = area->wind.series.timeSeries; + TimeSeriesConfigurer(windTS).setColumnCount(1).fillColumnWith(0, 0.); + windTS[0][1] = 100; + + // Fatal load at h=2-10 + auto& loadTS = area->load.series.timeSeries; + TimeSeriesConfigurer(loadTS).setColumnCount(1).fillColumnWith(0, 0.); + for (int i = 2; i < 10; i++) { - auto& windTS = area->wind.series.timeSeries; - TimeSeriesConfigurer(windTS).setColumnCount(1).fillColumnWith(0, 0.); - windTS[0][1] = 100; - } - - // Fatal load at h=2 - { - auto& loadTS = area->load.series.timeSeries; - TimeSeriesConfigurer(loadTS).setColumnCount(1).fillColumnWith(0, 0.); - loadTS[0][2] = 100; + loadTS[0][i] = 100; } // Usual values, avoid spillage & unsupplied energy @@ -343,6 +343,51 @@ BOOST_FIXTURE_TEST_CASE(STS_initial_level_is_also_weekly_final_level, StudyFixtu == props.initialLevel * props.reservoirCapacity.value(), tt::tolerance(0.001)); } + +BOOST_FIXTURE_TEST_CASE(STS_efficiency_for_injection_and_withdrawal, StudyFixture) +{ + using namespace Antares::Data::ShortTermStorage; + setNumberMCyears(1); + auto& storages = area->shortTermStorage.storagesByIndex; + STStorageCluster sts; + auto& props = sts.properties; + props.name = "my-sts"; + props.injectionNominalCapacity = 10; + props.withdrawalNominalCapacity = 10; + props.reservoirCapacity = 100; + props.injectionEfficiency = .6; + props.withdrawalEfficiency = .8; + props.initialLevel = .5; + props.groupName = std::string("Some STS group"); + // Default values for series + sts.series->fillDefaultSeriesIfEmpty(); + + storages.push_back(sts); + + // Fatal gen at h=1 + auto& windTS = area->wind.series.timeSeries; + TimeSeriesConfigurer(windTS).setColumnCount(1).fillColumnWith(0, 0.); + windTS[0][1] = 100; + + // Fatal load at h=2 + auto& loadTS = area->load.series.timeSeries; + TimeSeriesConfigurer(loadTS).setColumnCount(1).fillColumnWith(0, 0.); + loadTS[0][2] = 100; + + // Usual values, avoid spillage & unsupplied energy + area->thermal.unsuppliedEnergyCost = 1.e3; + area->thermal.spilledEnergyCost = 1.; + + simulation->create(); + simulation->run(); + + unsigned int groupNb = 0; // Used to reach the first group of STS results + OutputRetriever output(simulation->rawSimu()); + + BOOST_CHECK_EQUAL(output.levelForSTSgroup(area, groupNb).hour(1), 56); // injection + BOOST_CHECK_EQUAL(output.levelForSTSgroup(area, groupNb).hour(2), 48); // withdrawal +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(HYDRO_MAX_POWER) diff --git a/src/tests/inmemory-study/in-memory-study.cpp b/src/tests/inmemory-study/in-memory-study.cpp index 30efd246bf..1f3f78c194 100644 --- a/src/tests/inmemory-study/in-memory-study.cpp +++ b/src/tests/inmemory-study/in-memory-study.cpp @@ -56,7 +56,7 @@ void addScratchpadToEachArea(Study& study) { for (unsigned int i = 0; i < study.maxNbYearsInParallel; ++i) { - area->scratchpad.emplace_back(*study.runtime, *area); + area->scratchpad.emplace_back(study.runtime, *area); } } } diff --git a/src/tests/src/api_internal/test_api.cpp b/src/tests/src/api_internal/test_api.cpp index d60f6315d5..fe006c9bd6 100644 --- a/src/tests/src/api_internal/test_api.cpp +++ b/src/tests/src/api_internal/test_api.cpp @@ -44,8 +44,8 @@ class InMemoryStudyLoader: public Antares::IStudyLoader StudyBuilder builder; builder.addAreaToStudy("area1"); builder.addAreaToStudy("area2"); - builder.setNumberMCyears(1); builder.study->initializeRuntimeInfos(); + builder.setNumberMCyears(1); builder.study->parameters.resultFormat = ResultFormat::inMemory; builder.study->prepareOutput(); return std::move(builder.study); diff --git a/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp b/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp index 83965049e6..99152e42d0 100644 --- a/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp +++ b/src/tests/src/libs/antares/study/short-term-storage-input/short-term-storage-input-output.cpp @@ -115,6 +115,7 @@ void createIniFile(bool enabled) outfile << "withdrawalnominalcapacity = 900.000000" << std::endl; outfile << "reservoircapacity = 31200.000000" << std::endl; outfile << "efficiency = 0.75" << std::endl; + outfile << "efficiencywithdrawal = 0.9" << std::endl; outfile << "initiallevel = 0.50000" << std::endl; outfile << "enabled = " << (enabled ? "true" : "false") << std::endl; outfile.close(); @@ -134,6 +135,7 @@ void createIniFileWrongValue() outfile << "withdrawalnominalcapacity = -900.000000" << std::endl; outfile << "reservoircapacity = -31200.000000" << std::endl; outfile << "efficiency = 4" << std::endl; + outfile << "efficiencywithdrawal = -2" << std::endl; outfile << "initiallevel = -0.50000" << std::endl; outfile.close(); diff --git a/src/tests/src/libs/antares/study/test_study.cpp b/src/tests/src/libs/antares/study/test_study.cpp index 104b304c0e..0ecb082cd1 100644 --- a/src/tests/src/libs/antares/study/test_study.cpp +++ b/src/tests/src/libs/antares/study/test_study.cpp @@ -139,7 +139,7 @@ BOOST_FIXTURE_TEST_CASE(short_term_storage_delete, OneAreaStudy) BOOST_CHECK(findDisabledCluster("Cluster1") != sts.end()); BOOST_CHECK(findDisabledCluster("Cluster2") != sts.end()); - study->initializeRuntimeInfos(); // This should remove all disabled short-term storages + study->initializeRuntimeInfos(); // Check that only "Cluster1" is found BOOST_CHECK(findDisabledCluster("Cluster1") != sts.end()); diff --git a/src/tests/src/solver/infeasible-problem-analysis/test-unfeasible-problem-analyzer.cpp b/src/tests/src/solver/infeasible-problem-analysis/test-unfeasible-problem-analyzer.cpp index 321bd4b3e7..20086d505f 100644 --- a/src/tests/src/solver/infeasible-problem-analysis/test-unfeasible-problem-analyzer.cpp +++ b/src/tests/src/solver/infeasible-problem-analysis/test-unfeasible-problem-analyzer.cpp @@ -135,48 +135,51 @@ BOOST_AUTO_TEST_CASE(analysis_should_detect_inconsistent_variable_bounds) /*! * Creates a problem with 2 variables linked by 1 constraint: * - Variable 1 must be greater than 1 - * - Variable 2 must be lesser than -1 + * - Variable 2 must be smaller than -1 * - but if feasible is false, constraint enforces that variable 2 is greater than variable 1 --> * infeasible */ -std::unique_ptr createProblem(const std::string& constraintName, bool feasible) + +std::unique_ptr createProblem(const std::string& constraintName) { std::unique_ptr problem(MPSolver::CreateSolver("GLOP")); const double infinity = problem->infinity(); - auto var1 = problem->MakeNumVar(1, infinity, "var1"); - auto var2 = problem->MakeNumVar(-infinity, -1, "var2"); + problem->MakeNumVar(1, infinity, "var1"); + problem->MakeNumVar(-infinity, -1, "var2"); auto constraint = problem->MakeRowConstraint(constraintName); constraint->SetBounds(0, infinity); - if (feasible) - { - constraint->SetCoefficient(var1, 1); - constraint->SetCoefficient(var2, -1); - } - else - { - constraint->SetCoefficient(var1, -1); - constraint->SetCoefficient(var2, 1); - } return problem; } std::unique_ptr createFeasibleProblem(const std::string& constraintName) { - return createProblem(constraintName, true); + auto problem = createProblem(constraintName); + auto constraint = problem->LookupConstraintOrNull(constraintName); + auto var1 = problem->LookupVariableOrNull("var1"); + auto var2 = problem->LookupVariableOrNull("var2"); + constraint->SetCoefficient(var1, 1); + constraint->SetCoefficient(var2, -1); + return problem; } std::unique_ptr createUnfeasibleProblem(const std::string& constraintName) { - return createProblem(constraintName, false); + auto problem = createProblem(constraintName); + auto constraint = problem->LookupConstraintOrNull(constraintName); + auto var1 = problem->LookupVariableOrNull("var1"); + auto var2 = problem->LookupVariableOrNull("var2"); + constraint->SetCoefficient(var1, -1); + constraint->SetCoefficient(var2, 1); + return problem; } -static const std::string validConstraintNames[] = { - "BC::hourly::hour<36>", - "BC::daily::day<67>", - "BC::weekly::week<12>", - "FictiveLoads::hour<25>", - "AreaHydroLevel::hour<8>", -}; +static const std::string validConstraintNames[] = {"BC-name-1::hourly::hour<36>", + "BC-name-2::daily::day<67>", + "BC-name-3::weekly::week<12>", + "FictiveLoads::area::hour<25>", + "AreaHydroLevel::area::hour<8>", + "Level::area::hour<28>", + "HydroPower::area::week<45>"}; BOOST_DATA_TEST_CASE(analysis_should_detect_unfeasible_constraint, bdata::make(validConstraintNames), diff --git a/src/tests/src/solver/simulation/test-hydro-final-reservoir-level-functions.cpp b/src/tests/src/solver/simulation/test-hydro-final-reservoir-level-functions.cpp index e426d99da4..9eeeaf9618 100644 --- a/src/tests/src/solver/simulation/test-hydro-final-reservoir-level-functions.cpp +++ b/src/tests/src/solver/simulation/test-hydro-final-reservoir-level-functions.cpp @@ -7,9 +7,10 @@ #include #include +#include "antares/solver/hydro/management/HydroErrorsCollector.h" +#include "antares/solver/hydro/management/finalLevelValidator.h" -#include "include/antares/solver/hydro/management/hydro-final-reservoir-level-functions.h" -#include "include/antares/study/parts/hydro/finalLevelValidator.h" +#include "include/antares/solver/hydro/management/HydroInputsChecker.h" using namespace Antares::Solver; using namespace Antares::Data; @@ -105,6 +106,7 @@ struct Fixture Study::Ptr study = std::make_shared(); Area* area_1; Area* area_2; + HydroErrorsCollector hydro_errors_collector; }; BOOST_FIXTURE_TEST_SUITE(final_level_validator, Fixture) @@ -119,7 +121,8 @@ BOOST_AUTO_TEST_CASE(all_parameters_good___check_succeeds_and_final_level_is_usa study->scenarioFinalHydroLevels[area_1->index][year], year, study->parameters.simulationDays.end, - study->parameters.firstMonthInYear); + study->parameters.firstMonthInYear, + hydro_errors_collector); BOOST_CHECK_EQUAL(validator.check(), true); BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), true); @@ -136,7 +139,8 @@ BOOST_AUTO_TEST_CASE(no_reservoir_management___check_succeeds_but_final_level_no study->scenarioFinalHydroLevels[area_1->index][year], year, study->parameters.simulationDays.end, - study->parameters.firstMonthInYear); + study->parameters.firstMonthInYear, + hydro_errors_collector); BOOST_CHECK_EQUAL(validator.check(), true); BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); @@ -154,7 +158,8 @@ BOOST_AUTO_TEST_CASE(use_water_value_is_true___check_succeeds_but_final_level_no study->scenarioFinalHydroLevels[area_1->index][year], year, study->parameters.simulationDays.end, - study->parameters.firstMonthInYear); + study->parameters.firstMonthInYear, + hydro_errors_collector); BOOST_CHECK_EQUAL(validator.check(), true); BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); @@ -172,7 +177,8 @@ BOOST_AUTO_TEST_CASE(final_level_not_set_by_user____check_succeeds_but_final_lev study->scenarioFinalHydroLevels[area_1->index][year], year, study->parameters.simulationDays.end, - study->parameters.firstMonthInYear); + study->parameters.firstMonthInYear, + hydro_errors_collector); BOOST_CHECK_EQUAL(validator.check(), true); BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); @@ -191,7 +197,8 @@ BOOST_AUTO_TEST_CASE( study->scenarioFinalHydroLevels[area_1->index][year], year, study->parameters.simulationDays.end, - study->parameters.firstMonthInYear); + study->parameters.firstMonthInYear, + hydro_errors_collector); BOOST_CHECK_EQUAL(validator.check(), false); BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); @@ -209,7 +216,8 @@ BOOST_AUTO_TEST_CASE(simulation_does_last_a_whole_year___check_fails_and_final_l study->scenarioFinalHydroLevels[area_1->index][year], year, study->parameters.simulationDays.end, - study->parameters.firstMonthInYear); + study->parameters.firstMonthInYear, + hydro_errors_collector); BOOST_CHECK_EQUAL(validator.check(), false); BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); @@ -228,7 +236,8 @@ BOOST_AUTO_TEST_CASE(final_level_out_of_rule_curves___check_fails_and_final_leve study->scenarioFinalHydroLevels[area_1->index][year], year, study->parameters.simulationDays.end, - study->parameters.firstMonthInYear); + study->parameters.firstMonthInYear, + hydro_errors_collector); BOOST_CHECK_EQUAL(validator.check(), false); BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); @@ -251,7 +260,8 @@ BOOST_AUTO_TEST_CASE( study->scenarioFinalHydroLevels[area_1->index][year], year, study->parameters.simulationDays.end, - study->parameters.firstMonthInYear); + study->parameters.firstMonthInYear, + hydro_errors_collector); BOOST_CHECK_EQUAL(validator.check(), false); BOOST_CHECK_EQUAL(validator.finalLevelFineForUse(), false); @@ -259,13 +269,11 @@ BOOST_AUTO_TEST_CASE( BOOST_AUTO_TEST_CASE(check_all_areas_final_levels_when_config_is_ok___all_checks_succeed) { + HydroInputsChecker hydro_input_checker(*study); + for (uint year: {0, 1}) { - CheckFinalReservoirLevelsConfiguration(study->areas, - study->parameters, - study->scenarioInitialHydroLevels, - study->scenarioFinalHydroLevels, - year); + hydro_input_checker.CheckFinalReservoirLevelsConfiguration(year); } // CheckFinalReservoirLevelsConfiguration(*study, 0); // CheckFinalReservoirLevelsConfiguration(*study, 1); diff --git a/src/tests/src/solver/simulation/test-store-timeseries-number.cpp b/src/tests/src/solver/simulation/test-store-timeseries-number.cpp index 16cbb59fc2..1e68ef95be 100644 --- a/src/tests/src/solver/simulation/test-store-timeseries-number.cpp +++ b/src/tests/src/solver/simulation/test-store-timeseries-number.cpp @@ -44,10 +44,9 @@ void initializeStudy(Study& study) { study.parameters.derated = false; - study.runtime = new StudyRuntimeInfos(); - study.runtime->rangeLimits.year[rangeBegin] = 0; - study.runtime->rangeLimits.year[rangeEnd] = 0; - study.runtime->rangeLimits.year[rangeCount] = 1; + study.runtime.rangeLimits.year[rangeBegin] = 0; + study.runtime.rangeLimits.year[rangeEnd] = 0; + study.runtime.rangeLimits.year[rangeCount] = 1; study.parameters.renewableGeneration.toAggregated(); // Default diff --git a/src/tests/src/solver/simulation/test-time_series.cpp b/src/tests/src/solver/simulation/test-time_series.cpp index 42f1fbddf4..b999c17b48 100644 --- a/src/tests/src/solver/simulation/test-time_series.cpp +++ b/src/tests/src/solver/simulation/test-time_series.cpp @@ -40,10 +40,9 @@ void initializeStudy(Study& study) { study.parameters.derated = false; - study.runtime = new StudyRuntimeInfos(); - study.runtime->rangeLimits.year[rangeBegin] = 0; - study.runtime->rangeLimits.year[rangeEnd] = 0; - study.runtime->rangeLimits.year[rangeCount] = 1; + study.runtime.rangeLimits.year[rangeBegin] = 0; + study.runtime.rangeLimits.year[rangeEnd] = 0; + study.runtime.rangeLimits.year[rangeCount] = 1; study.parameters.renewableGeneration.toAggregated(); // Default diff --git a/src/tests/src/solver/simulation/tests-ts-numbers.cpp b/src/tests/src/solver/simulation/tests-ts-numbers.cpp index 9767cbe525..8dbd450d53 100644 --- a/src/tests/src/solver/simulation/tests-ts-numbers.cpp +++ b/src/tests/src/solver/simulation/tests-ts-numbers.cpp @@ -38,9 +38,8 @@ void initializeStudy(Study::Ptr study, unsigned int nbYears = 1) { study->parameters.derated = false; - study->runtime = new StudyRuntimeInfos(); - study->runtime->rangeLimits.year[rangeBegin] = 0; - study->runtime->rangeLimits.year[rangeEnd] = nbYears - 1; + study->runtime.rangeLimits.year[rangeBegin] = 0; + study->runtime.rangeLimits.year[rangeEnd] = nbYears - 1; study->parameters.renewableGeneration.toAggregated(); // Default @@ -130,7 +129,7 @@ BOOST_AUTO_TEST_CASE(two_areas_with_5_ready_made_ts_on_load___check_intra_modal_ Area* area_1 = addAreaToStudy(study, "Area 1"); Area* area_2 = addAreaToStudy(study, "Area 2"); - study->areas.resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + study->areas.resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); area_1->load.series.timeSeries.resize(5, 1); area_2->load.series.timeSeries.resize(5, 1); @@ -157,7 +156,7 @@ static bool intramodal_load_two_areas(unsigned width_area_1, unsigned width_area Area* area_1 = addAreaToStudy(study, "Area 1"); Area* area_2 = addAreaToStudy(study, "Area 2"); - study->areas.resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + study->areas.resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); area_1->load.series.timeSeries.resize(width_area_1, 1); area_2->load.series.timeSeries.resize(width_area_2, 1); @@ -199,7 +198,7 @@ BOOST_AUTO_TEST_CASE( auto thCluster_21 = addClusterToArea(area_2, "th-cluster-21"); thCluster_21->series.timeSeries.resize(4, 1); - study->areas.resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + study->areas.resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); BOOST_CHECK(Generate(*study)); @@ -231,7 +230,7 @@ BOOST_AUTO_TEST_CASE( auto thCluster_21 = addClusterToArea(area_2, "th-cluster-21"); thCluster_21->series.timeSeries.resize(4, 1); - study->areas.resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + study->areas.resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); BOOST_CHECK(Generate(*study)); @@ -263,7 +262,7 @@ BOOST_AUTO_TEST_CASE( auto thCluster_21 = addClusterToArea(area_2, "th-cluster-21"); thCluster_21->series.timeSeries.resize(3, 1); - study->areas.resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + study->areas.resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); BOOST_CHECK(not Generate(*study)); } @@ -291,7 +290,7 @@ BOOST_AUTO_TEST_CASE( auto rnCluster_21 = addClusterToArea(area_2, "rn-cluster-21"); rnCluster_21->series.timeSeries.resize(4, 1); - study->areas.resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + study->areas.resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); BOOST_CHECK(Generate(*study)); @@ -323,7 +322,7 @@ BOOST_AUTO_TEST_CASE( auto rnCluster_21 = addClusterToArea(area_2, "rn-cluster-21"); rnCluster_21->series.timeSeries.resize(4, 1); - study->areas.resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + study->areas.resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); BOOST_CHECK(Generate(*study)); @@ -357,7 +356,7 @@ BOOST_AUTO_TEST_CASE( auto rnCluster_21 = addClusterToArea(area_2, "rn-cluster-21"); rnCluster_21->series.timeSeries.resize(4, 1); - study->areas.resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + study->areas.resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); BOOST_CHECK(not Generate(*study)); } @@ -389,7 +388,7 @@ BOOST_AUTO_TEST_CASE( auto thCluster_1 = addClusterToArea(area, "th-cluster-1"); auto thCluster_2 = addClusterToArea(area, "th-cluster-2"); - area->resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + area->resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); TSGenerator::ResizeGeneratedTimeSeries(study->areas, study->parameters); BOOST_CHECK(Generate(*study)); @@ -425,7 +424,7 @@ BOOST_AUTO_TEST_CASE( auto thCluster_1 = addClusterToArea(area, "th-cluster-1"); auto thCluster_2 = addClusterToArea(area, "th-cluster-2"); - area->resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + area->resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); TSGenerator::ResizeGeneratedTimeSeries(study->areas, study->parameters); BOOST_CHECK(Generate(*study)); @@ -461,7 +460,7 @@ BOOST_AUTO_TEST_CASE( auto thCluster_1 = addClusterToArea(area, "th-cluster-1"); auto thCluster_2 = addClusterToArea(area, "th-cluster-2"); - area->resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + area->resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); TSGenerator::ResizeGeneratedTimeSeries(study->areas, study->parameters); BOOST_CHECK(not Generate(*study)); @@ -486,7 +485,7 @@ BOOST_AUTO_TEST_CASE( auto rnCluster_1 = addClusterToArea(area, "rn-cluster-1"); rnCluster_1->series.timeSeries.resize(5, 1); - area->resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + area->resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); BOOST_CHECK(Generate(*study)); @@ -515,7 +514,7 @@ BOOST_AUTO_TEST_CASE( auto rnCluster_1 = addClusterToArea(area, "rn-cluster-1"); rnCluster_1->series.timeSeries.resize(4, 1); - area->resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + area->resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); BOOST_CHECK(not Generate(*study)); } @@ -539,7 +538,7 @@ BOOST_AUTO_TEST_CASE( auto rnCluster_1 = addClusterToArea(area, "rn-cluster-1"); rnCluster_1->series.timeSeries.resize(1, 1); - area->resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + area->resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); BOOST_CHECK(Generate(*study)); } @@ -581,7 +580,7 @@ BOOST_AUTO_TEST_CASE(load_wind_thermal_in_intra_and_inter_modal____check_all_ts_ area_2->wind.series.timeSeries.resize(5, 1); // Ready made TS for wind auto thCluster_area_2 = addClusterToArea(area_2, "th-cluster-area-2"); - study->areas.resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + study->areas.resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); TSGenerator::ResizeGeneratedTimeSeries(study->areas, study->parameters); BOOST_CHECK(Generate(*study)); @@ -629,14 +628,14 @@ BOOST_AUTO_TEST_CASE(check_all_drawn_ts_numbers_are_bounded_between_0_and_nb_of_ auto thCluster = addClusterToArea(area, "th-cluster"); - area->resizeAllTimeseriesNumbers(1 + study->runtime->rangeLimits.year[rangeEnd]); + area->resizeAllTimeseriesNumbers(1 + study->runtime.rangeLimits.year[rangeEnd]); auto bc = study->bindingConstraints.add("dummy"); bc->group("dummy"); study->bindingConstraintsGroups.add(bc->group()); bc->RHSTimeSeries().resize(42, 1); study->bindingConstraintsGroups.resizeAllTimeseriesNumbers( - 1 + study->runtime->rangeLimits.year[rangeEnd]); + 1 + study->runtime.rangeLimits.year[rangeEnd]); TSGenerator::ResizeGeneratedTimeSeries(study->areas, study->parameters); BOOST_CHECK(Generate(*study)); diff --git a/src/tools/ts-generator/CMakeLists.txt b/src/tools/ts-generator/CMakeLists.txt index cea38ae0cd..f037e6f1dd 100644 --- a/src/tools/ts-generator/CMakeLists.txt +++ b/src/tools/ts-generator/CMakeLists.txt @@ -1,5 +1,9 @@ set(SRCS main.cpp + include/antares/tools/ts-generator/linksTSgenerator.h + include/antares/tools/ts-generator/tsGenerationOptions.h + tsGenerationOptions.cpp + linksTSgenerator.cpp ) set(execname "antares-ts-generator") @@ -13,15 +17,15 @@ INSTALL(EXPORT ${execname} target_link_libraries(${execname} PRIVATE - Antares::utils - antares-solver-ts-generator - Antares::study - Antares::checks + Antares::utils + antares-solver-ts-generator + Antares::study + Antares::checks ) target_include_directories(${execname} PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/include" + "${CMAKE_CURRENT_SOURCE_DIR}/include" ) import_std_libs(${execname}) diff --git a/src/tools/ts-generator/include/antares/tools/ts-generator/linksTSgenerator.h b/src/tools/ts-generator/include/antares/tools/ts-generator/linksTSgenerator.h new file mode 100644 index 0000000000..1feaac5038 --- /dev/null +++ b/src/tools/ts-generator/include/antares/tools/ts-generator/linksTSgenerator.h @@ -0,0 +1,32 @@ + +#pragma once + +#include +#include "antares/tools/ts-generator/tsGenerationOptions.h" + +namespace fs = std::filesystem; + +namespace Antares::TSGenerator +{ + +class LinksTSgenerator +{ +public: + LinksTSgenerator(Settings&); + void extractData(); + bool generate(); + +private: + LinkPairs extractLinkNamesFromStudy(); + LinkPairs extractLinkNamesFromCmdLine(const LinkPairs&); + StudyParamsForLinkTS readGeneralParamsForLinksTS(); + void extractLinksSpecificTSparameters(); + + std::string linksFromCmdLineOptions_; + fs::path studyFolder_; + bool generateTSforAllLinks_ = false; + std::vector linkList_; + StudyParamsForLinkTS generalParams_; +}; + +} // namespace Antares::TSGenerator diff --git a/src/tools/ts-generator/include/antares/tools/ts-generator/tsGenerationOptions.h b/src/tools/ts-generator/include/antares/tools/ts-generator/tsGenerationOptions.h new file mode 100644 index 0000000000..1d83cce593 --- /dev/null +++ b/src/tools/ts-generator/include/antares/tools/ts-generator/tsGenerationOptions.h @@ -0,0 +1,31 @@ + +#pragma once + +#include + +#include + +namespace Antares::TSGenerator +{ +struct Settings +{ + std::string studyFolder; + + /// generate TS for all clusters if activated + bool allThermal = false; + /// generate TS for a list "area.cluster;area2.cluster2;" + std::string thermalListToGen = ""; + + /// generate TS for all links if activated + bool allLinks = false; + /// generate TS for a list "area.link;area2.link2;" + std::string linksListToGen; +}; + +bool parseOptions(int, char*[], Settings&); +std::unique_ptr createTsGeneratorParser(Settings&); + +bool checkOptions(Settings& options); +bool linkTSrequired(Settings& options); +bool thermalTSrequired(Settings& options); +} // namespace Antares::TSGenerator diff --git a/src/tools/ts-generator/linksTSgenerator.cpp b/src/tools/ts-generator/linksTSgenerator.cpp new file mode 100644 index 0000000000..a200386a7e --- /dev/null +++ b/src/tools/ts-generator/linksTSgenerator.cpp @@ -0,0 +1,347 @@ + +#include "antares/tools/ts-generator/linksTSgenerator.h" + +#include "antares/utils/utils.h" + +namespace Antares::TSGenerator +{ +// ================== +// Free functions +// ================== +std::vector extractTargetAreas(const fs::path& sourceLinkDir) +{ + std::vector to_return; + fs::path pathToIni = sourceLinkDir / "properties.ini"; + IniFile ini; + ini.open(pathToIni); // gp : we should handle reading issues + for (auto s = ini.firstSection; s; s = s->next) + { + to_return.push_back(transformNameIntoID(s->name)); + } + return to_return; +} + +bool pairs_match(const LinkPair& p1, const LinkPair& p2) +{ + return (p1.first == p2.first && p1.second == p2.second) + || (p1.first == p2.second && p1.second == p2.first); +} + +const LinkPair* getMatchingPairInCollection(const LinkPair& pair, const LinkPairs& collection) +{ + for (const auto& p: collection) + { + if (pairs_match(pair, p)) + { + return &p; + } + } + return nullptr; +} + +bool readLinkGeneralProperty(StudyParamsForLinkTS& params, + const Yuni::String& key, + const Yuni::String& value) +{ + if (key == "derated") + { + return value.to(params.derated); + } + if (key == "nbtimeserieslinks") + { + return value.to(params.nbLinkTStoGenerate); + } + if (key == "seed-tsgen-links") + { + unsigned int seed{0}; + if (!value.to(seed)) + { + return false; + } + params.random.reset(seed); + return true; + } + return true; +} + +std::vector CreateLinkList(const LinkPairs& linksFromCmdLine) +{ + std::vector to_return; + std::for_each(linksFromCmdLine.begin(), + linksFromCmdLine.end(), + [&to_return](const auto& link_pair) + { + LinkTSgenerationParams link; + link.namesPair = link_pair; + to_return.push_back(std::move(link)); + }); + return to_return; +} + +LinkTSgenerationParams* findLinkInList(const LinkPair& link_to_find, + std::vector& linkList) +{ + for (auto& link: linkList) + { + if (link.namesPair == link_to_find) + { + return &link; + } + } + return nullptr; +} + +bool readLinkIniProperty(LinkTSgenerationParams* link, + const Yuni::String& key, + const Yuni::String& value) +{ + if (key == "unitcount") + { + return value.to(link->unitCount); + } + + if (key == "nominalcapacity") + { + return value.to(link->nominalCapacity); + } + + if (key == "law.planned") + { + return value.to(link->plannedLaw); + } + + if (key == "law.forced") + { + return value.to(link->forcedLaw); + } + + if (key == "volatility.planned") + { + return value.to(link->plannedVolatility); + } + + if (key == "volatility.forced") + { + return value.to(link->forcedVolatility); + } + + if (key == "force-no-generation") + { + return value.to(link->forceNoGeneration); + } + return true; +} + +void readLinkIniProperties(LinkTSgenerationParams* link, IniFile::Section* section) +{ + for (const IniFile::Property* p = section->firstProperty; p; p = p->next) + { + if (!readLinkIniProperty(link, p->key, p->value)) + { + std::string linkName = link->namesPair.first + "." + link->namesPair.second; + logs.warning() << "Link '" << linkName << "' : reading value of '" << p->key + << "' went wrong"; + link->hasValidData = false; + } + } +} + +void readSourceAreaIniFile(fs::path pathToIni, + std::string sourceAreaName, + std::vector& linkList) +{ + IniFile ini; + ini.open(pathToIni); // gp : we should handle reading problems here + for (auto* section = ini.firstSection; section; section = section->next) + { + std::string targetAreaName = transformNameIntoID(section->name); + const LinkPair processedLink = std::make_pair(sourceAreaName, targetAreaName); + if (auto* foundLink = findLinkInList(processedLink, linkList); foundLink) + { + readLinkIniProperties(foundLink, section); + } + } +} + +void readIniProperties(std::vector& linkList, fs::path toLinksDir) +{ + for (auto& link: linkList) + { + std::string sourceAreaName = link.namesPair.first; + fs::path pathToIni = toLinksDir / sourceAreaName / "properties.ini"; + readSourceAreaIniFile(pathToIni, sourceAreaName, linkList); + } +} + +fs::path makePreproFile(const fs::path& preproFilePath, const std::string& changingEnd) +{ + auto to_return = preproFilePath; + to_return += changingEnd + ".txt"; + return to_return; +} + +bool readLinkPreproTimeSeries(LinkTSgenerationParams& link, fs::path sourceAreaDir) +{ + bool to_return = true; + const auto preproId = link.namesPair.first + "/" + link.namesPair.second; + link.prepro = std::make_unique(preproId, link.unitCount); + + auto preproFileRoot = sourceAreaDir / "prepro" / link.namesPair.second; + + // Testing files existence + auto preproFile = makePreproFile(preproFileRoot, ""); + auto modulationDirectFile = makePreproFile(preproFileRoot, "_mod_direct"); + auto modulationIndirectFile = makePreproFile(preproFileRoot, "_mod_indirect"); + std::vector paths{preproFile, modulationDirectFile, modulationIndirectFile}; + if (std::any_of(paths.begin(), paths.end(), [](auto& path) { return !fs::exists(path); })) + { + link.hasValidData = false; + return false; + } + + // Files loading + to_return = link.prepro->data.loadFromCSVFile(preproFile.string(), + Data::PreproAvailability::preproAvailabilityMax, + DAYS_PER_YEAR) + && link.prepro->validate() && to_return; + + to_return = link.modulationCapacityDirect.loadFromCSVFile(modulationDirectFile.string(), + 1, + HOURS_PER_YEAR) + && to_return; + + to_return = link.modulationCapacityIndirect.loadFromCSVFile(modulationIndirectFile.string(), + 1, + HOURS_PER_YEAR) + && to_return; + + link.hasValidData = link.hasValidData && to_return; + return to_return; +} + +void readPreproTimeSeries(std::vector& linkList, fs::path toLinksDir) +{ + for (auto& link: linkList) + { + std::string sourceAreaName = link.namesPair.first; + fs::path sourceAreaDir = toLinksDir / sourceAreaName; + if (!readLinkPreproTimeSeries(link, sourceAreaDir)) + { + logs.warning() << "Could not load all prepro/modulation data for link '" + << link.namesPair.first << "." << link.namesPair.second << "'"; + } + } +} + +// ================== +// Class methods +// ================== +LinksTSgenerator::LinksTSgenerator(Settings& settings): + linksFromCmdLineOptions_(settings.linksListToGen), + studyFolder_(settings.studyFolder), + generateTSforAllLinks_(settings.allLinks) +{ +} + +void LinksTSgenerator::extractData() +{ + auto allLinksPairs = extractLinkNamesFromStudy(); + + LinkPairs namesLinksToGenerate; + if (generateTSforAllLinks_) + { + namesLinksToGenerate = allLinksPairs; + } + else + { + namesLinksToGenerate = extractLinkNamesFromCmdLine(allLinksPairs); + } + + linkList_ = CreateLinkList(namesLinksToGenerate); + extractLinksSpecificTSparameters(); + + generalParams_ = readGeneralParamsForLinksTS(); +} + +LinkPairs LinksTSgenerator::extractLinkNamesFromStudy() +{ + LinkPairs to_return; + fs::path linksDir = studyFolder_ / "input" / "links"; + for (const auto& item: fs::directory_iterator{linksDir}) + { + if (item.is_directory()) + { + std::string sourceAreaName = item.path().filename().generic_string(); + auto targetAreas = extractTargetAreas(item); + for (auto& targetAreaName: targetAreas) + { + auto linkPair = std::make_pair(sourceAreaName, targetAreaName); + to_return.push_back(linkPair); + } + } + } + return to_return; +} + +LinkPairs LinksTSgenerator::extractLinkNamesFromCmdLine(const LinkPairs& allLinks) +{ + LinkPairs to_return; + LinkPairs pairsFromCmdLine = splitStringIntoPairs(linksFromCmdLineOptions_, ';', '.'); + for (auto& p: pairsFromCmdLine) + { + if (const auto* found_pair = getMatchingPairInCollection(p, allLinks); found_pair) + { + to_return.push_back(*found_pair); + } + else + { + logs.error() << "Link '" << p.first << "." << p.second << "' not found"; + } + } + return to_return; +} + +StudyParamsForLinkTS LinksTSgenerator::readGeneralParamsForLinksTS() +{ + StudyParamsForLinkTS to_return; + fs::path pathToGeneraldata = studyFolder_ / "settings" / "generaldata.ini"; + IniFile ini; + ini.open(pathToGeneraldata); // gp : we should handle reading issues + for (auto* section = ini.firstSection; section; section = section->next) + { + // Skipping sections useless in the current context + Yuni::String sectionName = section->name; + if (sectionName != "general" && sectionName != "seeds - Mersenne Twister") + { + continue; + } + + for (const IniFile::Property* p = section->firstProperty; p; p = p->next) + { + if (!readLinkGeneralProperty(to_return, p->key, p->value)) + { + logs.warning() << ini.filename() << ": reading value of '" << p->key + << "' went wrong"; + } + } + } + return to_return; +} + +void LinksTSgenerator::extractLinksSpecificTSparameters() +{ + fs::path toLinksDir = studyFolder_ / "input" / "links"; + readIniProperties(linkList_, toLinksDir); + readPreproTimeSeries(linkList_, toLinksDir); +} + +bool LinksTSgenerator::generate() +{ + auto saveTSpath = fs::path(studyFolder_) / "output" / FormattedTime("%Y%m%d-%H%M"); + saveTSpath /= "ts-generator"; + saveTSpath /= "links"; + + return generateLinkTimeSeries(linkList_, generalParams_, saveTSpath); +} + +} // namespace Antares::TSGenerator diff --git a/src/tools/ts-generator/main.cpp b/src/tools/ts-generator/main.cpp index b1fabf2ff7..d923d26a6c 100644 --- a/src/tools/ts-generator/main.cpp +++ b/src/tools/ts-generator/main.cpp @@ -22,74 +22,25 @@ #include #include -#include -#include - -#include -#include -#include #include #include -#include #include #include -#include -#include +#include "antares/tools/ts-generator/linksTSgenerator.h" +#include "antares/tools/ts-generator/tsGenerationOptions.h" +using namespace Antares::TSGenerator; -using namespace Antares; using namespace Antares::TSGenerator; namespace fs = std::filesystem; -struct Settings -{ - std::string studyFolder; - - /// generate TS for all clusters if activated - bool allThermal = false; - /// generate TS for a list "area.cluster;area2.cluster2;" - std::string thermalListToGen = ""; - - /// generate TS for all links if activated - bool allLinks = false; - /// generate TS for a list "area.link;area2.link2;" - std::string linksListToGen = ""; -}; - -std::unique_ptr createTsGeneratorParser(Settings& settings) -{ - auto parser = std::make_unique(); - parser->addParagraph("Antares Time Series generator\n"); - - parser->addFlag(settings.allThermal, - ' ', - "all-thermal", - "Generate TS for all thermal clusters"); - parser->addFlag(settings.thermalListToGen, - ' ', - "thermal", - "Generate TS for a list of area IDs and thermal clusters IDs, " - "\nusage: --thermal=\"areaID.clusterID;area2ID.clusterID\""); - - parser->addFlag(settings.allLinks, ' ', "all-links", "Generate TS capacities for all links"); - parser->addFlag(settings.linksListToGen, - ' ', - "links", - "Generate TS capacities for a list of 2 area IDs, " - "usage: --links=\"areaID.area2ID;area3ID.area1ID\""); - - parser->remainingArguments(settings.studyFolder); - - return parser; -} - std::vector getClustersToGen(Data::AreaList& areas, const std::string& clustersToGen) { std::vector clusters; - const auto ids = splitStringIntoPairs(clustersToGen, ';', '.'); + const auto pairsAreaCluster = splitStringIntoPairs(clustersToGen, ';', '.'); - for (const auto& [areaID, clusterID]: ids) + for (const auto& [areaID, clusterID]: pairsAreaCluster) { logs.info() << "Searching for area: " << areaID << " and cluster: " << clusterID; @@ -113,419 +64,63 @@ std::vector getClustersToGen(Data::AreaList& areas, return clusters; } -// ===== New code for TS generation links ==================================== - -std::vector extractTargetAreas(fs::path sourceLinkDir) -{ - std::vector to_return; - fs::path pathToIni = sourceLinkDir / "properties.ini"; - IniFile ini; - ini.open(pathToIni); // gp : we should handle reading issues - for (auto* s = ini.firstSection; s; s = s->next) - { - std::string targetAreaName = transformNameIntoID(s->name); - to_return.push_back(targetAreaName); - } - return to_return; -} - -LinkPairs extractLinkNamesFromStudy(fs::path studyDir) -{ - LinkPairs to_return; - fs::path linksDir = studyDir / "input" / "links"; - for (const auto& item: fs::directory_iterator{linksDir}) - { - if (item.is_directory()) - { - std::string sourceAreaName = item.path().filename().generic_string(); - auto targetAreas = extractTargetAreas(item); - for (auto& targetAreaName: targetAreas) - { - auto linkPair = std::make_pair(sourceAreaName, targetAreaName); - to_return.push_back(linkPair); - } - } - } - return to_return; -} - -bool pairs_match(const LinkPair& p1, const LinkPair& p2) -{ - return (p1.first == p2.first && p1.second == p2.second) - || (p1.first == p2.second && p1.second == p2.first); -} - -const LinkPair* getMatchingPairInCollection(const LinkPair& pair, const LinkPairs& collection) -{ - for (const auto& p: collection) - { - if (pairs_match(pair, p)) - { - return &p; - } - } - return nullptr; -} - -LinkPairs extractLinkNamesFromCmdLine(const LinkPairs& allLinks, const std::string linksFromCmdLine) -{ - LinkPairs to_return; - LinkPairs pairsFromCmdLine = splitStringIntoPairs(linksFromCmdLine, ';', '.'); - for (auto& p: pairsFromCmdLine) - { - if (const auto* found_pair = getMatchingPairInCollection(p, allLinks); found_pair) - { - to_return.push_back(*found_pair); - } - else - { - logs.error() << "Link '" << p.first << "." << p.second << "' not found"; - } - } - return to_return; -} - -bool readLinkGeneralProperty(StudyParamsForLinkTS& params, - const Yuni::String& key, - const Yuni::String& value) -{ - if (key == "derated") - { - return value.to(params.derated); - } - if (key == "nbtimeserieslinks") - { - return value.to(params.nbLinkTStoGenerate); - } - if (key == "seed-tsgen-links") - { - unsigned int seed{0}; - if (!value.to(seed)) - { - return false; - } - params.random.reset(seed); - return true; - } - return true; // gp : should we return true here ? -} - -StudyParamsForLinkTS readGeneralParamsForLinksTS(fs::path studyDir) -{ - StudyParamsForLinkTS to_return; - fs::path pathToGeneraldata = studyDir / "settings" / "generaldata.ini"; - IniFile ini; - ini.open(pathToGeneraldata); // gp : we should handle reading issues - for (auto* section = ini.firstSection; section; section = section->next) - { - // Skipping sections useless in the current context - Yuni::String sectionName = section->name; - if (sectionName != "general" && sectionName != "seeds - Mersenne Twister") - { - continue; - } - - for (const IniFile::Property* p = section->firstProperty; p; p = p->next) - { - if (!readLinkGeneralProperty(to_return, p->key, p->value)) - { - logs.warning() << ini.filename() << ": reading value of '" << p->key - << "' went wrong"; - } - } - } - return to_return; -} - -std::vector CreateLinkList(const LinkPairs& linksFromCmdLine) -{ - std::vector to_return; - to_return.reserve(linksFromCmdLine.size()); - for (const auto& link_pair: linksFromCmdLine) - { - LinkTSgenerationParams params; - params.namesPair = link_pair; - to_return.push_back(std::move(params)); - } - return to_return; -} - -LinkTSgenerationParams* findLinkInList(const LinkPair& link_to_find, - std::vector& linkList) -{ - for (auto& link: linkList) - { - if (link.namesPair == link_to_find) - { - return &link; - } - } - return nullptr; -} - -bool readLinkIniProperty(LinkTSgenerationParams* link, - const Yuni::String& key, - const Yuni::String& value) +int main(int argc, char* argv[]) { - if (key == "unitcount") - { - return value.to(link->unitCount); - } - - if (key == "nominalcapacity") - { - return value.to(link->nominalCapacity); - } - - if (key == "law.planned") - { - return value.to(link->plannedLaw); - } - - if (key == "law.forced") - { - return value.to(link->forcedLaw); - } + logs.applicationName("ts-generator"); - if (key == "volatility.planned") + Settings settings; + if (!parseOptions(argc, argv, settings)) { - return value.to(link->plannedVolatility); + return 1; } - if (key == "volatility.forced") + if (!checkOptions(settings)) { - return value.to(link->forcedVolatility); + return 1; } - if (key == "force-no-generation") - { - return value.to(link->forceNoGeneration); - } - return true; -} + bool return_code{true}; -void readLinkIniProperties(LinkTSgenerationParams* link, IniFile::Section* section) -{ - for (const IniFile::Property* p = section->firstProperty; p; p = p->next) + if (thermalTSrequired(settings)) { - if (!readLinkIniProperty(link, p->key, p->value)) + // === Data for TS generation === + auto study = std::make_shared(true); + Data::StudyLoadOptions studyOptions; + if (!study->loadFromFolder(settings.studyFolder, studyOptions)) { - std::string linkName = link->namesPair.first + "." + link->namesPair.second; - logs.warning() << "Link '" << linkName << "' : reading value of '" << p->key - << "' went wrong"; - link->hasValidData = false; + logs.error() << "Invalid study given to the generator"; + return 1; } - } -} -void readSourceAreaIniFile(fs::path pathToIni, - std::string sourceAreaName, - std::vector& linkList) -{ - IniFile ini; - ini.open(pathToIni); // gp : we should handle reading issues - for (auto* section = ini.firstSection; section; section = section->next) - { - std::string targetAreaName = transformNameIntoID(section->name); - const LinkPair processedLink = std::make_pair(sourceAreaName, targetAreaName); - if (auto* foundLink = findLinkInList(processedLink, linkList); foundLink) + std::vector clusters; + if (settings.allThermal) { - readLinkIniProperties(foundLink, section); + clusters = getAllClustersToGen(study->areas, true); } - } -} - -void readIniProperties(std::vector& linkList, fs::path toLinksDir) -{ - for (auto& link: linkList) - { - std::string sourceAreaName = link.namesPair.first; - fs::path pathToIni = toLinksDir / sourceAreaName / "properties.ini"; - readSourceAreaIniFile(pathToIni, sourceAreaName, linkList); - } -} - -bool readLinkPreproTimeSeries(LinkTSgenerationParams& link, fs::path sourceAreaDir) -{ - bool to_return = true; - const auto preproId = link.namesPair.first + "/" + link.namesPair.second; - link.prepro = std::make_unique(preproId, link.unitCount); - - auto preproFileRoot = sourceAreaDir / "prepro" / link.namesPair.second; - - auto preproFile = preproFileRoot; - preproFile += ".txt"; - if (fs::exists(preproFile)) - { - to_return = link.prepro->data.loadFromCSVFile( - preproFile.string(), - Data::PreproAvailability::preproAvailabilityMax, - DAYS_PER_YEAR) - && link.prepro->validate() && to_return; - } - - auto modulationFileDirect = preproFileRoot; - modulationFileDirect += "_mod_direct.txt"; - if (fs::exists(modulationFileDirect)) - { - to_return = link.modulationCapacityDirect.loadFromCSVFile(modulationFileDirect.string(), - 1, - HOURS_PER_YEAR) - && to_return; - } - - auto modulationFileIndirect = preproFileRoot; - modulationFileIndirect += "_mod_indirect.txt"; - if (fs::exists(modulationFileIndirect)) - { - to_return = link.modulationCapacityIndirect.loadFromCSVFile(modulationFileIndirect.string(), - 1, - HOURS_PER_YEAR) - && to_return; - } - // Makes possible a skip of TS generation when time comes - link.hasValidData = link.hasValidData && to_return; - return to_return; -} - -void readPreproTimeSeries(std::vector& linkList, fs::path toLinksDir) -{ - for (auto& link: linkList) - { - std::string sourceAreaName = link.namesPair.first; - fs::path sourceAreaDir = toLinksDir / sourceAreaName; - if (!readLinkPreproTimeSeries(link, sourceAreaDir)) + else if (!settings.thermalListToGen.empty()) { - logs.warning() << "Could not load all prepro data for link '" << link.namesPair.first - << "." << link.namesPair.second << "'"; + clusters = getClustersToGen(study->areas, settings.thermalListToGen); } - } -} - -void readLinksSpecificTSparameters(std::vector& linkList, - fs::path studyFolder) -{ - fs::path toLinksDir = studyFolder / "input" / "links"; - readIniProperties(linkList, toLinksDir); - readPreproTimeSeries(linkList, toLinksDir); -} - -std::string DateAndTime() -{ - YString to_return; - unsigned int now = Yuni::DateTime::Now(); - Yuni::DateTime::TimestampToString(to_return, "%Y%m%d-%H%M", now); - return to_return.to(); -} -// ============================================================================ + // === TS generation === + MersenneTwister thermalRandom; + thermalRandom.reset(study->parameters.seed[Data::seedTsGenThermal]); + return_code = TSGenerator::generateThermalTimeSeries(*study, clusters, thermalRandom); -int main(int argc, char* argv[]) -{ - logs.applicationName("ts-generator"); - - Settings settings; - - auto parser = createTsGeneratorParser(settings); - switch (auto ret = parser->operator()(argc, argv); ret) - { - using namespace Yuni::GetOpt; - case ReturnCode::error: - logs.error() << "Unknown arguments, aborting"; - return parser->errors(); - case ReturnCode::help: - // End the program - return 0; - default: - break; - } - - if (settings.allThermal && !settings.thermalListToGen.empty()) - { - logs.error() << "Conflicting options, either choose all thermal clusters or a list"; - return 1; - } - - if (settings.allLinks && !settings.linksListToGen.empty()) - { - logs.error() << "Conflicting options, either choose all links or a list"; - return 1; - } - - auto study = std::make_shared(true); - Data::StudyLoadOptions studyOptions; - studyOptions.prepareOutput = true; - - if (!study->loadFromFolder(settings.studyFolder, studyOptions)) - { - logs.error() << "Invalid study given to the generator"; - return 1; - } - - study->initializeRuntimeInfos(); - // Force the writing of generated TS into output/YYYYMMDD-HHSSeco/ts-generator/thermal[/mc-0] - study->parameters.timeSeriesToArchive |= Antares::Data::timeSeriesThermal; - - try - { - Antares::Check::checkMinStablePower(true, study->areas); - } - catch (Error::InvalidParametersForThermalClusters& ex) - { - Antares::logs.error() << ex.what(); - } - - Benchmarking::DurationCollector durationCollector; - - auto resultWriter = Solver::resultWriterFactory(Data::ResultFormat::legacyFilesDirectories, - study->folderOutput, - nullptr, - durationCollector); - - const auto thermalSavePath = fs::path("ts-generator") / "thermal"; - - // ============ THERMAL : Getting data for generating time-series ========= - std::vector clusters; - if (settings.allThermal) - { - clusters = TSGenerator::getAllClustersToGen(study->areas, true); - } - else if (!settings.thermalListToGen.empty()) - { - clusters = getClustersToGen(study->areas, settings.thermalListToGen); - } - - for (auto& c: clusters) - { - logs.debug() << c->id(); + // === Writing generated TS on disk === + auto thermalSavePath = fs::path(settings.studyFolder) / "output" + / FormattedTime("%Y%m%d-%H%M"); + thermalSavePath /= "ts-generator"; + thermalSavePath /= "thermal"; + writeThermalTimeSeries(clusters, thermalSavePath); } - // ============ LINKS : Getting data for generating LINKS time-series ===== - auto allLinksPairs = extractLinkNamesFromStudy(settings.studyFolder); - auto linksFromCmdLine = extractLinkNamesFromCmdLine(allLinksPairs, settings.linksListToGen); - if (settings.allLinks) + if (linkTSrequired(settings)) { - linksFromCmdLine = allLinksPairs; + LinksTSgenerator linksTSgenerator(settings); + linksTSgenerator.extractData(); + return_code = linksTSgenerator.generate() && return_code; } - StudyParamsForLinkTS generalParams = readGeneralParamsForLinksTS(settings.studyFolder); - - std::vector linkList = CreateLinkList(linksFromCmdLine); - readLinksSpecificTSparameters(linkList, settings.studyFolder); - - auto saveLinksTSpath = fs::path(settings.studyFolder) / "output" / DateAndTime(); - saveLinksTSpath /= "ts-generator"; - saveLinksTSpath /= "links"; - - // ============ TS Generation ============================================= - - bool ret = TSGenerator::generateThermalTimeSeries(*study, - clusters, - *resultWriter, - thermalSavePath.string()); - - ret = TSGenerator::generateLinkTimeSeries(linkList, generalParams, saveLinksTSpath.string()) - && ret; - - return !ret; // return 0 for success + return !return_code; // return 0 for success } diff --git a/src/tools/ts-generator/tsGenerationOptions.cpp b/src/tools/ts-generator/tsGenerationOptions.cpp new file mode 100644 index 0000000000..29fa64b6ad --- /dev/null +++ b/src/tools/ts-generator/tsGenerationOptions.cpp @@ -0,0 +1,77 @@ +#include "antares/tools/ts-generator/tsGenerationOptions.h" + +#include + +namespace Antares::TSGenerator +{ + +std::unique_ptr createTsGeneratorParser(Settings& settings) +{ + auto parser = std::make_unique(); + parser->addParagraph("Antares Time Series generator\n"); + + parser->addFlag(settings.allThermal, + ' ', + "all-thermal", + "Generate TS for all thermal clusters"); + parser->addFlag(settings.thermalListToGen, + ' ', + "thermal", + "Generate TS for a list of area IDs and thermal clusters IDs, " + "\nusage: --thermal=\"areaID.clusterID;area2ID.clusterID\""); + + parser->addFlag(settings.allLinks, ' ', "all-links", "Generate TS capacities for all links"); + parser->addFlag(settings.linksListToGen, + ' ', + "links", + "Generate TS capacities for a list of 2 area IDs, " + "usage: --links=\"areaID.area2ID;area3ID.area1ID\""); + + parser->remainingArguments(settings.studyFolder); + + return parser; +} + +bool parseOptions(int argc, char* argv[], Settings& options) +{ + auto parser = createTsGeneratorParser(options); + switch (auto ret = parser->operator()(argc, argv); ret) + { + using namespace Yuni::GetOpt; + case ReturnCode::error: + logs.error() << "Unknown arguments, aborting"; + return false; + case ReturnCode::help: + return false; + default: + break; + } + return true; +} + +bool checkOptions(Settings& options) +{ + if (options.allThermal && !options.thermalListToGen.empty()) + { + logs.error() << "Conflicting options, either choose all thermal clusters or a list"; + return false; + } + + if (options.allLinks && !options.linksListToGen.empty()) + { + logs.error() << "Conflicting options, either choose all links or a list"; + return false; + } + return true; +} + +bool linkTSrequired(Settings& options) +{ + return options.allLinks || !options.linksListToGen.empty(); +} + +bool thermalTSrequired(Settings& options) +{ + return options.allThermal || !options.thermalListToGen.empty(); +} +} // namespace Antares::TSGenerator