flextool 4.0.0__py3-none-any.whl

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 (322) hide show
  1. flextool/__init__.py +41 -0
  2. flextool/_mem_sampler.py +193 -0
  3. flextool/_resources.py +43 -0
  4. flextool/calibrate/__init__.py +51 -0
  5. flextool/calibrate/__main__.py +11 -0
  6. flextool/calibrate/_cli.py +316 -0
  7. flextool/calibrate/_db_alt.py +166 -0
  8. flextool/calibrate/_final_outputs.py +110 -0
  9. flextool/calibrate/_guard.py +151 -0
  10. flextool/calibrate/_loop.py +558 -0
  11. flextool/calibrate/_readers.py +223 -0
  12. flextool/calibrate/_report.py +263 -0
  13. flextool/calibrate/_sizing.py +699 -0
  14. flextool/calibrate/_solve.py +134 -0
  15. flextool/calibrate/_solve_status.py +495 -0
  16. flextool/cli/__init__.py +9 -0
  17. flextool/cli/_console.py +51 -0
  18. flextool/cli/_timing.py +147 -0
  19. flextool/cli/cmd_execute_flextool_workflow.py +187 -0
  20. flextool/cli/cmd_export_to_tabular.py +56 -0
  21. flextool/cli/cmd_import_sensitivities.py +75 -0
  22. flextool/cli/cmd_migrate_database.py +13 -0
  23. flextool/cli/cmd_open_results_db.py +269 -0
  24. flextool/cli/cmd_read_matpower.py +66 -0
  25. flextool/cli/cmd_read_old_flextool.py +63 -0
  26. flextool/cli/cmd_read_self_describing_tabular_input.py +50 -0
  27. flextool/cli/cmd_read_tabular_input.py +81 -0
  28. flextool/cli/cmd_run_flextool.py +1095 -0
  29. flextool/cli/cmd_scenario_results.py +284 -0
  30. flextool/cli/cmd_solve_mps.py +169 -0
  31. flextool/cli/cmd_update_flextool.py +17 -0
  32. flextool/cli/cmd_write_outputs.py +125 -0
  33. flextool/common_utils/__init__.py +1 -0
  34. flextool/common_utils/plot_mem_shape.py +77 -0
  35. flextool/common_utils/precision.py +451 -0
  36. flextool/decomposition/__init__.py +0 -0
  37. flextool/decomposition/region_decomposition.py +128 -0
  38. flextool/decomposition/region_filter.py +1261 -0
  39. flextool/engine_polars/__init__.py +110 -0
  40. flextool/engine_polars/_axis_enums.py +742 -0
  41. flextool/engine_polars/_benders.py +3462 -0
  42. flextool/engine_polars/_block_layout.py +1479 -0
  43. flextool/engine_polars/_blocks.py +1515 -0
  44. flextool/engine_polars/_commodity_ladder.py +660 -0
  45. flextool/engine_polars/_cumulative_invest.py +1165 -0
  46. flextool/engine_polars/_db_loader.py +153 -0
  47. flextool/engine_polars/_db_reader.py +127 -0
  48. flextool/engine_polars/_dc_power_flow.py +445 -0
  49. flextool/engine_polars/_delay.py +442 -0
  50. flextool/engine_polars/_derived_arithmetic.py +432 -0
  51. flextool/engine_polars/_derived_block.py +990 -0
  52. flextool/engine_polars/_derived_branch.py +769 -0
  53. flextool/engine_polars/_derived_existing.py +1353 -0
  54. flextool/engine_polars/_derived_npv.py +1297 -0
  55. flextool/engine_polars/_derived_params.py +9850 -0
  56. flextool/engine_polars/_derived_profile.py +881 -0
  57. flextool/engine_polars/_derived_walks.py +276 -0
  58. flextool/engine_polars/_determinism.py +70 -0
  59. flextool/engine_polars/_direct_params.py +2186 -0
  60. flextool/engine_polars/_dump_csvs.py +1009 -0
  61. flextool/engine_polars/_emit_arc_unions.py +1631 -0
  62. flextool/engine_polars/_emit_calc_params.py +729 -0
  63. flextool/engine_polars/_emit_chain_params.py +709 -0
  64. flextool/engine_polars/_emit_co2_accumulators.py +400 -0
  65. flextool/engine_polars/_emit_dispatchers.py +690 -0
  66. flextool/engine_polars/_emit_energy_margin.py +125 -0
  67. flextool/engine_polars/_emit_energy_margin_adder.py +290 -0
  68. flextool/engine_polars/_emit_entity_annual.py +428 -0
  69. flextool/engine_polars/_emit_inflow_scaling.py +1420 -0
  70. flextool/engine_polars/_emit_leaf_sets.py +550 -0
  71. flextool/engine_polars/_emit_lp_scaling.py +665 -0
  72. flextool/engine_polars/_emit_mid_sets.py +859 -0
  73. flextool/engine_polars/_emit_pdt_params.py +759 -0
  74. flextool/engine_polars/_emit_per_solve.py +774 -0
  75. flextool/engine_polars/_emit_period_calc.py +504 -0
  76. flextool/engine_polars/_emit_period_params.py +2398 -0
  77. flextool/engine_polars/_emit_provider_io.py +141 -0
  78. flextool/engine_polars/_emit_reserve.py +574 -0
  79. flextool/engine_polars/_emit_solve_time.py +311 -0
  80. flextool/engine_polars/_emit_solve_writers.py +1249 -0
  81. flextool/engine_polars/_flex_data_accumulator.py +388 -0
  82. flextool/engine_polars/_flex_data_provider.py +478 -0
  83. flextool/engine_polars/_group_slack.py +1253 -0
  84. flextool/engine_polars/_inmemory_reader.py +140 -0
  85. flextool/engine_polars/_input_source.py +336 -0
  86. flextool/engine_polars/_invest_seeds.py +191 -0
  87. flextool/engine_polars/_native_input_writer.py +100 -0
  88. flextool/engine_polars/_native_run_model.py +1348 -0
  89. flextool/engine_polars/_orchestration.py +4314 -0
  90. flextool/engine_polars/_output_writer.py +439 -0
  91. flextool/engine_polars/_param_shapes.py +1595 -0
  92. flextool/engine_polars/_parquet_bundle.py +723 -0
  93. flextool/engine_polars/_pdt_join.py +167 -0
  94. flextool/engine_polars/_pdt_lookup.py +547 -0
  95. flextool/engine_polars/_per_solve_sets.py +335 -0
  96. flextool/engine_polars/_projection_params.py +2056 -0
  97. flextool/engine_polars/_provider_keys.py +173 -0
  98. flextool/engine_polars/_provider_translators.py +225 -0
  99. flextool/engine_polars/_recursive_solve.py +703 -0
  100. flextool/engine_polars/_region_filter.py +2508 -0
  101. flextool/engine_polars/_reserve.py +649 -0
  102. flextool/engine_polars/_solve_acceptance.py +331 -0
  103. flextool/engine_polars/_solve_config.py +1001 -0
  104. flextool/engine_polars/_solve_context.py +885 -0
  105. flextool/engine_polars/_solve_handoff.py +164 -0
  106. flextool/engine_polars/_solve_state.py +232 -0
  107. flextool/engine_polars/_solver_base.py +36 -0
  108. flextool/engine_polars/_solver_dispatch.py +511 -0
  109. flextool/engine_polars/_spinedb_reader.py +1165 -0
  110. flextool/engine_polars/_stochastic.py +593 -0
  111. flextool/engine_polars/_subprocess_solve.py +1838 -0
  112. flextool/engine_polars/_timeline.py +1416 -0
  113. flextool/engine_polars/_vectorize.py +438 -0
  114. flextool/engine_polars/_warm.py +858 -0
  115. flextool/engine_polars/autoscale/__init__.py +107 -0
  116. flextool/engine_polars/autoscale/_config.py +218 -0
  117. flextool/engine_polars/autoscale/_layer2.py +1253 -0
  118. flextool/engine_polars/autoscale/_layer2_types.py +584 -0
  119. flextool/engine_polars/autoscale/_quantity_types.py +621 -0
  120. flextool/engine_polars/autoscale/_report.py +336 -0
  121. flextool/engine_polars/chain.py +259 -0
  122. flextool/engine_polars/input.py +6638 -0
  123. flextool/engine_polars/model.py +4754 -0
  124. flextool/env_check.py +388 -0
  125. flextool/export_to_tabular/__init__.py +5 -0
  126. flextool/export_to_tabular/db_reader.py +224 -0
  127. flextool/export_to_tabular/excel_writer.py +3559 -0
  128. flextool/export_to_tabular/export_settings.yaml +377 -0
  129. flextool/export_to_tabular/export_to_excel.py +227 -0
  130. flextool/export_to_tabular/formatting.py +543 -0
  131. flextool/export_to_tabular/sheet_config.py +876 -0
  132. flextool/gui/__init__.py +0 -0
  133. flextool/gui/__main__.py +118 -0
  134. flextool/gui/calibrate_commands.py +184 -0
  135. flextool/gui/calibrate_jobs.py +424 -0
  136. flextool/gui/check_tree.py +142 -0
  137. flextool/gui/cli_format.py +83 -0
  138. flextool/gui/config_parser.py +68 -0
  139. flextool/gui/data_models.py +362 -0
  140. flextool/gui/db_editor_integration.py +202 -0
  141. flextool/gui/db_version_check.py +269 -0
  142. flextool/gui/dialogs/__init__.py +0 -0
  143. flextool/gui/dialogs/add_dialog.py +1098 -0
  144. flextool/gui/dialogs/calibrate_dialog.py +1259 -0
  145. flextool/gui/dialogs/file_picker.py +473 -0
  146. flextool/gui/dialogs/group_picker.py +299 -0
  147. flextool/gui/dialogs/migration_consent_dialog.py +106 -0
  148. flextool/gui/dialogs/migration_progress_dialog.py +237 -0
  149. flextool/gui/dialogs/plot_dialog.py +459 -0
  150. flextool/gui/dialogs/plot_settings_picker.py +2184 -0
  151. flextool/gui/dialogs/project_dialog.py +426 -0
  152. flextool/gui/dialogs/update_dialog.py +212 -0
  153. flextool/gui/downsampling.py +88 -0
  154. flextool/gui/error_handling.py +50 -0
  155. flextool/gui/execution_manager.py +1715 -0
  156. flextool/gui/execution_window.py +1377 -0
  157. flextool/gui/hover_tooltip.py +111 -0
  158. flextool/gui/input_sources.py +730 -0
  159. flextool/gui/main_window.py +6181 -0
  160. flextool/gui/network_graph.py +215 -0
  161. flextool/gui/output_actions.py +393 -0
  162. flextool/gui/output_log_window.py +159 -0
  163. flextool/gui/platform_utils.py +421 -0
  164. flextool/gui/plot_cache.py +88 -0
  165. flextool/gui/plot_canvas.py +543 -0
  166. flextool/gui/plot_config_reader.py +272 -0
  167. flextool/gui/project_utils.py +100 -0
  168. flextool/gui/result_viewer.py +4394 -0
  169. flextool/gui/scenario_key.py +162 -0
  170. flextool/gui/scenario_lists.py +516 -0
  171. flextool/gui/settings_io.py +360 -0
  172. flextool/gui/solve_reader.py +103 -0
  173. flextool/gui/tree_reorder.py +88 -0
  174. flextool/gui/ui_metrics.py +420 -0
  175. flextool/input_derivation/__init__.py +281 -0
  176. flextool/input_derivation/_commodity_ladder.py +375 -0
  177. flextool/input_derivation/_commodity_ladder_sets.py +70 -0
  178. flextool/input_derivation/_dc_power_flow.py +377 -0
  179. flextool/input_derivation/_method_constants.py +77 -0
  180. flextool/input_derivation/_process_method.py +258 -0
  181. flextool/input_derivation/_specs.py +1026 -0
  182. flextool/input_derivation/_validators.py +321 -0
  183. flextool/lean_parquet.py +159 -0
  184. flextool/model_builder/__init__.py +5 -0
  185. flextool/model_builder/build_model.py +589 -0
  186. flextool/model_builder/encoding.py +67 -0
  187. flextool/model_builder/names.py +34 -0
  188. flextool/model_builder/profiles.py +129 -0
  189. flextool/plot_outputs/__init__.py +14 -0
  190. flextool/plot_outputs/axis_helpers.py +355 -0
  191. flextool/plot_outputs/color_template.py +888 -0
  192. flextool/plot_outputs/config.py +171 -0
  193. flextool/plot_outputs/format_helpers.py +345 -0
  194. flextool/plot_outputs/legend_helpers.py +143 -0
  195. flextool/plot_outputs/orchestrator.py +1141 -0
  196. flextool/plot_outputs/perf.py +37 -0
  197. flextool/plot_outputs/plan.py +1787 -0
  198. flextool/plot_outputs/plot_bars.py +1510 -0
  199. flextool/plot_outputs/plot_bars_detail.py +753 -0
  200. flextool/plot_outputs/plot_lines.py +951 -0
  201. flextool/plot_outputs/shared_manifest.py +564 -0
  202. flextool/plot_outputs/subplot_helpers.py +137 -0
  203. flextool/process_inputs/__init__.py +188 -0
  204. flextool/process_inputs/import_old_excel_input.json +4159 -0
  205. flextool/process_inputs/read_matpower.py +451 -0
  206. flextool/process_inputs/read_old_flextool.py +1288 -0
  207. flextool/process_inputs/read_self_describing_excel.py +1423 -0
  208. flextool/process_inputs/read_tabular_with_specification.py +1114 -0
  209. flextool/process_inputs/write_old_flextool_to_db.py +3077 -0
  210. flextool/process_inputs/write_self_describing_to_db.py +977 -0
  211. flextool/process_inputs/write_to_input_db.py +269 -0
  212. flextool/process_outputs/__init__.py +7 -0
  213. flextool/process_outputs/_annualize.py +55 -0
  214. flextool/process_outputs/_inmemory_helpers.py +292 -0
  215. flextool/process_outputs/_output_meta.py +672 -0
  216. flextool/process_outputs/calc_capacity_flows.py +107 -0
  217. flextool/process_outputs/calc_connections.py +136 -0
  218. flextool/process_outputs/calc_costs.py +260 -0
  219. flextool/process_outputs/calc_group_flows.py +192 -0
  220. flextool/process_outputs/calc_slacks.py +103 -0
  221. flextool/process_outputs/calc_storage_vre.py +160 -0
  222. flextool/process_outputs/drop_levels.py +208 -0
  223. flextool/process_outputs/handoff_writers.py +1315 -0
  224. flextool/process_outputs/out_ancillary.py +544 -0
  225. flextool/process_outputs/out_capacity.py +179 -0
  226. flextool/process_outputs/out_costs.py +334 -0
  227. flextool/process_outputs/out_flowgroup.py +189 -0
  228. flextool/process_outputs/out_flows.py +301 -0
  229. flextool/process_outputs/out_group.py +475 -0
  230. flextool/process_outputs/out_node.py +190 -0
  231. flextool/process_outputs/persist_realized_slice.py +601 -0
  232. flextool/process_outputs/process_results.py +24 -0
  233. flextool/process_outputs/read_highs_solution.py +2256 -0
  234. flextool/process_outputs/read_parameters.py +1799 -0
  235. flextool/process_outputs/read_sets.py +1095 -0
  236. flextool/process_outputs/read_variables.py +553 -0
  237. flextool/process_outputs/solve_order.py +81 -0
  238. flextool/process_outputs/spinedb_replay.py +412 -0
  239. flextool/process_outputs/union_realized_slice.py +224 -0
  240. flextool/process_outputs/write_outputs.py +1286 -0
  241. flextool/process_outputs/write_spinedb.py +1267 -0
  242. flextool/representative_periods/__init__.py +5 -0
  243. flextool/representative_periods/clustering.py +165 -0
  244. flextool/representative_periods/force_include.py +563 -0
  245. flextool/representative_periods/netload.py +365 -0
  246. flextool/representative_periods/netload_inputs.py +345 -0
  247. flextool/representative_periods/netload_iterate.py +722 -0
  248. flextool/representative_periods/preprocess.py +948 -0
  249. flextool/representative_periods/scenario_stack.py +195 -0
  250. flextool/representative_periods/weights.py +124 -0
  251. flextool/scenario_comparison/__init__.py +13 -0
  252. flextool/scenario_comparison/config_builder.py +158 -0
  253. flextool/scenario_comparison/constants.py +20 -0
  254. flextool/scenario_comparison/data_models.py +222 -0
  255. flextool/scenario_comparison/db_reader.py +399 -0
  256. flextool/scenario_comparison/dispatch_data.py +1002 -0
  257. flextool/scenario_comparison/dispatch_mappings.py +205 -0
  258. flextool/scenario_comparison/dispatch_plots.py +691 -0
  259. flextool/scenario_comparison/input_entity_colors.py +319 -0
  260. flextool/scenario_comparison/orchestrator.py +453 -0
  261. flextool/scenario_comparison/plan_union.py +244 -0
  262. flextool/scenario_comparison/plot_settings_seed.py +205 -0
  263. flextool/schemas/AXIS_CONTRACT.md +71 -0
  264. flextool/schemas/canonical_databases/howto_aggregate_output.json +6225 -0
  265. flextool/schemas/canonical_databases/howto_connections.json +5606 -0
  266. flextool/schemas/canonical_databases/howto_demand.json +5518 -0
  267. flextool/schemas/canonical_databases/howto_hydro_reservoir.json +6239 -0
  268. flextool/schemas/canonical_databases/howto_hydro_reservoir_with_pump.json +5933 -0
  269. flextool/schemas/canonical_databases/howto_non_sync_and_curtailment.json +5794 -0
  270. flextool/schemas/canonical_databases/howto_ramp_and_start_up.json +5707 -0
  271. flextool/schemas/canonical_databases/howto_stochastics.json +6032 -0
  272. flextool/schemas/canonical_databases/templates_examples.json +13532 -0
  273. flextool/schemas/canonical_databases/templates_time_settings_only.json +5340 -0
  274. flextool/schemas/comparison_settings_template.json +197 -0
  275. flextool/schemas/default_plot_settings.yaml +260 -0
  276. flextool/schemas/default_plots.yaml +2293 -0
  277. flextool/schemas/flextool_axis_contract.json +303 -0
  278. flextool/schemas/flextool_axis_contract.schema.json +247 -0
  279. flextool/schemas/old_flextool_import_template.json +4443 -0
  280. flextool/schemas/output_info_template.json +48 -0
  281. flextool/schemas/output_settings_template.json +256 -0
  282. flextool/schemas/pre_v26/flextool_template_constant_default.json +2105 -0
  283. flextool/schemas/pre_v26/flextool_template_default_optional_output.json +2152 -0
  284. flextool/schemas/pre_v26/flextool_template_default_value.json +2094 -0
  285. flextool/schemas/pre_v26/flextool_template_drop_down.json +2080 -0
  286. flextool/schemas/pre_v26/flextool_template_lifetime_method.json +1990 -0
  287. flextool/schemas/pre_v26/flextool_template_optional_outputs.json +2094 -0
  288. flextool/schemas/pre_v26/flextool_template_output_node_flows.json +2105 -0
  289. flextool/schemas/pre_v26/flextool_template_results_master.json +493 -0
  290. flextool/schemas/pre_v26/flextool_template_rolling_start_remove.json +2087 -0
  291. flextool/schemas/pre_v26/flextool_template_rolling_window.json +2059 -0
  292. flextool/schemas/pre_v26/flextool_template_storage_binding_defaults.json +46 -0
  293. flextool/schemas/pre_v26/flextool_template_v2.json +1990 -0
  294. flextool/schemas/pre_v26/flextool_template_v25.json +3864 -0
  295. flextool/schemas/spinedb_results_schema.json +581 -0
  296. flextool/schemas/spinedb_schema.json +4636 -0
  297. flextool/solver_config/copt.opt.template +18 -0
  298. flextool/solver_config/cplex.opt.template +25 -0
  299. flextool/solver_config/gurobi.opt.template +18 -0
  300. flextool/solver_config/highs.opt.template +18 -0
  301. flextool/solver_config/xpress.opt.template +26 -0
  302. flextool/spinedb_backend/__init__.py +26 -0
  303. flextool/spinedb_backend/_axis_enums.py +1119 -0
  304. flextool/spinedb_backend/_backend.py +1139 -0
  305. flextool/update_flextool/__init__.py +12 -0
  306. flextool/update_flextool/canonical_databases.py +251 -0
  307. flextool/update_flextool/db_migration.py +7108 -0
  308. flextool/update_flextool/ensure_settings_db.py +138 -0
  309. flextool/update_flextool/export_database.py +103 -0
  310. flextool/update_flextool/extend_tests_fixture.py +772 -0
  311. flextool/update_flextool/generate_canonical.py +274 -0
  312. flextool/update_flextool/initialize_database.py +42 -0
  313. flextool/update_flextool/install_info.py +225 -0
  314. flextool/update_flextool/self_update.py +464 -0
  315. flextool/update_flextool/sync_master_json_template.py +125 -0
  316. flextool/update_flextool/test_fixtures.py +187 -0
  317. flextool-4.0.0.dist-info/METADATA +217 -0
  318. flextool-4.0.0.dist-info/RECORD +322 -0
  319. flextool-4.0.0.dist-info/WHEEL +5 -0
  320. flextool-4.0.0.dist-info/entry_points.txt +17 -0
  321. flextool-4.0.0.dist-info/licenses/LICENSE.txt +19 -0
  322. flextool-4.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,187 @@
1
+ """Round-trip every tests/fixtures/*.json through the current schema migration.
2
+
3
+ Mirrors :mod:`flextool.update_flextool.canonical_databases` for the test
4
+ fixture corpus. The format used here differs from canonical_databases
5
+ (base64-packed values vs. UTF-8 decoded), so this module uses
6
+ ``tests.db_utils.json_to_db`` / ``tests.db_utils.db_to_json`` rather than
7
+ the canonical ``initialize_database`` / ``export_database`` pair.
8
+
9
+ Run after a schema bump alongside ``sync_master_json_template`` and
10
+ ``canonical_databases migrate-all``. See CONTRIBUTING.md.
11
+
12
+ Usage:
13
+ python -m flextool.update_flextool.test_fixtures migrate-all
14
+ python -m flextool.update_flextool.test_fixtures verify
15
+
16
+ The ``verify`` subcommand is intended for CI: it performs the same
17
+ round-trip in a temp file and exits non-zero (printing a short diff
18
+ summary) when any committed fixture is out of sync with the current
19
+ schema.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import argparse
25
+ import json
26
+ import shutil
27
+ import tempfile
28
+ from pathlib import Path
29
+
30
+ from flextool.update_flextool.db_migration import migrate_database
31
+
32
+
33
+ # Resolved at runtime against the repository checkout — these fixtures live
34
+ # under ``tests/fixtures/`` (outside the installed wheel), so we walk up
35
+ # from this module to find the source tree.
36
+ TEST_FIXTURES: tuple[str, ...] = (
37
+ "tests.json",
38
+ "stochastics.json",
39
+ "lh2_three_region.json",
40
+ "h2_trade_parity.json",
41
+ "stochastics_pbt_inflow.json",
42
+ "branch2_parent_period.json",
43
+ "case14_dc_power_flow.json",
44
+ )
45
+
46
+
47
+ def _fixtures_dir() -> Path:
48
+ """Locate ``tests/fixtures/`` from a source checkout.
49
+
50
+ Walks up from this file (``flextool/update_flextool/test_fixtures.py``)
51
+ to the repo root and joins ``tests/fixtures``. Fails loudly if the
52
+ directory is missing — this module is only meaningful in an editable
53
+ install where the fixtures are present on disk.
54
+ """
55
+ here = Path(__file__).resolve()
56
+ # .../flextool-engine/flextool/update_flextool/test_fixtures.py
57
+ repo_root = here.parent.parent.parent
58
+ fixtures = repo_root / "tests" / "fixtures"
59
+ if not fixtures.is_dir():
60
+ raise RuntimeError(
61
+ f"tests/fixtures/ not found at {fixtures}. "
62
+ "This migration must be run from a source checkout."
63
+ )
64
+ return fixtures
65
+
66
+
67
+ def _migrate_one(json_path: Path, out_path: Path) -> None:
68
+ """Round-trip a single fixture: JSON -> SQLite -> migrate -> JSON.
69
+
70
+ Uses ``tests.db_utils.json_to_db`` and ``tests.db_utils.db_to_json``
71
+ so the base64 value-packing convention used by the test fixtures is
72
+ preserved across the round-trip.
73
+ """
74
+ # Imported lazily so this module can be loaded outside a checkout
75
+ # (e.g. the canonical_databases command in a wheel install) without
76
+ # tripping over the test-package layering inversion.
77
+ from tests.db_utils import db_to_json, json_to_db
78
+
79
+ with tempfile.TemporaryDirectory() as tmp:
80
+ sqlite_path = Path(tmp) / "staging.sqlite"
81
+ url = json_to_db(json_path, sqlite_path)
82
+ migrate_database(url)
83
+ db_to_json(sqlite_path, out_path)
84
+
85
+
86
+ def migrate_all() -> None:
87
+ """Migrate every ``tests/fixtures/*.json`` file in place.
88
+
89
+ Writes the result back over the source JSON. Diffs may be large
90
+ when a fixture's ``parameter_definitions`` are catching up to a new
91
+ schema; that is expected.
92
+ """
93
+ fixtures = _fixtures_dir()
94
+ for name in TEST_FIXTURES:
95
+ src = fixtures / name
96
+ if not src.is_file():
97
+ print(f"!! test fixture missing: {src}")
98
+ continue
99
+ with tempfile.TemporaryDirectory() as tmp:
100
+ staging_json = Path(tmp) / name
101
+ _migrate_one(src, staging_json)
102
+ shutil.copyfile(staging_json, src)
103
+ print(f"migrated test fixture: {name}")
104
+
105
+
106
+ def _summarise_diff(committed: Path, regenerated: Path) -> str:
107
+ """Return a short human-readable summary of which top-level sections
108
+ differ between two fixture JSONs.
109
+
110
+ Used by ``verify`` to surface drift without flooding the CI log.
111
+ """
112
+ with open(committed) as f:
113
+ a = json.load(f)
114
+ with open(regenerated) as f:
115
+ b = json.load(f)
116
+ keys = sorted(set(a) | set(b))
117
+ lines = []
118
+ for key in keys:
119
+ av = a.get(key)
120
+ bv = b.get(key)
121
+ if av == bv:
122
+ continue
123
+ la = len(av) if isinstance(av, list) else "?"
124
+ lb = len(bv) if isinstance(bv, list) else "?"
125
+ lines.append(f" {key}: committed={la} regenerated={lb}")
126
+ return "\n".join(lines) if lines else " (no top-level section differs — check value-level)"
127
+
128
+
129
+ def verify_all() -> int:
130
+ """Verify that every committed fixture matches its migrated form.
131
+
132
+ Returns 0 when every file is byte-stable through the round-trip, or
133
+ a positive count of mismatched files otherwise. Intended for CI use
134
+ via ``python -m flextool.update_flextool.test_fixtures verify``.
135
+ """
136
+ fixtures = _fixtures_dir()
137
+ mismatches = 0
138
+ for name in TEST_FIXTURES:
139
+ src = fixtures / name
140
+ if not src.is_file():
141
+ print(f"!! test fixture missing: {src}")
142
+ mismatches += 1
143
+ continue
144
+ with tempfile.TemporaryDirectory() as tmp:
145
+ regenerated = Path(tmp) / name
146
+ _migrate_one(src, regenerated)
147
+ with open(src) as f:
148
+ committed_text = f.read()
149
+ with open(regenerated) as f:
150
+ regenerated_text = f.read()
151
+ if committed_text == regenerated_text:
152
+ print(f"ok: {name}")
153
+ else:
154
+ mismatches += 1
155
+ print(f"DRIFT: {name}")
156
+ print(_summarise_diff(src, regenerated))
157
+ if mismatches:
158
+ print(
159
+ f"\n{mismatches} test fixture(s) out of sync with current schema. "
160
+ "Run `python -m flextool.update_flextool.test_fixtures migrate-all`."
161
+ )
162
+ return mismatches
163
+
164
+
165
+ def main(argv: list[str] | None = None) -> int:
166
+ parser = argparse.ArgumentParser(description=__doc__)
167
+ sub = parser.add_subparsers(dest="cmd", required=True)
168
+ sub.add_parser(
169
+ "migrate-all",
170
+ help="Round-trip every tests/fixtures/*.json through the current schema",
171
+ )
172
+ sub.add_parser(
173
+ "verify",
174
+ help="Check every test fixture is byte-stable through the current schema",
175
+ )
176
+ args = parser.parse_args(argv)
177
+
178
+ if args.cmd == "migrate-all":
179
+ migrate_all()
180
+ return 0
181
+ elif args.cmd == "verify":
182
+ return verify_all()
183
+ return 0
184
+
185
+
186
+ if __name__ == "__main__":
187
+ raise SystemExit(main())
@@ -0,0 +1,217 @@
1
+ Metadata-Version: 2.4
2
+ Name: flextool
3
+ Version: 4.0.0
4
+ Summary: IRENA FlexTool is an energy and power systems model for understanding the role of variable power generation in future energy systems.
5
+ Author: IRENA FlexTool contributors
6
+ License: © International Renewable Energy Agency 2018-2022
7
+
8
+ The FlexTool is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
9
+ as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10
+
11
+ The FlexTool is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12
+ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
13
+
14
+ You should have received a copy of the GNU Lesser General Public License along with the FlexTool.
15
+ If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ Author: Juha Kiviluoma (2017-2022), VTT Technical Research Centre of Finland
18
+
19
+
20
+
21
+
22
+ -----------------------------------------------
23
+ IRENA FlexTool uses the HiGHS solver, which has its own license.
24
+ HiGHS is licensed with MIT license. Please see https://github.com/ERGO-Code/HiGHS for more information.
25
+ Project-URL: Documentation, https://irena-flextool.github.io/flextool/
26
+ Project-URL: Repository, https://github.com/irena-flextool/flextool
27
+ Project-URL: Issues, https://github.com/irena-flextool/flextool/issues
28
+ Keywords: energy-systems,optimization,linear-programming,mixed-integer-programming,power-systems,investment-planning,highs,spine
29
+ Classifier: Development Status :: 5 - Production/Stable
30
+ Classifier: Intended Audience :: Science/Research
31
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
32
+ Classifier: Operating System :: OS Independent
33
+ Classifier: Programming Language :: Python :: 3
34
+ Classifier: Programming Language :: Python :: 3.11
35
+ Classifier: Programming Language :: Python :: 3.12
36
+ Classifier: Programming Language :: Python :: 3.13
37
+ Classifier: Topic :: Scientific/Engineering
38
+ Requires-Python: >=3.11
39
+ Description-Content-Type: text/markdown
40
+ License-File: LICENSE.txt
41
+ Requires-Dist: polar-high>=3.7.0
42
+ Requires-Dist: highspy<=1.14.0
43
+ Requires-Dist: polars>=1.40
44
+ Requires-Dist: pandas
45
+ Requires-Dist: spinedb-api
46
+ Requires-Dist: openpyxl
47
+ Requires-Dist: odfpy
48
+ Requires-Dist: python-calamine
49
+ Requires-Dist: xlsxwriter
50
+ Requires-Dist: pyyaml
51
+ Requires-Dist: matplotlib
52
+ Requires-Dist: psutil
53
+ Requires-Dist: sv-ttk
54
+ Requires-Dist: screeninfo
55
+ Requires-Dist: pywin32; sys_platform == "win32"
56
+ Provides-Extra: toolbox
57
+ Requires-Dist: spinetoolbox; extra == "toolbox"
58
+ Provides-Extra: viewer
59
+ Requires-Dist: tsdownsample>=0.1.3; extra == "viewer"
60
+ Requires-Dist: networkx>=3.0; extra == "viewer"
61
+ Provides-Extra: engine-polars
62
+ Provides-Extra: dev
63
+ Requires-Dist: pytest>=8.0; extra == "dev"
64
+ Requires-Dist: pytest-timeout>=2.3; extra == "dev"
65
+ Requires-Dist: pytest-xdist>=3.0; extra == "dev"
66
+ Requires-Dist: ruff>=0.5; extra == "dev"
67
+ Dynamic: license-file
68
+
69
+ [![PyPI version](https://img.shields.io/pypi/v/flextool.svg)](https://pypi.org/project/flextool/)
70
+ [![Documentation Status](https://img.shields.io/badge/Documentation-passing-brightgreen)](https://irena-flextool.github.io/flextool/)
71
+ [![Python](https://img.shields.io/badge/python-3.11%20|%203.12-blue.svg)](https://www.python.org/downloads/release/python-3120/)
72
+
73
+ ![IRENA FlexTool logo](./docs/img/flextool_logo.png)
74
+
75
+ IRENA FlexTool is an energy and power systems model for understanding the role of variable power generation in future energy systems. It performs capacity expansion planning as well as operational planning.
76
+
77
+ > [!NOTE]
78
+ > The previous `master` branch is deprecated and no longer updated — `main` is
79
+ > now FlexTool. If you still run FlexTool from `master`, migrate with a fresh
80
+ > parallel install (separate directory and venv); see
81
+ > [Installation](#installation) so your existing setup is left untouched.
82
+
83
+ ## What's new
84
+
85
+ - **A lot faster.** The model build and pre-processing now run as pure Python
86
+ instead of the previous GLPSOL-based text pre-processing and AMPL-style model
87
+ translation. There is no LP file written to disk and re-read between build and
88
+ solve.
89
+ - **Automatic problem scaling.** The `polar-high` matrix layer automatically
90
+ scales the optimisation problem before it reaches the solver. Better numerical
91
+ conditioning often speeds up the HiGHS solve considerably — sometimes
92
+ dramatically — so total run time usually improves well beyond the faster build
93
+ alone. The solver is still HiGHS; the gain comes from handing it a
94
+ better-conditioned problem.
95
+ - **A new, easy-to-use interface.** The **FlexTool GUI** is a standalone
96
+ application (`python -m flextool.gui`) that manages projects and input
97
+ sources, runs scenarios, and lets you **browse the results** — a result viewer
98
+ reads outputs directly and supports single-scenario, scenario-comparison, and
99
+ network-graph views with keyboard navigation. See the
100
+ [FlexTool GUI guide](https://irena-flextool.github.io/flextool/flextool_gui_interface/).
101
+ - **Spine Toolbox is still a parallel interface.** Advanced users who build
102
+ multi-tool workflows or integrate FlexTool with other models can keep using
103
+ the [Spine Toolbox workflow](https://irena-flextool.github.io/flextool/spine_toolbox/).
104
+ - **The same model and data.** Existing input databases continue to work; the
105
+ change is in how the matrix is generated and solved, not in the model
106
+ formulation.
107
+
108
+ This is IRENA FlexTool v4 (see the current version in CHANGELOG.md). Report any bugs or difficulties in the [issue tracker](https://github.com/irena-flextool/flextool/issues).
109
+ The previous version of IRENA FlexTool can be found in https://www.irena.org/energytransition/Energy-System-Models-and-Data/IRENA-FlexTool.
110
+
111
+ ## Under the hood: Rust-based matrix generation
112
+
113
+ FlexTool is a thin Python layer over a Rust engine. It reads its input data and
114
+ builds the optimisation matrix as [polars](https://pola.rs/) DataFrames via
115
+ [`polar-high`](https://github.com/nodal-tools/polar-high), then solves with
116
+ [HiGHS](https://highs.dev/). Variables and parameters are polars frames, so
117
+ multiplications become joins and aggregations become group-bys — the heavy
118
+ coefficient work runs inside polars' Rust core rather than as per-coefficient
119
+ Python objects, and the matrix goes straight to HiGHS through `highspy` with no
120
+ intermediate LP/MPS file. `polar-high` is a general-purpose, domain-free
121
+ modelling layer published separately (Apache-2.0, `pip install polar-high`); it
122
+ also applies the automatic problem scaling noted above, and its
123
+ [benchmark](https://nodal-tools.fi/polar-high/compare/benchmark/) compares it
124
+ against linopy and Pyomo on the same HiGHS solver.
125
+
126
+ ## Installation
127
+
128
+ ### Check your Python (and possibly install it)
129
+
130
+ FlexTool requires **Python 3.11+**.
131
+
132
+ Open a terminal (Windows: Command Prompt or PowerShell; macOS: Terminal; Linux: your shell)
133
+ and check which Python you have:
134
+
135
+ | Platform | Command |
136
+ |---|---|
137
+ | Windows | `py --version` (falls back to `python --version`) |
138
+ | macOS / Linux | `python3 --version` |
139
+
140
+ The output looks like `Python 3.12.4`. If the command is not found, prints
141
+ something below 3.11, or (on Windows) opens the Microsoft Store, install Python
142
+ from https://www.python.org/downloads/ — on Windows tick
143
+ **"Add python.exe to PATH"** in the installer. Then close and reopen the
144
+ terminal so the new PATH takes effect.
145
+
146
+ > On Windows, use `py` in place of `python` in the commands below if `python`
147
+ > is not recognised. If several versions are installed, `py -3.11 --version`
148
+ > (or `-3.12`, …) picks a specific one, and `py -0` lists them all.
149
+ > On macOS/Linux, `python` often means Python 2 or does not exist at all, so
150
+ > use `python3`. Inside an activated virtual environment, plain `python` always
151
+ > means that environment's Python on every platform.
152
+
153
+
154
+ ### Install from PyPI (recommended)
155
+
156
+ Install FlexTool into a virtual environment to keep its packages separate from
157
+ other Python applications. If you want to install into the base Python (perhaps
158
+ because you do not otherwise use Python), just run the last command from below,
159
+ `pip install flextool`.
160
+
161
+ ```
162
+ python -m venv .venv # Windows: py -m venv .venv
163
+ # macOS/Linux: python3 -m venv .venv
164
+ # activate — Windows PowerShell: .\.venv\Scripts\Activate.ps1
165
+ # Windows CMD: .\.venv\Scripts\activate.bat
166
+ # Linux / macOS: . .venv/bin/activate
167
+ python -m pip install --upgrade pip
168
+ pip install flextool
169
+ ```
170
+
171
+ Your prompt should now show `(.venv)`. Verify with `python --version` — it
172
+ should report 3.11 or newer.
173
+
174
+ (If PowerShell blocks the activate script, run once per user:
175
+ `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`.)
176
+
177
+ Launch the FlexTool GUI:
178
+
179
+ ```
180
+ flextool
181
+ ```
182
+
183
+ — equivalently `python -m flextool.gui`. The GUI creates and manages your
184
+ projects and input databases; to seed the bundled template and example
185
+ databases (handy for the CLI or Spine Toolbox workflows) run `flextool-update`.
186
+
187
+ ### Install from source (developers / latest `main`)
188
+
189
+ To participate in FlexTool development — or to run the not-yet-released `main`
190
+ — clone the repo and install it editable:
191
+
192
+ ```powershell
193
+ git clone https://github.com/irena-flextool/flextool.git
194
+ cd flextool
195
+ python -m venv .venv
196
+ .\.venv\Scripts\Activate.ps1 # Linux / macOS: . .venv/bin/activate
197
+ python -m pip install --upgrade pip
198
+ pip install -e .
199
+ ```
200
+
201
+ The editable install picks up source changes on `git pull`; re-run
202
+ `pip install -e .` only when a dependency pin in `pyproject.toml` changes.
203
+
204
+ ### Update FlexTool:
205
+
206
+ ```
207
+ # If using virtual environment (venv), then activate venv first:
208
+ # Windows PowerShell: .\.venv\Scripts\Activate.ps1
209
+ # Windows CMD: .\.venv\Scripts\activate.bat
210
+ # Linux / macOS: . .venv/bin/activate
211
+ pip install --upgrade flextool
212
+ ```
213
+
214
+ ## [Documentation](https://irena-flextool.github.io/flextool/) and [installation](https://irena-flextool.github.io/flextool/install_toolbox/)
215
+
216
+ > [!IMPORTANT]
217
+ > Installation, user guide and ***documentation*** can be found at: https://irena-flextool.github.io/flextool/.