flextool 4.2.0__tar.gz → 4.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (466) hide show
  1. {flextool-4.2.0 → flextool-4.2.2}/PKG-INFO +4 -4
  2. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_timeline.py +155 -25
  3. flextool-4.2.2/flextool/engine/preprocess/emit/_base_steps.py +74 -0
  4. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_inflow_scaling.py +234 -137
  5. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_inflow_scaling_checks.py +56 -22
  6. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_period_params.py +13 -3
  7. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_derived.py +6 -3
  8. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_run_model.py +1 -0
  9. {flextool-4.2.0 → flextool-4.2.2}/flextool/env_check.py +2 -2
  10. {flextool-4.2.0 → flextool-4.2.2}/flextool.egg-info/PKG-INFO +4 -4
  11. {flextool-4.2.0 → flextool-4.2.2}/flextool.egg-info/SOURCES.txt +1 -0
  12. {flextool-4.2.0 → flextool-4.2.2}/flextool.egg-info/requires.txt +3 -3
  13. {flextool-4.2.0 → flextool-4.2.2}/pyproject.toml +6 -6
  14. {flextool-4.2.0 → flextool-4.2.2}/LICENSE.txt +0 -0
  15. {flextool-4.2.0 → flextool-4.2.2}/README.md +0 -0
  16. {flextool-4.2.0 → flextool-4.2.2}/flextool/__init__.py +0 -0
  17. {flextool-4.2.0 → flextool-4.2.2}/flextool/_allocator.py +0 -0
  18. {flextool-4.2.0 → flextool-4.2.2}/flextool/_mem_sampler.py +0 -0
  19. {flextool-4.2.0 → flextool-4.2.2}/flextool/_resources.py +0 -0
  20. {flextool-4.2.0 → flextool-4.2.2}/flextool/_thread_budget.py +0 -0
  21. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/__init__.py +0 -0
  22. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/__main__.py +0 -0
  23. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_cli.py +0 -0
  24. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_db_alt.py +0 -0
  25. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_final_outputs.py +0 -0
  26. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_guard.py +0 -0
  27. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_loop.py +0 -0
  28. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_readers.py +0 -0
  29. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_report.py +0 -0
  30. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_sizing.py +0 -0
  31. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_solve.py +0 -0
  32. {flextool-4.2.0 → flextool-4.2.2}/flextool/calibrate/_solve_status.py +0 -0
  33. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/__init__.py +0 -0
  34. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/_console.py +0 -0
  35. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/_timing.py +0 -0
  36. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_execute_flextool_workflow.py +0 -0
  37. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_export_to_tabular.py +0 -0
  38. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_import_sensitivities.py +0 -0
  39. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_migrate_database.py +0 -0
  40. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_open_results_db.py +0 -0
  41. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_read_matpower.py +0 -0
  42. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_read_old_flextool.py +0 -0
  43. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_read_self_describing_tabular_input.py +0 -0
  44. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_read_tabular_input.py +0 -0
  45. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_run_flextool.py +0 -0
  46. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_scenario_results.py +0 -0
  47. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_solve_mps.py +0 -0
  48. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_update_flextool.py +0 -0
  49. {flextool-4.2.0 → flextool-4.2.2}/flextool/cli/cmd_write_outputs.py +0 -0
  50. {flextool-4.2.0 → flextool-4.2.2}/flextool/common_utils/__init__.py +0 -0
  51. {flextool-4.2.0 → flextool-4.2.2}/flextool/common_utils/plot_mem_shape.py +0 -0
  52. {flextool-4.2.0 → flextool-4.2.2}/flextool/decomposition/__init__.py +0 -0
  53. {flextool-4.2.0 → flextool-4.2.2}/flextool/decomposition/region_decomposition.py +0 -0
  54. {flextool-4.2.0 → flextool-4.2.2}/flextool/decomposition/region_filter.py +0 -0
  55. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/__init__.py +0 -0
  56. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/_behaviour_changes.py +0 -0
  57. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/_csv_reload/__init__.py +0 -0
  58. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/_csv_reload/_field_seeds.py +0 -0
  59. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/_csv_reload/_meta.py +0 -0
  60. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/_csv_reload/_seed.py +0 -0
  61. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/_csv_reload/_stamp.py +0 -0
  62. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/_csv_reload/_topology.py +0 -0
  63. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/_csv_reload/load.py +0 -0
  64. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/_csv_reload/offline_outputs.py +0 -0
  65. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/autoscale/__init__.py +0 -0
  66. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/autoscale/_config.py +0 -0
  67. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/autoscale/_layer2.py +0 -0
  68. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/autoscale/_layer2_types.py +0 -0
  69. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/autoscale/_quantity_types.py +0 -0
  70. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/autoscale/_report.py +0 -0
  71. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/__init__.py +0 -0
  72. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_axis_enums.py +0 -0
  73. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_constants.py +0 -0
  74. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_db_loader.py +0 -0
  75. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_db_reader.py +0 -0
  76. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_inmemory_reader.py +0 -0
  77. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_input_source.py +0 -0
  78. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_param_shapes.py +0 -0
  79. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_solve_config.py +0 -0
  80. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_solve_state.py +0 -0
  81. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_spinedb_reader.py +0 -0
  82. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/input/_timeline_rules.py +0 -0
  83. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/__init__.py +0 -0
  84. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_balance_terms.py +0 -0
  85. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_coarse_flows.py +0 -0
  86. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_commodity_ladder.py +0 -0
  87. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_common.py +0 -0
  88. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_cumulative_invest.py +0 -0
  89. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_dc_power_flow.py +0 -0
  90. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_delay.py +0 -0
  91. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_exact.py +0 -0
  92. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_group_slack.py +0 -0
  93. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_lead_time.py +0 -0
  94. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_non_anticipativity.py +0 -0
  95. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_online.py +0 -0
  96. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_pdt_join.py +0 -0
  97. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_reserve.py +0 -0
  98. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_self_discharge.py +0 -0
  99. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/_unit_floors.py +0 -0
  100. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/model/core.py +0 -0
  101. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/output/__init__.py +0 -0
  102. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/output/_coarse_flows.py +0 -0
  103. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/output/_dump_csvs.py +0 -0
  104. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/output/_horizon_dump.py +0 -0
  105. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/output/_parquet_bundle.py +0 -0
  106. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/output/_prices.py +0 -0
  107. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/output/_writer.py +0 -0
  108. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/__init__.py +0 -0
  109. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_dump_stamp.py +0 -0
  110. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_flex_data_provider.py +0 -0
  111. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_input_writer.py +0 -0
  112. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_invest_seeds.py +0 -0
  113. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_provider_io.py +0 -0
  114. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_provider_keys.py +0 -0
  115. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_provider_translators.py +0 -0
  116. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_reserve_methods.py +0 -0
  117. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_solve_context.py +0 -0
  118. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/_stochastic_detect.py +0 -0
  119. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/__init__.py +0 -0
  120. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_arc_unions.py +0 -0
  121. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_blocks.py +0 -0
  122. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_calc_params.py +0 -0
  123. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_chain_params.py +0 -0
  124. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_co2_accumulators.py +0 -0
  125. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_dispatchers.py +0 -0
  126. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_energy_margin.py +0 -0
  127. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_energy_margin_adder.py +0 -0
  128. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_entity_annual.py +0 -0
  129. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_leaf_sets.py +0 -0
  130. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_lp_scaling.py +0 -0
  131. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_mid_sets.py +0 -0
  132. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_pdt_lookup.py +0 -0
  133. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_pdt_params.py +0 -0
  134. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_per_solve.py +0 -0
  135. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_period_calc.py +0 -0
  136. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_reserve_sets.py +0 -0
  137. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_solve_time.py +0 -0
  138. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_solve_writers.py +0 -0
  139. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/emit/_vectorize.py +0 -0
  140. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/flex_data.py +0 -0
  141. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/__init__.py +0 -0
  142. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_arithmetic.py +0 -0
  143. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_block.py +0 -0
  144. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_block_layout.py +0 -0
  145. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_branch.py +0 -0
  146. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_coarse.py +0 -0
  147. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_direct.py +0 -0
  148. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_existing.py +0 -0
  149. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_mintime.py +0 -0
  150. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_npv.py +0 -0
  151. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_per_solve_sets.py +0 -0
  152. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_profiles.py +0 -0
  153. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_projection.py +0 -0
  154. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_ramp_links.py +0 -0
  155. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_retire_existing.py +0 -0
  156. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_run_calendar.py +0 -0
  157. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/preprocess/params/_walks.py +0 -0
  158. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/__init__.py +0 -0
  159. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_acceptance.py +0 -0
  160. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_benders.py +0 -0
  161. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_carry_routing.py +0 -0
  162. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_determinism.py +0 -0
  163. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_handoff.py +0 -0
  164. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_orchestration.py +0 -0
  165. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_realization.py +0 -0
  166. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_realized_overlap.py +0 -0
  167. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_recursive_solve.py +0 -0
  168. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_region_filter.py +0 -0
  169. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_solver_base.py +0 -0
  170. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_solver_dispatch.py +0 -0
  171. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_stochastic.py +0 -0
  172. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_subprocess_solve.py +0 -0
  173. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/_warm.py +0 -0
  174. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine/solve/chain.py +0 -0
  175. {flextool-4.2.0 → flextool-4.2.2}/flextool/engine_polars/__init__.py +0 -0
  176. {flextool-4.2.0 → flextool-4.2.2}/flextool/export_to_tabular/__init__.py +0 -0
  177. {flextool-4.2.0 → flextool-4.2.2}/flextool/export_to_tabular/db_reader.py +0 -0
  178. {flextool-4.2.0 → flextool-4.2.2}/flextool/export_to_tabular/excel_writer.py +0 -0
  179. {flextool-4.2.0 → flextool-4.2.2}/flextool/export_to_tabular/export_settings.yaml +0 -0
  180. {flextool-4.2.0 → flextool-4.2.2}/flextool/export_to_tabular/export_to_excel.py +0 -0
  181. {flextool-4.2.0 → flextool-4.2.2}/flextool/export_to_tabular/formatting.py +0 -0
  182. {flextool-4.2.0 → flextool-4.2.2}/flextool/export_to_tabular/sheet_config.py +0 -0
  183. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/__init__.py +0 -0
  184. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/__main__.py +0 -0
  185. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/calibrate_commands.py +0 -0
  186. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/calibrate_jobs.py +0 -0
  187. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/check_tree.py +0 -0
  188. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/cli_format.py +0 -0
  189. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/config_parser.py +0 -0
  190. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/data_models.py +0 -0
  191. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/db_editor_integration.py +0 -0
  192. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/db_version_check.py +0 -0
  193. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/__init__.py +0 -0
  194. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/add_dialog.py +0 -0
  195. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/calibrate_dialog.py +0 -0
  196. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/file_picker.py +0 -0
  197. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/group_picker.py +0 -0
  198. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/migration_consent_dialog.py +0 -0
  199. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/migration_progress_dialog.py +0 -0
  200. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/plot_dialog.py +0 -0
  201. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/plot_settings_picker.py +0 -0
  202. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/project_dialog.py +0 -0
  203. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/dialogs/update_dialog.py +0 -0
  204. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/downsampling.py +0 -0
  205. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/error_handling.py +0 -0
  206. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/execution_manager.py +0 -0
  207. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/execution_window.py +0 -0
  208. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/hover_tooltip.py +0 -0
  209. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/input_sources.py +0 -0
  210. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/main_window.py +0 -0
  211. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/network_graph.py +0 -0
  212. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/output_actions.py +0 -0
  213. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/output_log_window.py +0 -0
  214. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/platform_utils.py +0 -0
  215. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/plot_cache.py +0 -0
  216. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/plot_canvas.py +0 -0
  217. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/plot_config_reader.py +0 -0
  218. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/project_utils.py +0 -0
  219. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/result_viewer.py +0 -0
  220. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/scenario_key.py +0 -0
  221. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/scenario_lists.py +0 -0
  222. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/settings_io.py +0 -0
  223. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/solve_reader.py +0 -0
  224. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/tree_reorder.py +0 -0
  225. {flextool-4.2.0 → flextool-4.2.2}/flextool/gui/ui_metrics.py +0 -0
  226. {flextool-4.2.0 → flextool-4.2.2}/flextool/input_derivation/__init__.py +0 -0
  227. {flextool-4.2.0 → flextool-4.2.2}/flextool/input_derivation/_commodity_ladder.py +0 -0
  228. {flextool-4.2.0 → flextool-4.2.2}/flextool/input_derivation/_commodity_ladder_sets.py +0 -0
  229. {flextool-4.2.0 → flextool-4.2.2}/flextool/input_derivation/_dc_power_flow.py +0 -0
  230. {flextool-4.2.0 → flextool-4.2.2}/flextool/input_derivation/_method_constants.py +0 -0
  231. {flextool-4.2.0 → flextool-4.2.2}/flextool/input_derivation/_process_method.py +0 -0
  232. {flextool-4.2.0 → flextool-4.2.2}/flextool/input_derivation/_specs.py +0 -0
  233. {flextool-4.2.0 → flextool-4.2.2}/flextool/input_derivation/_validators.py +0 -0
  234. {flextool-4.2.0 → flextool-4.2.2}/flextool/lean_parquet.py +0 -0
  235. {flextool-4.2.0 → flextool-4.2.2}/flextool/model_builder/__init__.py +0 -0
  236. {flextool-4.2.0 → flextool-4.2.2}/flextool/model_builder/build_model.py +0 -0
  237. {flextool-4.2.0 → flextool-4.2.2}/flextool/model_builder/encoding.py +0 -0
  238. {flextool-4.2.0 → flextool-4.2.2}/flextool/model_builder/names.py +0 -0
  239. {flextool-4.2.0 → flextool-4.2.2}/flextool/model_builder/profiles.py +0 -0
  240. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/__init__.py +0 -0
  241. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/axis_helpers.py +0 -0
  242. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/color_template.py +0 -0
  243. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/config.py +0 -0
  244. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/format_helpers.py +0 -0
  245. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/legend_helpers.py +0 -0
  246. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/orchestrator.py +0 -0
  247. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/perf.py +0 -0
  248. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/plan.py +0 -0
  249. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/plot_bars.py +0 -0
  250. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/plot_bars_detail.py +0 -0
  251. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/plot_lines.py +0 -0
  252. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/shared_manifest.py +0 -0
  253. {flextool-4.2.0 → flextool-4.2.2}/flextool/plot_outputs/subplot_helpers.py +0 -0
  254. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_inputs/__init__.py +0 -0
  255. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_inputs/import_old_excel_input.json +0 -0
  256. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_inputs/read_matpower.py +0 -0
  257. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_inputs/read_old_flextool.py +0 -0
  258. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_inputs/read_self_describing_excel.py +0 -0
  259. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_inputs/read_tabular_with_specification.py +0 -0
  260. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_inputs/write_old_flextool_to_db.py +0 -0
  261. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_inputs/write_self_describing_to_db.py +0 -0
  262. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_inputs/write_to_input_db.py +0 -0
  263. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/__init__.py +0 -0
  264. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/_annualize.py +0 -0
  265. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/_fan_labels.py +0 -0
  266. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/_inmemory_helpers.py +0 -0
  267. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/_output_meta.py +0 -0
  268. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/_timeline_breaks.py +0 -0
  269. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/calc_capacity_flows.py +0 -0
  270. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/calc_connections.py +0 -0
  271. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/calc_costs.py +0 -0
  272. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/calc_group_flows.py +0 -0
  273. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/calc_slacks.py +0 -0
  274. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/calc_storage_vre.py +0 -0
  275. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/drop_levels.py +0 -0
  276. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/handoff_writers.py +0 -0
  277. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/out_ancillary.py +0 -0
  278. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/out_capacity.py +0 -0
  279. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/out_costs.py +0 -0
  280. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/out_flowgroup.py +0 -0
  281. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/out_flows.py +0 -0
  282. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/out_group.py +0 -0
  283. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/out_node.py +0 -0
  284. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/persist_realized_slice.py +0 -0
  285. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/process_results.py +0 -0
  286. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/read_highs_solution.py +0 -0
  287. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/read_parameters.py +0 -0
  288. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/read_sets.py +0 -0
  289. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/read_variables.py +0 -0
  290. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/solve_order.py +0 -0
  291. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/spinedb_replay.py +0 -0
  292. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/union_realized_slice.py +0 -0
  293. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/write_outputs.py +0 -0
  294. {flextool-4.2.0 → flextool-4.2.2}/flextool/process_outputs/write_spinedb.py +0 -0
  295. {flextool-4.2.0 → flextool-4.2.2}/flextool/representative_periods/__init__.py +0 -0
  296. {flextool-4.2.0 → flextool-4.2.2}/flextool/representative_periods/clustering.py +0 -0
  297. {flextool-4.2.0 → flextool-4.2.2}/flextool/representative_periods/force_include.py +0 -0
  298. {flextool-4.2.0 → flextool-4.2.2}/flextool/representative_periods/netload.py +0 -0
  299. {flextool-4.2.0 → flextool-4.2.2}/flextool/representative_periods/netload_inputs.py +0 -0
  300. {flextool-4.2.0 → flextool-4.2.2}/flextool/representative_periods/netload_iterate.py +0 -0
  301. {flextool-4.2.0 → flextool-4.2.2}/flextool/representative_periods/preprocess.py +0 -0
  302. {flextool-4.2.0 → flextool-4.2.2}/flextool/representative_periods/scenario_stack.py +0 -0
  303. {flextool-4.2.0 → flextool-4.2.2}/flextool/representative_periods/weights.py +0 -0
  304. {flextool-4.2.0 → flextool-4.2.2}/flextool/run_options.py +0 -0
  305. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/__init__.py +0 -0
  306. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/config_builder.py +0 -0
  307. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/constants.py +0 -0
  308. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/data_models.py +0 -0
  309. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/db_reader.py +0 -0
  310. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/dispatch_data.py +0 -0
  311. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/dispatch_mappings.py +0 -0
  312. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/dispatch_plots.py +0 -0
  313. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/input_entity_colors.py +0 -0
  314. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/orchestrator.py +0 -0
  315. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/plan_union.py +0 -0
  316. {flextool-4.2.0 → flextool-4.2.2}/flextool/scenario_comparison/plot_settings_seed.py +0 -0
  317. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/AXIS_CONTRACT.md +0 -0
  318. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/howto_aggregate_output.json +0 -0
  319. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/howto_connections.json +0 -0
  320. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/howto_demand.json +0 -0
  321. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/howto_hydro_reservoir.json +0 -0
  322. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/howto_hydro_reservoir_with_pump.json +0 -0
  323. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/howto_non_sync_and_curtailment.json +0 -0
  324. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/howto_ramp_and_start_up.json +0 -0
  325. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/howto_stochastics.json +0 -0
  326. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/templates_examples.json +0 -0
  327. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/canonical_databases/templates_time_settings_only.json +0 -0
  328. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/comparison_settings_template.json +0 -0
  329. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/default_plot_settings.yaml +0 -0
  330. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/default_plots.yaml +0 -0
  331. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/flextool_axis_contract.json +0 -0
  332. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/flextool_axis_contract.schema.json +0 -0
  333. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/old_flextool_import_template.json +0 -0
  334. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/output_info_template.json +0 -0
  335. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/output_settings_template.json +0 -0
  336. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_constant_default.json +0 -0
  337. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_default_optional_output.json +0 -0
  338. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_default_value.json +0 -0
  339. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_drop_down.json +0 -0
  340. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_lifetime_method.json +0 -0
  341. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_optional_outputs.json +0 -0
  342. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_output_node_flows.json +0 -0
  343. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_results_master.json +0 -0
  344. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_rolling_start_remove.json +0 -0
  345. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_rolling_window.json +0 -0
  346. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_storage_binding_defaults.json +0 -0
  347. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_v2.json +0 -0
  348. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/pre_v26/flextool_template_v25.json +0 -0
  349. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/spinedb_results_schema.json +0 -0
  350. {flextool-4.2.0 → flextool-4.2.2}/flextool/schemas/spinedb_schema.json +0 -0
  351. {flextool-4.2.0 → flextool-4.2.2}/flextool/solver_config/copt.opt.template +0 -0
  352. {flextool-4.2.0 → flextool-4.2.2}/flextool/solver_config/cplex.opt.template +0 -0
  353. {flextool-4.2.0 → flextool-4.2.2}/flextool/solver_config/gurobi.opt.template +0 -0
  354. {flextool-4.2.0 → flextool-4.2.2}/flextool/solver_config/highs.opt.template +0 -0
  355. {flextool-4.2.0 → flextool-4.2.2}/flextool/solver_config/xpress.opt.template +0 -0
  356. {flextool-4.2.0 → flextool-4.2.2}/flextool/spinedb_backend/__init__.py +0 -0
  357. {flextool-4.2.0 → flextool-4.2.2}/flextool/spinedb_backend/_axis_enums.py +0 -0
  358. {flextool-4.2.0 → flextool-4.2.2}/flextool/spinedb_backend/_backend.py +0 -0
  359. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/__init__.py +0 -0
  360. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/bundle_install.py +0 -0
  361. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/canonical_databases.py +0 -0
  362. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/db_handle.py +0 -0
  363. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/db_migration.py +0 -0
  364. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/ensure_settings_db.py +0 -0
  365. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/export_database.py +0 -0
  366. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/extend_tests_fixture.py +0 -0
  367. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/fixture_schema.py +0 -0
  368. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/generate_canonical.py +0 -0
  369. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/initialize_database.py +0 -0
  370. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/install_info.py +0 -0
  371. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/self_update.py +0 -0
  372. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/sync_master_json_template.py +0 -0
  373. {flextool-4.2.0 → flextool-4.2.2}/flextool/update_flextool/test_fixtures.py +0 -0
  374. {flextool-4.2.0 → flextool-4.2.2}/flextool.egg-info/dependency_links.txt +0 -0
  375. {flextool-4.2.0 → flextool-4.2.2}/flextool.egg-info/entry_points.txt +0 -0
  376. {flextool-4.2.0 → flextool-4.2.2}/flextool.egg-info/top_level.txt +0 -0
  377. {flextool-4.2.0 → flextool-4.2.2}/setup.cfg +0 -0
  378. {flextool-4.2.0 → flextool-4.2.2}/tests/test_bar_layout.py +0 -0
  379. {flextool-4.2.0 → flextool-4.2.2}/tests/test_bar_sub_pixel_labels.py +0 -0
  380. {flextool-4.2.0 → flextool-4.2.2}/tests/test_blocks.py +0 -0
  381. {flextool-4.2.0 → flextool-4.2.2}/tests/test_branch_weights.py +0 -0
  382. {flextool-4.2.0 → flextool-4.2.2}/tests/test_child_run_argv.py +0 -0
  383. {flextool-4.2.0 → flextool-4.2.2}/tests/test_ci_shard.py +0 -0
  384. {flextool-4.2.0 → flextool-4.2.2}/tests/test_color_template.py +0 -0
  385. {flextool-4.2.0 → flextool-4.2.2}/tests/test_commodity_ladder_rolling.py +0 -0
  386. {flextool-4.2.0 → flextool-4.2.2}/tests/test_comparison_excel.py +0 -0
  387. {flextool-4.2.0 → flextool-4.2.2}/tests/test_comparison_project_root.py +0 -0
  388. {flextool-4.2.0 → flextool-4.2.2}/tests/test_complete_period_share_reduction.py +0 -0
  389. {flextool-4.2.0 → flextool-4.2.2}/tests/test_connection_node_warnings.py +0 -0
  390. {flextool-4.2.0 → flextool-4.2.2}/tests/test_console_run_tool.py +0 -0
  391. {flextool-4.2.0 → flextool-4.2.2}/tests/test_cost_aggregation_semantics.py +0 -0
  392. {flextool-4.2.0 → flextool-4.2.2}/tests/test_cost_breakdown_by_entity.py +0 -0
  393. {flextool-4.2.0 → flextool-4.2.2}/tests/test_csv_dump_roundtrip.py +0 -0
  394. {flextool-4.2.0 → flextool-4.2.2}/tests/test_dc_power_flow.py +0 -0
  395. {flextool-4.2.0 → flextool-4.2.2}/tests/test_dispatch_consumer_and_connection_signs.py +0 -0
  396. {flextool-4.2.0 → flextool-4.2.2}/tests/test_dispatch_node_debug_and_tree.py +0 -0
  397. {flextool-4.2.0 → flextool-4.2.2}/tests/test_dispatch_order_specials.py +0 -0
  398. {flextool-4.2.0 → flextool-4.2.2}/tests/test_dispatch_scenario_tag.py +0 -0
  399. {flextool-4.2.0 → flextool-4.2.2}/tests/test_ensure_settings_db.py +0 -0
  400. {flextool-4.2.0 → flextool-4.2.2}/tests/test_entity_unitsize_invariant.py +0 -0
  401. {flextool-4.2.0 → flextool-4.2.2}/tests/test_entity_universe_carrier.py +0 -0
  402. {flextool-4.2.0 → flextool-4.2.2}/tests/test_env_check.py +0 -0
  403. {flextool-4.2.0 → flextool-4.2.2}/tests/test_env_knob_registry.py +0 -0
  404. {flextool-4.2.0 → flextool-4.2.2}/tests/test_export_to_tabular.py +0 -0
  405. {flextool-4.2.0 → flextool-4.2.2}/tests/test_extend_tests_fixture.py +0 -0
  406. {flextool-4.2.0 → flextool-4.2.2}/tests/test_flowgroup_indicators.py +0 -0
  407. {flextool-4.2.0 → flextool-4.2.2}/tests/test_force_include.py +0 -0
  408. {flextool-4.2.0 → flextool-4.2.2}/tests/test_group_output_warnings.py +0 -0
  409. {flextool-4.2.0 → flextool-4.2.2}/tests/test_handoff_writers.py +0 -0
  410. {flextool-4.2.0 → flextool-4.2.2}/tests/test_input_entity_colors.py +0 -0
  411. {flextool-4.2.0 → flextool-4.2.2}/tests/test_lh2_three_region.py +0 -0
  412. {flextool-4.2.0 → flextool-4.2.2}/tests/test_min_load_rolling.py +0 -0
  413. {flextool-4.2.0 → flextool-4.2.2}/tests/test_non_anticipativity.py +0 -0
  414. {flextool-4.2.0 → flextool-4.2.2}/tests/test_old_flextool_importer_schema_drift.py +0 -0
  415. {flextool-4.2.0 → flextool-4.2.2}/tests/test_open_results_db.py +0 -0
  416. {flextool-4.2.0 → flextool-4.2.2}/tests/test_output_column_metadata.py +0 -0
  417. {flextool-4.2.0 → flextool-4.2.2}/tests/test_output_csv_names.py +0 -0
  418. {flextool-4.2.0 → flextool-4.2.2}/tests/test_output_metadata_renderers.py +0 -0
  419. {flextool-4.2.0 → flextool-4.2.2}/tests/test_output_work_folder_isolation.py +0 -0
  420. {flextool-4.2.0 → flextool-4.2.2}/tests/test_parameter_group_coverage.py +0 -0
  421. {flextool-4.2.0 → flextool-4.2.2}/tests/test_plan_union.py +0 -0
  422. {flextool-4.2.0 → flextool-4.2.2}/tests/test_plot_draw_order.py +0 -0
  423. {flextool-4.2.0 → flextool-4.2.2}/tests/test_plot_pipeline.py +0 -0
  424. {flextool-4.2.0 → flextool-4.2.2}/tests/test_plot_plan_chunk_reconstruction.py +0 -0
  425. {flextool-4.2.0 → flextool-4.2.2}/tests/test_plot_plan_time_labels_roundtrip.py +0 -0
  426. {flextool-4.2.0 → flextool-4.2.2}/tests/test_plot_settings_seed.py +0 -0
  427. {flextool-4.2.0 → flextool-4.2.2}/tests/test_read_highs_solution.py +0 -0
  428. {flextool-4.2.0 → flextool-4.2.2}/tests/test_recourse_cost_reconciliation.py +0 -0
  429. {flextool-4.2.0 → flextool-4.2.2}/tests/test_regional_filter.py +0 -0
  430. {flextool-4.2.0 → flextool-4.2.2}/tests/test_representative_periods.py +0 -0
  431. {flextool-4.2.0 → flextool-4.2.2}/tests/test_rolling_online_carry.py +0 -0
  432. {flextool-4.2.0 → flextool-4.2.2}/tests/test_rolling_online_mintime_carry.py +0 -0
  433. {flextool-4.2.0 → flextool-4.2.2}/tests/test_rolling_online_minuptime.py +0 -0
  434. {flextool-4.2.0 → flextool-4.2.2}/tests/test_rp_alternative_name.py +0 -0
  435. {flextool-4.2.0 → flextool-4.2.2}/tests/test_rp_force_include_wiring.py +0 -0
  436. {flextool-4.2.0 → flextool-4.2.2}/tests/test_rp_solves_filter.py +0 -0
  437. {flextool-4.2.0 → flextool-4.2.2}/tests/test_run_options.py +0 -0
  438. {flextool-4.2.0 → flextool-4.2.2}/tests/test_scenario_colors.py +0 -0
  439. {flextool-4.2.0 → flextool-4.2.2}/tests/test_scenario_stack.py +0 -0
  440. {flextool-4.2.0 → flextool-4.2.2}/tests/test_scenarios.py +0 -0
  441. {flextool-4.2.0 → flextool-4.2.2}/tests/test_self_describing_reader.py +0 -0
  442. {flextool-4.2.0 → flextool-4.2.2}/tests/test_shared_axis_manifest.py +0 -0
  443. {flextool-4.2.0 → flextool-4.2.2}/tests/test_simple_bar_entity_color.py +0 -0
  444. {flextool-4.2.0 → flextool-4.2.2}/tests/test_solve_config.py +0 -0
  445. {flextool-4.2.0 → flextool-4.2.2}/tests/test_solve_reader.py +0 -0
  446. {flextool-4.2.0 → flextool-4.2.2}/tests/test_stream_concat_temp_cleanup.py +0 -0
  447. {flextool-4.2.0 → flextool-4.2.2}/tests/test_support_load.py +0 -0
  448. {flextool-4.2.0 → flextool-4.2.2}/tests/test_synthesize_invest_dual.py +0 -0
  449. {flextool-4.2.0 → flextool-4.2.2}/tests/test_thread_budget.py +0 -0
  450. {flextool-4.2.0 → flextool-4.2.2}/tests/test_timeline_breaks.py +0 -0
  451. {flextool-4.2.0 → flextool-4.2.2}/tests/test_timings_csv.py +0 -0
  452. {flextool-4.2.0 → flextool-4.2.2}/tests/test_unidirectional_connection.py +0 -0
  453. {flextool-4.2.0 → flextool-4.2.2}/tests/test_unit_online_dt_columns.py +0 -0
  454. {flextool-4.2.0 → flextool-4.2.2}/tests/test_v52_migration.py +0 -0
  455. {flextool-4.2.0 → flextool-4.2.2}/tests/test_v63_migration.py +0 -0
  456. {flextool-4.2.0 → flextool-4.2.2}/tests/test_v64_migration.py +0 -0
  457. {flextool-4.2.0 → flextool-4.2.2}/tests/test_v65_migration.py +0 -0
  458. {flextool-4.2.0 → flextool-4.2.2}/tests/test_v66_migration.py +0 -0
  459. {flextool-4.2.0 → flextool-4.2.2}/tests/test_v67_migration.py +0 -0
  460. {flextool-4.2.0 → flextool-4.2.2}/tests/test_v68_migration.py +0 -0
  461. {flextool-4.2.0 → flextool-4.2.2}/tests/test_v69_migration.py +0 -0
  462. {flextool-4.2.0 → flextool-4.2.2}/tests/test_v70_migration.py +0 -0
  463. {flextool-4.2.0 → flextool-4.2.2}/tests/test_workdir_empty_default.py +0 -0
  464. {flextool-4.2.0 → flextool-4.2.2}/tests/test_xlsx_v77_round_trip.py +0 -0
  465. {flextool-4.2.0 → flextool-4.2.2}/tests/test_xlsx_workflow.py +0 -0
  466. {flextool-4.2.0 → flextool-4.2.2}/tests/test_years_represented.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flextool
3
- Version: 4.2.0
3
+ Version: 4.2.2
4
4
  Summary: IRENA FlexTool is an energy and power systems model for understanding the role of variable power generation in future energy systems.
5
5
  Author: IRENA FlexTool contributors
6
6
  License: © International Renewable Energy Agency 2018-2022
@@ -39,9 +39,9 @@ Classifier: Topic :: Scientific/Engineering
39
39
  Requires-Python: >=3.11
40
40
  Description-Content-Type: text/markdown
41
41
  License-File: LICENSE.txt
42
- Requires-Dist: polar-high>=3.12.2
42
+ Requires-Dist: polar-high>=3.12.3
43
43
  Requires-Dist: highspy<1.16,>=1.15.1
44
- Requires-Dist: polars<2,>=1.40
44
+ Requires-Dist: polars<3,>=1.40
45
45
  Requires-Dist: pandas<4,>=2.2.3
46
46
  Requires-Dist: spinedb-api<0.37,>=0.36.5
47
47
  Requires-Dist: openpyxl
@@ -60,7 +60,7 @@ Provides-Extra: viewer
60
60
  Requires-Dist: tsdownsample>=0.1.3; extra == "viewer"
61
61
  Requires-Dist: networkx>=3.0; extra == "viewer"
62
62
  Provides-Extra: compat
63
- Requires-Dist: polars[rtcompat]<2,>=1.40; extra == "compat"
63
+ Requires-Dist: polars[rtcompat]<3,>=1.40; extra == "compat"
64
64
  Provides-Extra: engine-polars
65
65
  Provides-Extra: dev
66
66
  Requires-Dist: pytest>=8.0; extra == "dev"
@@ -62,6 +62,7 @@ from flextool.engine.input._solve_state import (
62
62
  ActiveTimeEntry,
63
63
  FlexToolConfigError,
64
64
  )
65
+ from flextool.engine.preprocess.emit._base_steps import NEW_STEP_BASE_STEPS, NEW_STEP_BASE_STEPS_SCHEMA
65
66
  from flextool.engine.preprocess._provider_io import (
66
67
  _provider_key,
67
68
  )
@@ -390,12 +391,18 @@ class TimelineConfig:
390
391
  f"{ts} (representative_period_weights)": [str(b) for b in self.rp_weights[ts]]
391
392
  for ts in tl_timesets if self.rp_weights.get(ts)
392
393
  }
394
+ # The fit of the new step is checked only for solves the model
395
+ # runs: an unused solve must not stop the run.
396
+ used_by = [step_solve_in_model_of[ts] for ts in tl_timesets if ts in step_solve_in_model_of]
393
397
  chunks, block_chunks = _aggregate_timeline_pieces(
394
398
  old_steps,
395
399
  {ts: raw_durations[ts] for ts in tl_timesets},
396
400
  step_duration,
397
401
  logger,
398
402
  extra_cuts=base_cuts,
403
+ solve_name=used_by[0] if used_by else first_solve,
404
+ timeline_name=timeline_name,
405
+ check_fit=bool(used_by),
399
406
  )
400
407
  # Guard: every base start on the original timeline must start a step
401
408
  # of the aggregated one, else the rp base chain would sort it last.
@@ -668,18 +675,24 @@ class TimelineConfig:
668
675
 
669
676
  Aggregation rules (lines 405-422 of the flextool source):
670
677
 
678
+ Each new step combines the time steps of the original timeline
679
+ that it covers (any step length; see ``_base_steps_of_new_steps``):
680
+ the rows of those steps are read and combined into one row.
681
+
671
682
  * ``pt_node_inflow.csv`` and ``pbt_node_inflow.csv`` →
672
- ``"sum"`` (inflow energies sum across coarsened steps).
683
+ ``"sum"`` (inflow energies per step sum across coarsened steps).
673
684
  * Every other ``pt_*.csv`` / ``pbt_*.csv``, plus ``pdt_process.csv`` / ``pdt_node.csv`` (the
674
685
  genuine period × time maps — the group key includes their leading ``period`` column, found by
675
- name like every other column before ``time``) → ``"average"``.
686
+ name like every other column before ``time``) → ``"average"``, weighted by the durations of
687
+ the combined steps (the plain mean when they are equally long), rounded to 6 decimals.
676
688
  * Rows with parameter ``"storage_state_reference_value"`` are
677
689
  ALWAYS bypassed (line 474) — they're per-step state targets
678
690
  rather than time-integrated quantities.
679
691
 
680
692
  After file aggregation, ``input/p_node``'s ``inflow`` rows
681
693
  are appended to ``solve_data/pt_node_inflow`` as one entry
682
- per new timestep, scaled by the new step's duration.
694
+ per new timestep, times the number of time steps the new step
695
+ combines (a constant inflow is energy per time step).
683
696
 
684
697
  Args:
685
698
  solve: Active solve name.
@@ -722,6 +735,7 @@ class TimelineConfig:
722
735
  solve in self.new_step_durations
723
736
  and self.new_step_durations[solve] is not None
724
737
  )
738
+ base_steps_key = _provider_key(wf / "solve_data" / NEW_STEP_BASE_STEPS)
725
739
  if not create:
726
740
  for timeseries in timeseries_map:
727
741
  src_path = wf / "input" / timeseries
@@ -732,6 +746,13 @@ class TimelineConfig:
732
746
  if frame is None:
733
747
  continue
734
748
  provider.put(dst_key, frame)
749
+ # Every solve step is one original step (an earlier coarsened
750
+ # solve's rows must not reach this solve).
751
+ provider.put(base_steps_key, pl.DataFrame(schema=NEW_STEP_BASE_STEPS_SCHEMA))
752
+ # No full-timeline inflow energy: scale_to_annual_flow uses the
753
+ # solve's own period share (not an earlier coarsened solve's).
754
+ provider.put(_provider_key(wf / "solve_data" / "node_full_timeline_flow.csv"),
755
+ pl.DataFrame(schema={"node": pl.Utf8, "value": pl.Float64}))
735
756
  return
736
757
 
737
758
  # All timesets used by *solve* must resolve to the same
@@ -751,14 +772,30 @@ class TimelineConfig:
751
772
  logger.error(message)
752
773
  raise FlexToolConfigError(message)
753
774
  timelines_list.append(timeline)
754
- # Pre-build timeline duration lookup for O(1) access per row.
755
- timeline_duration_lookup: dict[str, int] = {}
775
+ # Durations of the original time steps each new step combines
776
+ # (O(1) access per row); a timeline that is not an aggregated
777
+ # one keeps one step per step.
778
+ base_durations: dict[str, list[float]] = {}
779
+ # (original step, the new step that combines it), in timeline order.
780
+ base_step_rows: list[tuple[str, str]] = []
756
781
  for timeline in timelines_list:
757
782
  new_timeline = self.timelines[timeline]
758
- for timeline_row in new_timeline:
759
- timeline_duration_lookup[timeline_row[0]] = int(
760
- float(timeline_row[1])
761
- )
783
+ original = self.original_timeline.get(timeline)
784
+ if original is not None and original in self.timelines:
785
+ base_durations.update(_base_steps_of_new_steps(
786
+ self.timelines[original], new_timeline, logger,
787
+ solve_name=solve, timeline_name=timeline,
788
+ ))
789
+ base_step_rows.extend(_new_step_of_base_steps(self.timelines[original], new_timeline))
790
+ else:
791
+ for timeline_row in new_timeline:
792
+ base_durations[timeline_row[0]] = [float(timeline_row[1])]
793
+ base_step_rows.append((timeline_row[0], timeline_row[0]))
794
+ # The scalings read how many original steps each solve step combines
795
+ # (inflow and peak_inflow are energy per original step).
796
+ provider.put(base_steps_key, pl.DataFrame(
797
+ [(t, s, float(len(base_durations[s]))) for t, s in base_step_rows],
798
+ schema=NEW_STEP_BASE_STEPS_SCHEMA, orient="row"))
762
799
 
763
800
  # node-name → constant-inflow value (used to append per-step
764
801
  # contributions to pt_node_inflow below).
@@ -799,10 +836,8 @@ class TimelineConfig:
799
836
  try:
800
837
  while True:
801
838
  datain = next(rows_iter)
802
- timeline_step_duration = (
803
- timeline_duration_lookup.get(datain[time_index])
804
- )
805
- if timeline_step_duration is not None:
839
+ step_base_durations = base_durations.get(datain[time_index])
840
+ if step_base_durations is not None:
806
841
  values: list[float] = []
807
842
  params = list(datain[0:time_index])
808
843
  row = list(datain[0 : time_index + 1])
@@ -811,7 +846,7 @@ class TimelineConfig:
811
846
  time_index >= 2
812
847
  and datain[1] != "storage_state_reference_value"
813
848
  ) or time_index < 2:
814
- for _i in range(timeline_step_duration - 1):
849
+ for _i in range(len(step_base_durations) - 1):
815
850
  try:
816
851
  datain = next(rows_iter)
817
852
  except StopIteration:
@@ -832,9 +867,17 @@ class TimelineConfig:
832
867
  )
833
868
 
834
869
  if timeseries_map[timeseries] == "average":
835
- out_value = round(
836
- sum(values) / len(values), 6
837
- )
870
+ ds = step_base_durations[: len(values)]
871
+ if all(d == ds[0] for d in ds):
872
+ out_value = round(
873
+ sum(values) / len(values), 6
874
+ )
875
+ else:
876
+ # Rates per hour (profile, limits, prices):
877
+ # the new step keeps their time integral.
878
+ out_value = round(
879
+ sum(v * d for v, d in zip(values, ds)) / sum(ds), 6
880
+ )
838
881
  else:
839
882
  out_value = sum(values)
840
883
  row.append(out_value)
@@ -884,15 +927,16 @@ class TimelineConfig:
884
927
  pass
885
928
 
886
929
  # Append per-step inflow contributions from p_node to
887
- # pt_node_inflow only; scaled by each new step's duration.
930
+ # pt_node_inflow only; times the number of time steps each new
931
+ # step combines (a constant inflow is energy per time step).
888
932
  if timeseries == "pt_node_inflow.csv" and node__inflow:
889
933
  for timeline in timelines_list:
890
934
  new_timeline = self.timelines[timeline]
891
935
  for node__value in node__inflow:
892
936
  for timeline_row in new_timeline:
893
- tl_step_duration = int(float(timeline_row[1]))
894
937
  value = (
895
- float(node__value[1]) * tl_step_duration
938
+ float(node__value[1])
939
+ * len(base_durations[timeline_row[0]])
896
940
  )
897
941
  out_rows.append(
898
942
  [node__value[0], timeline_row[0], value]
@@ -962,9 +1006,10 @@ class TimelineConfig:
962
1006
  for n_name in dict.fromkeys(
963
1007
  list(ts_sum) + list(const_for_node)
964
1008
  ):
1009
+ # A constant inflow is energy per time step: once per step.
965
1010
  energy = (
966
1011
  ts_sum.get(n_name, 0.0)
967
- + const_for_node.get(n_name, 0.0) * total_hours
1012
+ + const_for_node.get(n_name, 0.0) * len(base_steps)
968
1013
  )
969
1014
  flow_rows.append([n_name, energy / duration_in_years])
970
1015
  flow_path = wf / "solve_data" / "node_full_timeline_flow.csv"
@@ -992,12 +1037,59 @@ def _solves_in_model(solve_config: "SolveConfig") -> set[str]:
992
1037
  return out
993
1038
 
994
1039
 
1040
+ def _base_steps_of_new_steps(
1041
+ old_steps: list, new_steps: list, logger: logging.Logger, *, solve_name: str, timeline_name: str,
1042
+ ) -> dict[str, list[float]]:
1043
+ """``{new step: [durations of the original steps it combines]}``.
1044
+
1045
+ The new steps (chunks of :func:`_aggregate_timeline_pieces`) partition
1046
+ the whole original timeline from its first step, each labelled by its
1047
+ first original step, so every original step belongs to the latest new
1048
+ step label seen on the walk.
1049
+ """
1050
+ labels = {step[0] for step in new_steps}
1051
+ out: dict[str, list[float]] = {}
1052
+ current: list[float] | None = None
1053
+ for name, duration in old_steps:
1054
+ if name in labels:
1055
+ current = out.setdefault(name, [])
1056
+ if current is not None:
1057
+ current.append(float(duration))
1058
+ missing = [step[0] for step in new_steps if step[0] not in out]
1059
+ if missing:
1060
+ message = (
1061
+ f"Solve '{solve_name}': time steps {missing[:5]} of the aggregated timeline '{timeline_name}' are not "
1062
+ f"time steps of its original timeline."
1063
+ )
1064
+ logger.error(message)
1065
+ raise FlexToolConfigError(message)
1066
+ return out
1067
+
1068
+
1069
+ def _new_step_of_base_steps(old_steps: list, new_steps: list) -> list[tuple[str, str]]:
1070
+ """``[(original step, the new step that combines it)]`` in timeline order: the walk of
1071
+ :func:`_base_steps_of_new_steps` (every original step belongs to the latest new step label seen)."""
1072
+ labels = {step[0] for step in new_steps}
1073
+ out: list[tuple[str, str]] = []
1074
+ current: str | None = None
1075
+ for name, _duration in old_steps:
1076
+ if name in labels:
1077
+ current = name
1078
+ if current is not None:
1079
+ out.append((name, current))
1080
+ return out
1081
+
1082
+
995
1083
  def _aggregate_timeline_pieces(
996
1084
  old_steps: list,
997
1085
  timeset_blocks: dict[str, list],
998
1086
  step_duration: float,
999
1087
  logger: logging.Logger,
1000
1088
  extra_cuts: dict[str, list[str]] | None = None,
1089
+ *,
1090
+ solve_name: str,
1091
+ timeline_name: str,
1092
+ check_fit: bool = True,
1001
1093
  ) -> tuple[list[tuple[str, str]], dict[str, list[tuple[str, int]]]]:
1002
1094
  """Aggregate a whole timeline to ``step_duration`` hours, piece by piece.
1003
1095
 
@@ -1005,12 +1097,23 @@ def _aggregate_timeline_pieces(
1005
1097
  the runs between the block starts and block ends of ``timeset_blocks``
1006
1098
  (``{timeset: [(start, count), ...]}``) and the steps in ``extra_cuts``
1007
1099
  (``{owner: [step, ...]}``, e.g. representative base-period starts). Each piece is aggregated from its
1008
- first step: a chunk closes once it holds ``step_duration`` hours or more.
1100
+ first step: a chunk closes as soon as its steps add up to ``step_duration`` hours (within a relative
1101
+ tolerance of 1e-4, so steps entered with rounded fractions still fit), and a chunk always holds at least
1102
+ one step. The steps may have any length (0.5 h, 2 h, mixed). The
1103
+ tolerance is capped at half the shortest step, so a long new step still
1104
+ closes at the right step.
1009
1105
  Returns the chunks ``[(first step, str(hours)), ...]`` in timeline order and
1010
1106
  per timeset ``[(block start, number of chunks in the block), ...]`` (a block
1011
1107
  whose start is not on the timeline is kept as given; ``get_active_time``
1012
1108
  skips it). Logs a warning when a block ends with a shorter chunk, naming
1013
1109
  the other timeset when its boundary is what cut the chunk short.
1110
+
1111
+ Raises :class:`FlexToolConfigError` (only with ``check_fit``, i.e. for a
1112
+ solve the model runs) when a chunk that starts inside a block
1113
+ would be longer than ``step_duration``: the new step is not a whole multiple
1114
+ of the timeline's steps, or (steps of different lengths) it would end inside
1115
+ a step. A gap chunk (outside every block, never active) may be longer, e.g.
1116
+ one long filler step between two blocks.
1014
1117
  """
1015
1118
  index_of = {step[0]: i for i, step in enumerate(old_steps)}
1016
1119
  n_steps = len(old_steps)
@@ -1033,6 +1136,12 @@ def _aggregate_timeline_pieces(
1033
1136
  if label in index_of:
1034
1137
  cut_owners.setdefault(index_of[label], set()).add(owner)
1035
1138
  cuts = sorted(c for c in cut_owners if c < n_steps) + [n_steps]
1139
+ block_ranges = [(rng[1], rng[2]) for rngs in ranges.values() for rng in rngs if rng is not None]
1140
+ # Relative to the new step (rounded fractions like 0.333333 h still add
1141
+ # up), but below half a base step, so a long new step never closes one
1142
+ # base step early.
1143
+ positive = [float(step[1]) for step in old_steps if float(step[1]) > 0]
1144
+ tol = min(1e-4 * step_duration, 0.5 * min(positive)) if positive else 0.0
1036
1145
 
1037
1146
  chunks: list[tuple[str, str]] = []
1038
1147
  chunk_first: list[int] = []
@@ -1042,12 +1151,33 @@ def _aggregate_timeline_pieces(
1042
1151
  while i < piece_end:
1043
1152
  first = i
1044
1153
  hours = 0.0
1045
- while i < piece_end and (hours < step_duration or i == first):
1154
+ while i < piece_end and (hours < step_duration - tol or i == first):
1046
1155
  hours += float(old_steps[i][1])
1047
1156
  i += 1
1048
- chunks.append((old_steps[first][0], str(hours)))
1157
+ label = old_steps[first][0]
1158
+ if (check_fit and hours > step_duration + tol
1159
+ and any(b_first <= first < b_end for b_first, b_end in block_ranges)):
1160
+ ds = [float(step[1]) for step in old_steps[first:i]]
1161
+ if all(d == ds[0] for d in ds):
1162
+ message = (
1163
+ f"Solve '{solve_name}': new_stepduration {step_duration:g} h is not a whole multiple of "
1164
+ f"the {ds[0]:g} h time steps of timeline '{timeline_name}' (the new time step starting "
1165
+ f"at '{label}' would be {hours:g} h long). Give a new_stepduration that is a whole "
1166
+ f"multiple of the timeline's time steps."
1167
+ )
1168
+ else:
1169
+ message = (
1170
+ f"Solve '{solve_name}': new_stepduration {step_duration:g} h does not fit the time steps "
1171
+ f"of timeline '{timeline_name}': the new time step starting at '{label}' would end inside "
1172
+ f"base time step '{old_steps[i - 1][0]}' ({hours:g} h in total). On a timeline with time "
1173
+ f"steps of different lengths, every new time step must end where a time step of the "
1174
+ f"timeline ends."
1175
+ )
1176
+ logger.error(message)
1177
+ raise FlexToolConfigError(message)
1178
+ chunks.append((label, str(hours)))
1049
1179
  chunk_first.append(first)
1050
- if hours < step_duration:
1180
+ if hours < step_duration - tol:
1051
1181
  short_chunks.append((first, i, hours))
1052
1182
 
1053
1183
  block_chunks: dict[str, list[tuple[str, int]]] = {}
@@ -0,0 +1,74 @@
1
+ """The original time steps behind each time step of a solve with ``new_stepduration``.
2
+
3
+ ``TimelineConfig.create_averaged_timeseries`` writes ``solve_data/new_step_base_steps``: one row per original time
4
+ step of the coarsened timeline, ``time`` (the original step), ``new_step`` (the solve's time step that combines it;
5
+ its label is its first original step) and ``value`` (how many original steps that new step combines). A solve
6
+ without ``new_stepduration`` gets an empty frame, so a later uncoarsened solve never reads an earlier solve's
7
+ rows. An absent or empty frame means every solve step is one original step.
8
+
9
+ ``inflow`` and ``peak_inflow`` are energy per original time step; a coarsened solve step carries the sum of its
10
+ original steps, so the inflow scaling divides by (or multiplies with) the number of steps it combines.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ from pathlib import Path
15
+
16
+ import polars as pl
17
+
18
+ from flextool.engine.preprocess._provider_io import _provider_key
19
+
20
+ NEW_STEP_BASE_STEPS = "new_step_base_steps.csv"
21
+ NEW_STEP_BASE_STEPS_SCHEMA = {"time": pl.Utf8, "new_step": pl.Utf8, "value": pl.Float64}
22
+
23
+
24
+ def read_new_step_base_steps(solve_data_dir: Path, *, provider) -> tuple[dict[str, float], frozenset[str]]:
25
+ """``({solve step: number of original steps it combines}, original steps absorbed into another step)``.
26
+
27
+ Both are empty when the solve has no ``new_stepduration``. The absorbed steps are the original steps that are
28
+ not a solve step themselves (their energy is in the solve step that combines them).
29
+ """
30
+ df = provider.get(_provider_key(solve_data_dir / NEW_STEP_BASE_STEPS)) if provider is not None else None
31
+ if df is None or df.height == 0:
32
+ return {}, frozenset()
33
+ df = df.select(pl.col("time").cast(pl.Utf8), pl.col("new_step").cast(pl.Utf8),
34
+ pl.col("value").cast(pl.Float64))
35
+ n_of = {t: float(v) for t, s, v in df.iter_rows() if t == s}
36
+ absorbed = frozenset(t for t, s, _v in df.iter_rows() if t != s)
37
+ return n_of, absorbed
38
+
39
+
40
+ def base_steps_of_solve_steps(solve_data_dir: Path, *, provider) -> dict[str, tuple[str, ...]]:
41
+ """``{solve step: the original steps it combines, in timeline order}``; empty without ``new_stepduration``
42
+ (then every solve step is its own original step)."""
43
+ df = provider.get(_provider_key(solve_data_dir / NEW_STEP_BASE_STEPS)) if provider is not None else None
44
+ if df is None or df.height == 0:
45
+ return {}
46
+ out: dict[str, list[str]] = {}
47
+ for t, s in df.select(pl.col("time").cast(pl.Utf8), pl.col("new_step").cast(pl.Utf8)).iter_rows():
48
+ out.setdefault(s, []).append(t)
49
+ return {s: tuple(ts) for s, ts in out.items()}
50
+
51
+
52
+ def complete_steps_by_period(solve_data_dir: Path, periods: list[str], *, provider) -> list[tuple[str, str]]:
53
+ """``[(period, complete step)]`` for every period in ``periods``: the steps of ``steps_complete_solve`` of the
54
+ periods ``d2`` with ``(d2, period)`` in ``period__branch`` — the mapping ``complete_hours_in_period`` sums the
55
+ hours of. A stochastic branch period has no complete steps of its own and gets its parent's; a period without a
56
+ ``period__branch`` row keeps its own steps. When every period maps to itself the ``steps_complete_solve`` rows
57
+ are returned as they are (same order)."""
58
+ def _pairs(stem: str) -> list[tuple[str, str]]:
59
+ df = provider.get(_provider_key(solve_data_dir / stem)) if provider is not None else None
60
+ if df is None or df.height == 0 or df.width < 2:
61
+ return []
62
+ return [(str(a), str(b)) for a, b in df.select(df.columns[:2]).iter_rows()
63
+ if a is not None and b is not None and str(a) and str(b)]
64
+
65
+ complete = _pairs("steps_complete_solve.csv")
66
+ parents_of: dict[str, list[str]] = {}
67
+ for d2, d in _pairs("period__branch.csv"):
68
+ parents_of.setdefault(d, []).append(d2)
69
+ if all(parents_of.get(d, [d]) == [d] for d in periods):
70
+ return complete
71
+ steps_of: dict[str, list[str]] = {}
72
+ for d, t in complete:
73
+ steps_of.setdefault(d, []).append(t)
74
+ return [(d, t) for d in periods for d2 in parents_of.get(d, [d]) for t in steps_of.get(d2, ())]