flextool 4.0.0__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 (401) hide show
  1. flextool-4.0.0/LICENSE.txt +19 -0
  2. flextool-4.0.0/PKG-INFO +217 -0
  3. flextool-4.0.0/README.md +149 -0
  4. flextool-4.0.0/flextool/__init__.py +41 -0
  5. flextool-4.0.0/flextool/_mem_sampler.py +193 -0
  6. flextool-4.0.0/flextool/_resources.py +43 -0
  7. flextool-4.0.0/flextool/calibrate/__init__.py +51 -0
  8. flextool-4.0.0/flextool/calibrate/__main__.py +11 -0
  9. flextool-4.0.0/flextool/calibrate/_cli.py +316 -0
  10. flextool-4.0.0/flextool/calibrate/_db_alt.py +166 -0
  11. flextool-4.0.0/flextool/calibrate/_final_outputs.py +110 -0
  12. flextool-4.0.0/flextool/calibrate/_guard.py +151 -0
  13. flextool-4.0.0/flextool/calibrate/_loop.py +558 -0
  14. flextool-4.0.0/flextool/calibrate/_readers.py +223 -0
  15. flextool-4.0.0/flextool/calibrate/_report.py +263 -0
  16. flextool-4.0.0/flextool/calibrate/_sizing.py +699 -0
  17. flextool-4.0.0/flextool/calibrate/_solve.py +134 -0
  18. flextool-4.0.0/flextool/calibrate/_solve_status.py +495 -0
  19. flextool-4.0.0/flextool/cli/__init__.py +9 -0
  20. flextool-4.0.0/flextool/cli/_console.py +51 -0
  21. flextool-4.0.0/flextool/cli/_timing.py +147 -0
  22. flextool-4.0.0/flextool/cli/cmd_execute_flextool_workflow.py +187 -0
  23. flextool-4.0.0/flextool/cli/cmd_export_to_tabular.py +56 -0
  24. flextool-4.0.0/flextool/cli/cmd_import_sensitivities.py +75 -0
  25. flextool-4.0.0/flextool/cli/cmd_migrate_database.py +13 -0
  26. flextool-4.0.0/flextool/cli/cmd_open_results_db.py +269 -0
  27. flextool-4.0.0/flextool/cli/cmd_read_matpower.py +66 -0
  28. flextool-4.0.0/flextool/cli/cmd_read_old_flextool.py +63 -0
  29. flextool-4.0.0/flextool/cli/cmd_read_self_describing_tabular_input.py +50 -0
  30. flextool-4.0.0/flextool/cli/cmd_read_tabular_input.py +81 -0
  31. flextool-4.0.0/flextool/cli/cmd_run_flextool.py +1095 -0
  32. flextool-4.0.0/flextool/cli/cmd_scenario_results.py +284 -0
  33. flextool-4.0.0/flextool/cli/cmd_solve_mps.py +169 -0
  34. flextool-4.0.0/flextool/cli/cmd_update_flextool.py +17 -0
  35. flextool-4.0.0/flextool/cli/cmd_write_outputs.py +125 -0
  36. flextool-4.0.0/flextool/common_utils/__init__.py +1 -0
  37. flextool-4.0.0/flextool/common_utils/plot_mem_shape.py +77 -0
  38. flextool-4.0.0/flextool/common_utils/precision.py +451 -0
  39. flextool-4.0.0/flextool/decomposition/__init__.py +0 -0
  40. flextool-4.0.0/flextool/decomposition/region_decomposition.py +128 -0
  41. flextool-4.0.0/flextool/decomposition/region_filter.py +1261 -0
  42. flextool-4.0.0/flextool/engine_polars/__init__.py +110 -0
  43. flextool-4.0.0/flextool/engine_polars/_axis_enums.py +742 -0
  44. flextool-4.0.0/flextool/engine_polars/_benders.py +3462 -0
  45. flextool-4.0.0/flextool/engine_polars/_block_layout.py +1479 -0
  46. flextool-4.0.0/flextool/engine_polars/_blocks.py +1515 -0
  47. flextool-4.0.0/flextool/engine_polars/_commodity_ladder.py +660 -0
  48. flextool-4.0.0/flextool/engine_polars/_cumulative_invest.py +1165 -0
  49. flextool-4.0.0/flextool/engine_polars/_db_loader.py +153 -0
  50. flextool-4.0.0/flextool/engine_polars/_db_reader.py +127 -0
  51. flextool-4.0.0/flextool/engine_polars/_dc_power_flow.py +445 -0
  52. flextool-4.0.0/flextool/engine_polars/_delay.py +442 -0
  53. flextool-4.0.0/flextool/engine_polars/_derived_arithmetic.py +432 -0
  54. flextool-4.0.0/flextool/engine_polars/_derived_block.py +990 -0
  55. flextool-4.0.0/flextool/engine_polars/_derived_branch.py +769 -0
  56. flextool-4.0.0/flextool/engine_polars/_derived_existing.py +1353 -0
  57. flextool-4.0.0/flextool/engine_polars/_derived_npv.py +1297 -0
  58. flextool-4.0.0/flextool/engine_polars/_derived_params.py +9850 -0
  59. flextool-4.0.0/flextool/engine_polars/_derived_profile.py +881 -0
  60. flextool-4.0.0/flextool/engine_polars/_derived_walks.py +276 -0
  61. flextool-4.0.0/flextool/engine_polars/_determinism.py +70 -0
  62. flextool-4.0.0/flextool/engine_polars/_direct_params.py +2186 -0
  63. flextool-4.0.0/flextool/engine_polars/_dump_csvs.py +1009 -0
  64. flextool-4.0.0/flextool/engine_polars/_emit_arc_unions.py +1631 -0
  65. flextool-4.0.0/flextool/engine_polars/_emit_calc_params.py +729 -0
  66. flextool-4.0.0/flextool/engine_polars/_emit_chain_params.py +709 -0
  67. flextool-4.0.0/flextool/engine_polars/_emit_co2_accumulators.py +400 -0
  68. flextool-4.0.0/flextool/engine_polars/_emit_dispatchers.py +690 -0
  69. flextool-4.0.0/flextool/engine_polars/_emit_energy_margin.py +125 -0
  70. flextool-4.0.0/flextool/engine_polars/_emit_energy_margin_adder.py +290 -0
  71. flextool-4.0.0/flextool/engine_polars/_emit_entity_annual.py +428 -0
  72. flextool-4.0.0/flextool/engine_polars/_emit_inflow_scaling.py +1420 -0
  73. flextool-4.0.0/flextool/engine_polars/_emit_leaf_sets.py +550 -0
  74. flextool-4.0.0/flextool/engine_polars/_emit_lp_scaling.py +665 -0
  75. flextool-4.0.0/flextool/engine_polars/_emit_mid_sets.py +859 -0
  76. flextool-4.0.0/flextool/engine_polars/_emit_pdt_params.py +759 -0
  77. flextool-4.0.0/flextool/engine_polars/_emit_per_solve.py +774 -0
  78. flextool-4.0.0/flextool/engine_polars/_emit_period_calc.py +504 -0
  79. flextool-4.0.0/flextool/engine_polars/_emit_period_params.py +2398 -0
  80. flextool-4.0.0/flextool/engine_polars/_emit_provider_io.py +141 -0
  81. flextool-4.0.0/flextool/engine_polars/_emit_reserve.py +574 -0
  82. flextool-4.0.0/flextool/engine_polars/_emit_solve_time.py +311 -0
  83. flextool-4.0.0/flextool/engine_polars/_emit_solve_writers.py +1249 -0
  84. flextool-4.0.0/flextool/engine_polars/_flex_data_accumulator.py +388 -0
  85. flextool-4.0.0/flextool/engine_polars/_flex_data_provider.py +478 -0
  86. flextool-4.0.0/flextool/engine_polars/_group_slack.py +1253 -0
  87. flextool-4.0.0/flextool/engine_polars/_inmemory_reader.py +140 -0
  88. flextool-4.0.0/flextool/engine_polars/_input_source.py +336 -0
  89. flextool-4.0.0/flextool/engine_polars/_invest_seeds.py +191 -0
  90. flextool-4.0.0/flextool/engine_polars/_native_input_writer.py +100 -0
  91. flextool-4.0.0/flextool/engine_polars/_native_run_model.py +1348 -0
  92. flextool-4.0.0/flextool/engine_polars/_orchestration.py +4314 -0
  93. flextool-4.0.0/flextool/engine_polars/_output_writer.py +439 -0
  94. flextool-4.0.0/flextool/engine_polars/_param_shapes.py +1595 -0
  95. flextool-4.0.0/flextool/engine_polars/_parquet_bundle.py +723 -0
  96. flextool-4.0.0/flextool/engine_polars/_pdt_join.py +167 -0
  97. flextool-4.0.0/flextool/engine_polars/_pdt_lookup.py +547 -0
  98. flextool-4.0.0/flextool/engine_polars/_per_solve_sets.py +335 -0
  99. flextool-4.0.0/flextool/engine_polars/_projection_params.py +2056 -0
  100. flextool-4.0.0/flextool/engine_polars/_provider_keys.py +173 -0
  101. flextool-4.0.0/flextool/engine_polars/_provider_translators.py +225 -0
  102. flextool-4.0.0/flextool/engine_polars/_recursive_solve.py +703 -0
  103. flextool-4.0.0/flextool/engine_polars/_region_filter.py +2508 -0
  104. flextool-4.0.0/flextool/engine_polars/_reserve.py +649 -0
  105. flextool-4.0.0/flextool/engine_polars/_solve_acceptance.py +331 -0
  106. flextool-4.0.0/flextool/engine_polars/_solve_config.py +1001 -0
  107. flextool-4.0.0/flextool/engine_polars/_solve_context.py +885 -0
  108. flextool-4.0.0/flextool/engine_polars/_solve_handoff.py +164 -0
  109. flextool-4.0.0/flextool/engine_polars/_solve_state.py +232 -0
  110. flextool-4.0.0/flextool/engine_polars/_solver_base.py +36 -0
  111. flextool-4.0.0/flextool/engine_polars/_solver_dispatch.py +511 -0
  112. flextool-4.0.0/flextool/engine_polars/_spinedb_reader.py +1165 -0
  113. flextool-4.0.0/flextool/engine_polars/_stochastic.py +593 -0
  114. flextool-4.0.0/flextool/engine_polars/_subprocess_solve.py +1838 -0
  115. flextool-4.0.0/flextool/engine_polars/_timeline.py +1416 -0
  116. flextool-4.0.0/flextool/engine_polars/_vectorize.py +438 -0
  117. flextool-4.0.0/flextool/engine_polars/_warm.py +858 -0
  118. flextool-4.0.0/flextool/engine_polars/autoscale/__init__.py +107 -0
  119. flextool-4.0.0/flextool/engine_polars/autoscale/_config.py +218 -0
  120. flextool-4.0.0/flextool/engine_polars/autoscale/_layer2.py +1253 -0
  121. flextool-4.0.0/flextool/engine_polars/autoscale/_layer2_types.py +584 -0
  122. flextool-4.0.0/flextool/engine_polars/autoscale/_quantity_types.py +621 -0
  123. flextool-4.0.0/flextool/engine_polars/autoscale/_report.py +336 -0
  124. flextool-4.0.0/flextool/engine_polars/chain.py +259 -0
  125. flextool-4.0.0/flextool/engine_polars/input.py +6638 -0
  126. flextool-4.0.0/flextool/engine_polars/model.py +4754 -0
  127. flextool-4.0.0/flextool/env_check.py +388 -0
  128. flextool-4.0.0/flextool/export_to_tabular/__init__.py +5 -0
  129. flextool-4.0.0/flextool/export_to_tabular/db_reader.py +224 -0
  130. flextool-4.0.0/flextool/export_to_tabular/excel_writer.py +3559 -0
  131. flextool-4.0.0/flextool/export_to_tabular/export_settings.yaml +377 -0
  132. flextool-4.0.0/flextool/export_to_tabular/export_to_excel.py +227 -0
  133. flextool-4.0.0/flextool/export_to_tabular/formatting.py +543 -0
  134. flextool-4.0.0/flextool/export_to_tabular/sheet_config.py +876 -0
  135. flextool-4.0.0/flextool/gui/__init__.py +0 -0
  136. flextool-4.0.0/flextool/gui/__main__.py +118 -0
  137. flextool-4.0.0/flextool/gui/calibrate_commands.py +184 -0
  138. flextool-4.0.0/flextool/gui/calibrate_jobs.py +424 -0
  139. flextool-4.0.0/flextool/gui/check_tree.py +142 -0
  140. flextool-4.0.0/flextool/gui/cli_format.py +83 -0
  141. flextool-4.0.0/flextool/gui/config_parser.py +68 -0
  142. flextool-4.0.0/flextool/gui/data_models.py +362 -0
  143. flextool-4.0.0/flextool/gui/db_editor_integration.py +202 -0
  144. flextool-4.0.0/flextool/gui/db_version_check.py +269 -0
  145. flextool-4.0.0/flextool/gui/dialogs/__init__.py +0 -0
  146. flextool-4.0.0/flextool/gui/dialogs/add_dialog.py +1098 -0
  147. flextool-4.0.0/flextool/gui/dialogs/calibrate_dialog.py +1259 -0
  148. flextool-4.0.0/flextool/gui/dialogs/file_picker.py +473 -0
  149. flextool-4.0.0/flextool/gui/dialogs/group_picker.py +299 -0
  150. flextool-4.0.0/flextool/gui/dialogs/migration_consent_dialog.py +106 -0
  151. flextool-4.0.0/flextool/gui/dialogs/migration_progress_dialog.py +237 -0
  152. flextool-4.0.0/flextool/gui/dialogs/plot_dialog.py +459 -0
  153. flextool-4.0.0/flextool/gui/dialogs/plot_settings_picker.py +2184 -0
  154. flextool-4.0.0/flextool/gui/dialogs/project_dialog.py +426 -0
  155. flextool-4.0.0/flextool/gui/dialogs/update_dialog.py +212 -0
  156. flextool-4.0.0/flextool/gui/downsampling.py +88 -0
  157. flextool-4.0.0/flextool/gui/error_handling.py +50 -0
  158. flextool-4.0.0/flextool/gui/execution_manager.py +1715 -0
  159. flextool-4.0.0/flextool/gui/execution_window.py +1377 -0
  160. flextool-4.0.0/flextool/gui/hover_tooltip.py +111 -0
  161. flextool-4.0.0/flextool/gui/input_sources.py +730 -0
  162. flextool-4.0.0/flextool/gui/main_window.py +6181 -0
  163. flextool-4.0.0/flextool/gui/network_graph.py +215 -0
  164. flextool-4.0.0/flextool/gui/output_actions.py +393 -0
  165. flextool-4.0.0/flextool/gui/output_log_window.py +159 -0
  166. flextool-4.0.0/flextool/gui/platform_utils.py +421 -0
  167. flextool-4.0.0/flextool/gui/plot_cache.py +88 -0
  168. flextool-4.0.0/flextool/gui/plot_canvas.py +543 -0
  169. flextool-4.0.0/flextool/gui/plot_config_reader.py +272 -0
  170. flextool-4.0.0/flextool/gui/project_utils.py +100 -0
  171. flextool-4.0.0/flextool/gui/result_viewer.py +4394 -0
  172. flextool-4.0.0/flextool/gui/scenario_key.py +162 -0
  173. flextool-4.0.0/flextool/gui/scenario_lists.py +516 -0
  174. flextool-4.0.0/flextool/gui/settings_io.py +360 -0
  175. flextool-4.0.0/flextool/gui/solve_reader.py +103 -0
  176. flextool-4.0.0/flextool/gui/tree_reorder.py +88 -0
  177. flextool-4.0.0/flextool/gui/ui_metrics.py +420 -0
  178. flextool-4.0.0/flextool/input_derivation/__init__.py +281 -0
  179. flextool-4.0.0/flextool/input_derivation/_commodity_ladder.py +375 -0
  180. flextool-4.0.0/flextool/input_derivation/_commodity_ladder_sets.py +70 -0
  181. flextool-4.0.0/flextool/input_derivation/_dc_power_flow.py +377 -0
  182. flextool-4.0.0/flextool/input_derivation/_method_constants.py +77 -0
  183. flextool-4.0.0/flextool/input_derivation/_process_method.py +258 -0
  184. flextool-4.0.0/flextool/input_derivation/_specs.py +1026 -0
  185. flextool-4.0.0/flextool/input_derivation/_validators.py +321 -0
  186. flextool-4.0.0/flextool/lean_parquet.py +159 -0
  187. flextool-4.0.0/flextool/model_builder/__init__.py +5 -0
  188. flextool-4.0.0/flextool/model_builder/build_model.py +589 -0
  189. flextool-4.0.0/flextool/model_builder/encoding.py +67 -0
  190. flextool-4.0.0/flextool/model_builder/names.py +34 -0
  191. flextool-4.0.0/flextool/model_builder/profiles.py +129 -0
  192. flextool-4.0.0/flextool/plot_outputs/__init__.py +14 -0
  193. flextool-4.0.0/flextool/plot_outputs/axis_helpers.py +355 -0
  194. flextool-4.0.0/flextool/plot_outputs/color_template.py +888 -0
  195. flextool-4.0.0/flextool/plot_outputs/config.py +171 -0
  196. flextool-4.0.0/flextool/plot_outputs/format_helpers.py +345 -0
  197. flextool-4.0.0/flextool/plot_outputs/legend_helpers.py +143 -0
  198. flextool-4.0.0/flextool/plot_outputs/orchestrator.py +1141 -0
  199. flextool-4.0.0/flextool/plot_outputs/perf.py +37 -0
  200. flextool-4.0.0/flextool/plot_outputs/plan.py +1787 -0
  201. flextool-4.0.0/flextool/plot_outputs/plot_bars.py +1510 -0
  202. flextool-4.0.0/flextool/plot_outputs/plot_bars_detail.py +753 -0
  203. flextool-4.0.0/flextool/plot_outputs/plot_lines.py +951 -0
  204. flextool-4.0.0/flextool/plot_outputs/shared_manifest.py +564 -0
  205. flextool-4.0.0/flextool/plot_outputs/subplot_helpers.py +137 -0
  206. flextool-4.0.0/flextool/process_inputs/__init__.py +188 -0
  207. flextool-4.0.0/flextool/process_inputs/import_old_excel_input.json +4159 -0
  208. flextool-4.0.0/flextool/process_inputs/read_matpower.py +451 -0
  209. flextool-4.0.0/flextool/process_inputs/read_old_flextool.py +1288 -0
  210. flextool-4.0.0/flextool/process_inputs/read_self_describing_excel.py +1423 -0
  211. flextool-4.0.0/flextool/process_inputs/read_tabular_with_specification.py +1114 -0
  212. flextool-4.0.0/flextool/process_inputs/write_old_flextool_to_db.py +3077 -0
  213. flextool-4.0.0/flextool/process_inputs/write_self_describing_to_db.py +977 -0
  214. flextool-4.0.0/flextool/process_inputs/write_to_input_db.py +269 -0
  215. flextool-4.0.0/flextool/process_outputs/__init__.py +7 -0
  216. flextool-4.0.0/flextool/process_outputs/_annualize.py +55 -0
  217. flextool-4.0.0/flextool/process_outputs/_inmemory_helpers.py +292 -0
  218. flextool-4.0.0/flextool/process_outputs/_output_meta.py +672 -0
  219. flextool-4.0.0/flextool/process_outputs/calc_capacity_flows.py +107 -0
  220. flextool-4.0.0/flextool/process_outputs/calc_connections.py +136 -0
  221. flextool-4.0.0/flextool/process_outputs/calc_costs.py +260 -0
  222. flextool-4.0.0/flextool/process_outputs/calc_group_flows.py +192 -0
  223. flextool-4.0.0/flextool/process_outputs/calc_slacks.py +103 -0
  224. flextool-4.0.0/flextool/process_outputs/calc_storage_vre.py +160 -0
  225. flextool-4.0.0/flextool/process_outputs/drop_levels.py +208 -0
  226. flextool-4.0.0/flextool/process_outputs/handoff_writers.py +1315 -0
  227. flextool-4.0.0/flextool/process_outputs/out_ancillary.py +544 -0
  228. flextool-4.0.0/flextool/process_outputs/out_capacity.py +179 -0
  229. flextool-4.0.0/flextool/process_outputs/out_costs.py +334 -0
  230. flextool-4.0.0/flextool/process_outputs/out_flowgroup.py +189 -0
  231. flextool-4.0.0/flextool/process_outputs/out_flows.py +301 -0
  232. flextool-4.0.0/flextool/process_outputs/out_group.py +475 -0
  233. flextool-4.0.0/flextool/process_outputs/out_node.py +190 -0
  234. flextool-4.0.0/flextool/process_outputs/persist_realized_slice.py +601 -0
  235. flextool-4.0.0/flextool/process_outputs/process_results.py +24 -0
  236. flextool-4.0.0/flextool/process_outputs/read_highs_solution.py +2256 -0
  237. flextool-4.0.0/flextool/process_outputs/read_parameters.py +1799 -0
  238. flextool-4.0.0/flextool/process_outputs/read_sets.py +1095 -0
  239. flextool-4.0.0/flextool/process_outputs/read_variables.py +553 -0
  240. flextool-4.0.0/flextool/process_outputs/solve_order.py +81 -0
  241. flextool-4.0.0/flextool/process_outputs/spinedb_replay.py +412 -0
  242. flextool-4.0.0/flextool/process_outputs/union_realized_slice.py +224 -0
  243. flextool-4.0.0/flextool/process_outputs/write_outputs.py +1286 -0
  244. flextool-4.0.0/flextool/process_outputs/write_spinedb.py +1267 -0
  245. flextool-4.0.0/flextool/representative_periods/__init__.py +5 -0
  246. flextool-4.0.0/flextool/representative_periods/clustering.py +165 -0
  247. flextool-4.0.0/flextool/representative_periods/force_include.py +563 -0
  248. flextool-4.0.0/flextool/representative_periods/netload.py +365 -0
  249. flextool-4.0.0/flextool/representative_periods/netload_inputs.py +345 -0
  250. flextool-4.0.0/flextool/representative_periods/netload_iterate.py +722 -0
  251. flextool-4.0.0/flextool/representative_periods/preprocess.py +948 -0
  252. flextool-4.0.0/flextool/representative_periods/scenario_stack.py +195 -0
  253. flextool-4.0.0/flextool/representative_periods/weights.py +124 -0
  254. flextool-4.0.0/flextool/scenario_comparison/__init__.py +13 -0
  255. flextool-4.0.0/flextool/scenario_comparison/config_builder.py +158 -0
  256. flextool-4.0.0/flextool/scenario_comparison/constants.py +20 -0
  257. flextool-4.0.0/flextool/scenario_comparison/data_models.py +222 -0
  258. flextool-4.0.0/flextool/scenario_comparison/db_reader.py +399 -0
  259. flextool-4.0.0/flextool/scenario_comparison/dispatch_data.py +1002 -0
  260. flextool-4.0.0/flextool/scenario_comparison/dispatch_mappings.py +205 -0
  261. flextool-4.0.0/flextool/scenario_comparison/dispatch_plots.py +691 -0
  262. flextool-4.0.0/flextool/scenario_comparison/input_entity_colors.py +319 -0
  263. flextool-4.0.0/flextool/scenario_comparison/orchestrator.py +453 -0
  264. flextool-4.0.0/flextool/scenario_comparison/plan_union.py +244 -0
  265. flextool-4.0.0/flextool/scenario_comparison/plot_settings_seed.py +205 -0
  266. flextool-4.0.0/flextool/schemas/AXIS_CONTRACT.md +71 -0
  267. flextool-4.0.0/flextool/schemas/canonical_databases/howto_aggregate_output.json +6225 -0
  268. flextool-4.0.0/flextool/schemas/canonical_databases/howto_connections.json +5606 -0
  269. flextool-4.0.0/flextool/schemas/canonical_databases/howto_demand.json +5518 -0
  270. flextool-4.0.0/flextool/schemas/canonical_databases/howto_hydro_reservoir.json +6239 -0
  271. flextool-4.0.0/flextool/schemas/canonical_databases/howto_hydro_reservoir_with_pump.json +5933 -0
  272. flextool-4.0.0/flextool/schemas/canonical_databases/howto_non_sync_and_curtailment.json +5794 -0
  273. flextool-4.0.0/flextool/schemas/canonical_databases/howto_ramp_and_start_up.json +5707 -0
  274. flextool-4.0.0/flextool/schemas/canonical_databases/howto_stochastics.json +6032 -0
  275. flextool-4.0.0/flextool/schemas/canonical_databases/templates_examples.json +13532 -0
  276. flextool-4.0.0/flextool/schemas/canonical_databases/templates_time_settings_only.json +5340 -0
  277. flextool-4.0.0/flextool/schemas/comparison_settings_template.json +197 -0
  278. flextool-4.0.0/flextool/schemas/default_plot_settings.yaml +260 -0
  279. flextool-4.0.0/flextool/schemas/default_plots.yaml +2293 -0
  280. flextool-4.0.0/flextool/schemas/flextool_axis_contract.json +303 -0
  281. flextool-4.0.0/flextool/schemas/flextool_axis_contract.schema.json +247 -0
  282. flextool-4.0.0/flextool/schemas/old_flextool_import_template.json +4443 -0
  283. flextool-4.0.0/flextool/schemas/output_info_template.json +48 -0
  284. flextool-4.0.0/flextool/schemas/output_settings_template.json +256 -0
  285. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_constant_default.json +2105 -0
  286. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_default_optional_output.json +2152 -0
  287. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_default_value.json +2094 -0
  288. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_drop_down.json +2080 -0
  289. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_lifetime_method.json +1990 -0
  290. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_optional_outputs.json +2094 -0
  291. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_output_node_flows.json +2105 -0
  292. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_results_master.json +493 -0
  293. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_rolling_start_remove.json +2087 -0
  294. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_rolling_window.json +2059 -0
  295. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_storage_binding_defaults.json +46 -0
  296. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_v2.json +1990 -0
  297. flextool-4.0.0/flextool/schemas/pre_v26/flextool_template_v25.json +3864 -0
  298. flextool-4.0.0/flextool/schemas/spinedb_results_schema.json +581 -0
  299. flextool-4.0.0/flextool/schemas/spinedb_schema.json +4636 -0
  300. flextool-4.0.0/flextool/solver_config/copt.opt.template +18 -0
  301. flextool-4.0.0/flextool/solver_config/cplex.opt.template +25 -0
  302. flextool-4.0.0/flextool/solver_config/gurobi.opt.template +18 -0
  303. flextool-4.0.0/flextool/solver_config/highs.opt.template +18 -0
  304. flextool-4.0.0/flextool/solver_config/xpress.opt.template +26 -0
  305. flextool-4.0.0/flextool/spinedb_backend/__init__.py +26 -0
  306. flextool-4.0.0/flextool/spinedb_backend/_axis_enums.py +1119 -0
  307. flextool-4.0.0/flextool/spinedb_backend/_backend.py +1139 -0
  308. flextool-4.0.0/flextool/update_flextool/__init__.py +12 -0
  309. flextool-4.0.0/flextool/update_flextool/canonical_databases.py +251 -0
  310. flextool-4.0.0/flextool/update_flextool/db_migration.py +7108 -0
  311. flextool-4.0.0/flextool/update_flextool/ensure_settings_db.py +138 -0
  312. flextool-4.0.0/flextool/update_flextool/export_database.py +103 -0
  313. flextool-4.0.0/flextool/update_flextool/extend_tests_fixture.py +772 -0
  314. flextool-4.0.0/flextool/update_flextool/generate_canonical.py +274 -0
  315. flextool-4.0.0/flextool/update_flextool/initialize_database.py +42 -0
  316. flextool-4.0.0/flextool/update_flextool/install_info.py +225 -0
  317. flextool-4.0.0/flextool/update_flextool/self_update.py +464 -0
  318. flextool-4.0.0/flextool/update_flextool/sync_master_json_template.py +125 -0
  319. flextool-4.0.0/flextool/update_flextool/test_fixtures.py +187 -0
  320. flextool-4.0.0/flextool.egg-info/PKG-INFO +217 -0
  321. flextool-4.0.0/flextool.egg-info/SOURCES.txt +399 -0
  322. flextool-4.0.0/flextool.egg-info/dependency_links.txt +1 -0
  323. flextool-4.0.0/flextool.egg-info/entry_points.txt +17 -0
  324. flextool-4.0.0/flextool.egg-info/requires.txt +32 -0
  325. flextool-4.0.0/flextool.egg-info/top_level.txt +1 -0
  326. flextool-4.0.0/pyproject.toml +225 -0
  327. flextool-4.0.0/setup.cfg +4 -0
  328. flextool-4.0.0/tests/test_bar_layout.py +367 -0
  329. flextool-4.0.0/tests/test_bar_sub_pixel_labels.py +128 -0
  330. flextool-4.0.0/tests/test_blocks.py +619 -0
  331. flextool-4.0.0/tests/test_branch_weights.py +260 -0
  332. flextool-4.0.0/tests/test_color_template.py +1458 -0
  333. flextool-4.0.0/tests/test_commodity_ladder_rolling.py +814 -0
  334. flextool-4.0.0/tests/test_comparison_excel.py +139 -0
  335. flextool-4.0.0/tests/test_comparison_project_root.py +195 -0
  336. flextool-4.0.0/tests/test_complete_period_share_reduction.py +90 -0
  337. flextool-4.0.0/tests/test_connection_node_warnings.py +97 -0
  338. flextool-4.0.0/tests/test_console_run_tool.py +107 -0
  339. flextool-4.0.0/tests/test_cost_aggregation_semantics.py +1037 -0
  340. flextool-4.0.0/tests/test_cost_breakdown_by_entity.py +524 -0
  341. flextool-4.0.0/tests/test_csv_dump_roundtrip.py +131 -0
  342. flextool-4.0.0/tests/test_dc_power_flow.py +1145 -0
  343. flextool-4.0.0/tests/test_dispatch_consumer_and_connection_signs.py +290 -0
  344. flextool-4.0.0/tests/test_dispatch_node_debug_and_tree.py +184 -0
  345. flextool-4.0.0/tests/test_dispatch_order_specials.py +455 -0
  346. flextool-4.0.0/tests/test_dispatch_scenario_tag.py +154 -0
  347. flextool-4.0.0/tests/test_ensure_settings_db.py +118 -0
  348. flextool-4.0.0/tests/test_entity_unitsize_invariant.py +97 -0
  349. flextool-4.0.0/tests/test_entity_universe_carrier.py +75 -0
  350. flextool-4.0.0/tests/test_env_check.py +156 -0
  351. flextool-4.0.0/tests/test_export_to_tabular.py +1492 -0
  352. flextool-4.0.0/tests/test_extend_tests_fixture.py +232 -0
  353. flextool-4.0.0/tests/test_flowgroup_indicators.py +325 -0
  354. flextool-4.0.0/tests/test_force_include.py +583 -0
  355. flextool-4.0.0/tests/test_group_output_warnings.py +272 -0
  356. flextool-4.0.0/tests/test_handoff_writers.py +490 -0
  357. flextool-4.0.0/tests/test_input_entity_colors.py +389 -0
  358. flextool-4.0.0/tests/test_lh2_three_region.py +379 -0
  359. flextool-4.0.0/tests/test_min_load_rolling.py +131 -0
  360. flextool-4.0.0/tests/test_non_anticipativity.py +282 -0
  361. flextool-4.0.0/tests/test_old_flextool_importer_schema_drift.py +92 -0
  362. flextool-4.0.0/tests/test_open_results_db.py +278 -0
  363. flextool-4.0.0/tests/test_output_column_metadata.py +93 -0
  364. flextool-4.0.0/tests/test_output_metadata_renderers.py +252 -0
  365. flextool-4.0.0/tests/test_parameter_group_coverage.py +41 -0
  366. flextool-4.0.0/tests/test_plan_union.py +254 -0
  367. flextool-4.0.0/tests/test_plot_draw_order.py +154 -0
  368. flextool-4.0.0/tests/test_plot_pipeline.py +1326 -0
  369. flextool-4.0.0/tests/test_plot_plan_chunk_reconstruction.py +263 -0
  370. flextool-4.0.0/tests/test_plot_plan_time_labels_roundtrip.py +197 -0
  371. flextool-4.0.0/tests/test_plot_settings_seed.py +552 -0
  372. flextool-4.0.0/tests/test_precision.py +226 -0
  373. flextool-4.0.0/tests/test_read_highs_solution.py +485 -0
  374. flextool-4.0.0/tests/test_regional_filter.py +443 -0
  375. flextool-4.0.0/tests/test_representative_periods.py +435 -0
  376. flextool-4.0.0/tests/test_rp_alternative_name.py +211 -0
  377. flextool-4.0.0/tests/test_rp_force_include_wiring.py +296 -0
  378. flextool-4.0.0/tests/test_rp_solves_filter.py +164 -0
  379. flextool-4.0.0/tests/test_scenario_colors.py +126 -0
  380. flextool-4.0.0/tests/test_scenario_stack.py +172 -0
  381. flextool-4.0.0/tests/test_scenarios.py +371 -0
  382. flextool-4.0.0/tests/test_self_describing_reader.py +478 -0
  383. flextool-4.0.0/tests/test_shared_axis_manifest.py +1171 -0
  384. flextool-4.0.0/tests/test_simple_bar_entity_color.py +357 -0
  385. flextool-4.0.0/tests/test_solve_config.py +141 -0
  386. flextool-4.0.0/tests/test_solve_reader.py +79 -0
  387. flextool-4.0.0/tests/test_synthesize_invest_dual.py +333 -0
  388. flextool-4.0.0/tests/test_timings_csv.py +145 -0
  389. flextool-4.0.0/tests/test_unidirectional_connection.py +106 -0
  390. flextool-4.0.0/tests/test_unit_online_dt_columns.py +170 -0
  391. flextool-4.0.0/tests/test_v52_migration.py +40 -0
  392. flextool-4.0.0/tests/test_v63_migration.py +84 -0
  393. flextool-4.0.0/tests/test_v64_migration.py +102 -0
  394. flextool-4.0.0/tests/test_v65_migration.py +266 -0
  395. flextool-4.0.0/tests/test_v66_migration.py +204 -0
  396. flextool-4.0.0/tests/test_v67_migration.py +76 -0
  397. flextool-4.0.0/tests/test_v68_migration.py +91 -0
  398. flextool-4.0.0/tests/test_v69_migration.py +88 -0
  399. flextool-4.0.0/tests/test_workdir_empty_default.py +202 -0
  400. flextool-4.0.0/tests/test_xlsx_workflow.py +241 -0
  401. flextool-4.0.0/tests/test_years_represented.py +543 -0
@@ -0,0 +1,19 @@
1
+ © International Renewable Energy Agency 2018-2022
2
+
3
+ The FlexTool is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
4
+ as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
5
+
6
+ The FlexTool is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
7
+ without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
8
+
9
+ You should have received a copy of the GNU Lesser General Public License along with the FlexTool.
10
+ If not, see <https://www.gnu.org/licenses/>.
11
+
12
+ Author: Juha Kiviluoma (2017-2022), VTT Technical Research Centre of Finland
13
+
14
+
15
+
16
+
17
+ -----------------------------------------------
18
+ IRENA FlexTool uses the HiGHS solver, which has its own license.
19
+ HiGHS is licensed with MIT license. Please see https://github.com/ERGO-Code/HiGHS for more information.
@@ -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/.
@@ -0,0 +1,149 @@
1
+ [![PyPI version](https://img.shields.io/pypi/v/flextool.svg)](https://pypi.org/project/flextool/)
2
+ [![Documentation Status](https://img.shields.io/badge/Documentation-passing-brightgreen)](https://irena-flextool.github.io/flextool/)
3
+ [![Python](https://img.shields.io/badge/python-3.11%20|%203.12-blue.svg)](https://www.python.org/downloads/release/python-3120/)
4
+
5
+ ![IRENA FlexTool logo](./docs/img/flextool_logo.png)
6
+
7
+ 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.
8
+
9
+ > [!NOTE]
10
+ > The previous `master` branch is deprecated and no longer updated — `main` is
11
+ > now FlexTool. If you still run FlexTool from `master`, migrate with a fresh
12
+ > parallel install (separate directory and venv); see
13
+ > [Installation](#installation) so your existing setup is left untouched.
14
+
15
+ ## What's new
16
+
17
+ - **A lot faster.** The model build and pre-processing now run as pure Python
18
+ instead of the previous GLPSOL-based text pre-processing and AMPL-style model
19
+ translation. There is no LP file written to disk and re-read between build and
20
+ solve.
21
+ - **Automatic problem scaling.** The `polar-high` matrix layer automatically
22
+ scales the optimisation problem before it reaches the solver. Better numerical
23
+ conditioning often speeds up the HiGHS solve considerably — sometimes
24
+ dramatically — so total run time usually improves well beyond the faster build
25
+ alone. The solver is still HiGHS; the gain comes from handing it a
26
+ better-conditioned problem.
27
+ - **A new, easy-to-use interface.** The **FlexTool GUI** is a standalone
28
+ application (`python -m flextool.gui`) that manages projects and input
29
+ sources, runs scenarios, and lets you **browse the results** — a result viewer
30
+ reads outputs directly and supports single-scenario, scenario-comparison, and
31
+ network-graph views with keyboard navigation. See the
32
+ [FlexTool GUI guide](https://irena-flextool.github.io/flextool/flextool_gui_interface/).
33
+ - **Spine Toolbox is still a parallel interface.** Advanced users who build
34
+ multi-tool workflows or integrate FlexTool with other models can keep using
35
+ the [Spine Toolbox workflow](https://irena-flextool.github.io/flextool/spine_toolbox/).
36
+ - **The same model and data.** Existing input databases continue to work; the
37
+ change is in how the matrix is generated and solved, not in the model
38
+ formulation.
39
+
40
+ 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).
41
+ The previous version of IRENA FlexTool can be found in https://www.irena.org/energytransition/Energy-System-Models-and-Data/IRENA-FlexTool.
42
+
43
+ ## Under the hood: Rust-based matrix generation
44
+
45
+ FlexTool is a thin Python layer over a Rust engine. It reads its input data and
46
+ builds the optimisation matrix as [polars](https://pola.rs/) DataFrames via
47
+ [`polar-high`](https://github.com/nodal-tools/polar-high), then solves with
48
+ [HiGHS](https://highs.dev/). Variables and parameters are polars frames, so
49
+ multiplications become joins and aggregations become group-bys — the heavy
50
+ coefficient work runs inside polars' Rust core rather than as per-coefficient
51
+ Python objects, and the matrix goes straight to HiGHS through `highspy` with no
52
+ intermediate LP/MPS file. `polar-high` is a general-purpose, domain-free
53
+ modelling layer published separately (Apache-2.0, `pip install polar-high`); it
54
+ also applies the automatic problem scaling noted above, and its
55
+ [benchmark](https://nodal-tools.fi/polar-high/compare/benchmark/) compares it
56
+ against linopy and Pyomo on the same HiGHS solver.
57
+
58
+ ## Installation
59
+
60
+ ### Check your Python (and possibly install it)
61
+
62
+ FlexTool requires **Python 3.11+**.
63
+
64
+ Open a terminal (Windows: Command Prompt or PowerShell; macOS: Terminal; Linux: your shell)
65
+ and check which Python you have:
66
+
67
+ | Platform | Command |
68
+ |---|---|
69
+ | Windows | `py --version` (falls back to `python --version`) |
70
+ | macOS / Linux | `python3 --version` |
71
+
72
+ The output looks like `Python 3.12.4`. If the command is not found, prints
73
+ something below 3.11, or (on Windows) opens the Microsoft Store, install Python
74
+ from https://www.python.org/downloads/ — on Windows tick
75
+ **"Add python.exe to PATH"** in the installer. Then close and reopen the
76
+ terminal so the new PATH takes effect.
77
+
78
+ > On Windows, use `py` in place of `python` in the commands below if `python`
79
+ > is not recognised. If several versions are installed, `py -3.11 --version`
80
+ > (or `-3.12`, …) picks a specific one, and `py -0` lists them all.
81
+ > On macOS/Linux, `python` often means Python 2 or does not exist at all, so
82
+ > use `python3`. Inside an activated virtual environment, plain `python` always
83
+ > means that environment's Python on every platform.
84
+
85
+
86
+ ### Install from PyPI (recommended)
87
+
88
+ Install FlexTool into a virtual environment to keep its packages separate from
89
+ other Python applications. If you want to install into the base Python (perhaps
90
+ because you do not otherwise use Python), just run the last command from below,
91
+ `pip install flextool`.
92
+
93
+ ```
94
+ python -m venv .venv # Windows: py -m venv .venv
95
+ # macOS/Linux: python3 -m venv .venv
96
+ # activate — Windows PowerShell: .\.venv\Scripts\Activate.ps1
97
+ # Windows CMD: .\.venv\Scripts\activate.bat
98
+ # Linux / macOS: . .venv/bin/activate
99
+ python -m pip install --upgrade pip
100
+ pip install flextool
101
+ ```
102
+
103
+ Your prompt should now show `(.venv)`. Verify with `python --version` — it
104
+ should report 3.11 or newer.
105
+
106
+ (If PowerShell blocks the activate script, run once per user:
107
+ `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`.)
108
+
109
+ Launch the FlexTool GUI:
110
+
111
+ ```
112
+ flextool
113
+ ```
114
+
115
+ — equivalently `python -m flextool.gui`. The GUI creates and manages your
116
+ projects and input databases; to seed the bundled template and example
117
+ databases (handy for the CLI or Spine Toolbox workflows) run `flextool-update`.
118
+
119
+ ### Install from source (developers / latest `main`)
120
+
121
+ To participate in FlexTool development — or to run the not-yet-released `main`
122
+ — clone the repo and install it editable:
123
+
124
+ ```powershell
125
+ git clone https://github.com/irena-flextool/flextool.git
126
+ cd flextool
127
+ python -m venv .venv
128
+ .\.venv\Scripts\Activate.ps1 # Linux / macOS: . .venv/bin/activate
129
+ python -m pip install --upgrade pip
130
+ pip install -e .
131
+ ```
132
+
133
+ The editable install picks up source changes on `git pull`; re-run
134
+ `pip install -e .` only when a dependency pin in `pyproject.toml` changes.
135
+
136
+ ### Update FlexTool:
137
+
138
+ ```
139
+ # If using virtual environment (venv), then activate venv first:
140
+ # Windows PowerShell: .\.venv\Scripts\Activate.ps1
141
+ # Windows CMD: .\.venv\Scripts\activate.bat
142
+ # Linux / macOS: . .venv/bin/activate
143
+ pip install --upgrade flextool
144
+ ```
145
+
146
+ ## [Documentation](https://irena-flextool.github.io/flextool/) and [installation](https://irena-flextool.github.io/flextool/install_toolbox/)
147
+
148
+ > [!IMPORTANT]
149
+ > Installation, user guide and ***documentation*** can be found at: https://irena-flextool.github.io/flextool/.
@@ -0,0 +1,41 @@
1
+ """FlexTool energy system optimization model package."""
2
+ import os as _os
3
+ # jemalloc decay config — set BEFORE the first ``import polars`` (which
4
+ # happens transitively in the imports just below). polars statically
5
+ # links jemalloc; jemalloc reads ``_RJEM_MALLOC_CONF`` exactly once at
6
+ # .so load. Its default ``dirty_decay_ms`` (~10 s) is longer than the
7
+ # gap between rolling-solve rolls, so each roll's freed GB-scale
8
+ # coefficient frames stay resident as dirty pages when the next roll
9
+ # starts allocating — the per-roll ``priv_dirty`` floor ratchets up
10
+ # (~+2 GB/roll on the 9-roll DES run). ``dirty_decay_ms:1000`` purges a
11
+ # roll's freed pages ~1 s after they go idle (well before the next roll,
12
+ # which takes minutes) without eagerly madvise-ing on every transient
13
+ # free inside a hot operation. ``muzzy_decay_ms:0`` forces MADV_DONTNEED
14
+ # (so Private_Dirty actually drops) rather than leaving pages as
15
+ # reclaimable-but-resident MADV_FREE. glibc knobs (MALLOC_ARENA_MAX,
16
+ # malloc_trim) do NOT touch polars memory — this is the lever that does.
17
+ # ``setdefault`` so a shell-provided value still wins (A/B profiling).
18
+ _os.environ.setdefault("_RJEM_MALLOC_CONF", "dirty_decay_ms:1000,muzzy_decay_ms:0")
19
+ import warnings as _warnings
20
+ # Silence requests' RequestsDependencyWarning. The FlexTool venv ships a
21
+ # ``requests`` whose pinned urllib3/charset_normalizer version ranges lag the
22
+ # (working) installed versions, so ``import requests`` warns once at import
23
+ # time. It is harmless but leaks into Spine Toolbox's Tool console for any
24
+ # path that pulls in requests (e.g. an ``http://`` Spine DB-server URL).
25
+ # Match by message — set BEFORE requests is imported transitively below — so
26
+ # we needn't import requests here just to reference its warning class, and the
27
+ # filter stays robust across the venv's exact version numbers.
28
+ _warnings.filterwarnings(
29
+ "ignore",
30
+ message=r"urllib3 .*doesn't match a supported version",
31
+ )
32
+ __all__ = [
33
+ 'write_outputs',
34
+ 'migrate_database',
35
+ 'initialize_database',
36
+ 'update_flextool',
37
+ ]
38
+ from flextool.process_outputs import write_outputs # noqa: E402 (after env/warning config above)
39
+ from flextool.update_flextool import migrate_database, initialize_database, update_flextool # noqa: E402
40
+
41
+ name = "flextool"
@@ -0,0 +1,193 @@
1
+ """In-process memory sampler — timestamped, allocator-aware memory trace.
2
+
3
+ RSS is a poor signal for this workload: it conflates shared, clean, and
4
+ allocator-reserved-but-free pages with genuinely-live private data. This
5
+ sampler additionally records, per sample:
6
+
7
+ * ``pss_gb`` / ``priv_dirty_gb`` / ``proc_swap_gb`` — from
8
+ ``/proc/self/smaps_rollup``. ``Private_Dirty + Swap`` is the
9
+ allocator-agnostic "what this process truly owns" number (counts dirty
10
+ anon pages whether they live in glibc, mimalloc, or the C++ heap, and
11
+ whether resident or swapped).
12
+ * ``glibc_inuse_gb`` / ``glibc_free_gb`` / ``glibc_mmap_gb`` — from
13
+ glibc ``mallinfo2()`` (``uordblks`` / ``fordblks`` / ``hblkhd``). The
14
+ *live-vs-reserved* discriminator: ``fordblks`` is memory glibc has
15
+ freed but is holding on its arena free-list rather than returning to
16
+ the OS (what ``malloc_trim`` would release). NOTE: only sees the glibc
17
+ allocator — HiGHS/C++ go through glibc, but polars (Rust) may use its
18
+ own allocator, so a large ``priv_dirty`` with small ``glibc_inuse``
19
+ points at the non-glibc (polars) side.
20
+ * ``mem_used_gb`` — system used (``MemTotal - MemAvailable``), the number
21
+ most desktop system monitors plot. ``available_gb`` / ``swap_used_gb``
22
+ kept for back-compat.
23
+
24
+ Gated by ``FLEXTOOL_MEM_SAMPLER``; interval via
25
+ ``FLEXTOOL_MEM_SAMPLER_INTERVAL_MS`` (default 100 ms).
26
+ """
27
+ from __future__ import annotations
28
+
29
+ import ctypes
30
+ import datetime
31
+ import os
32
+ import sys
33
+ import threading
34
+ import time
35
+
36
+
37
+ class _Mallinfo2(ctypes.Structure):
38
+ # glibc >= 2.33. All fields size_t (the legacy ``mallinfo`` used int
39
+ # and overflows past 2 GB — useless here).
40
+ _fields_ = [
41
+ (n, ctypes.c_size_t)
42
+ for n in (
43
+ "arena", "ordblks", "smblks", "hblks", "hblkhd", "usmblks",
44
+ "fsmblks", "uordblks", "fordblks", "keepcost",
45
+ )
46
+ ]
47
+
48
+
49
+ def _make_mallinfo2():
50
+ """Return a callable giving (inuse, free, mmap) bytes, or None."""
51
+ try:
52
+ libc = ctypes.CDLL("libc.so.6")
53
+ fn = libc.mallinfo2 # AttributeError on glibc < 2.33
54
+ fn.restype = _Mallinfo2
55
+ fn.argtypes = []
56
+ except (OSError, AttributeError):
57
+ return None
58
+
59
+ def _read():
60
+ mi = fn()
61
+ return mi.uordblks, mi.fordblks, mi.hblkhd
62
+
63
+ return _read
64
+
65
+
66
+ def start_mem_sampler() -> None:
67
+ """Start a daemon thread that samples memory at a fixed interval if FLEXTOOL_MEM_SAMPLER is set."""
68
+ if os.environ.get("FLEXTOOL_MEM_SAMPLER", "").strip().lower() in ("", "0", "false"):
69
+ return
70
+
71
+ pid = os.getpid()
72
+ log_path = os.environ.get("FLEXTOOL_MEM_SAMPLER_LOG", f"/tmp/flextool_mem_sampler_{pid}.log")
73
+ try:
74
+ interval_ms = max(20, min(10000, int(os.environ.get("FLEXTOOL_MEM_SAMPLER_INTERVAL_MS", "100"))))
75
+ except ValueError:
76
+ interval_ms = 100
77
+ interval_s = interval_ms / 1000.0
78
+
79
+ if not sys.platform.startswith("linux"):
80
+ with open(log_path, "a", buffering=1) as _f:
81
+ _f.write(
82
+ f"# flextool memory sampler pid={pid} WARNING: platform is not "
83
+ f"linux — sampler disabled\n"
84
+ )
85
+ return
86
+
87
+ _GB = float(2 ** 30)
88
+ _mallinfo2 = _make_mallinfo2()
89
+
90
+ def _read_kv_bytes(path: str, keys: tuple[str, ...]) -> dict[str, int]:
91
+ """Parse ``Key: N kB`` lines from a /proc file → bytes."""
92
+ out: dict[str, int] = {}
93
+ want = set(keys)
94
+ with open(path) as fh:
95
+ for line in fh:
96
+ k = line.split(":", 1)[0]
97
+ if k in want:
98
+ out[k] = int(line.split()[1]) * 1024
99
+ if len(out) == len(want):
100
+ break
101
+ return out
102
+
103
+ def _sample() -> dict[str, float]:
104
+ # Per-process private cost — allocator-agnostic.
105
+ rss = pss = priv_dirty = proc_swap = 0
106
+ try:
107
+ r = _read_kv_bytes(
108
+ "/proc/self/smaps_rollup",
109
+ ("Rss", "Pss", "Private_Dirty", "Swap"),
110
+ )
111
+ rss = r.get("Rss", 0)
112
+ pss = r.get("Pss", 0)
113
+ priv_dirty = r.get("Private_Dirty", 0)
114
+ proc_swap = r.get("Swap", 0)
115
+ except Exception:
116
+ pass
117
+ if rss == 0:
118
+ # smaps_rollup unavailable — fall back to VmRSS.
119
+ try:
120
+ rss = _read_kv_bytes("/proc/self/status", ("VmRSS",)).get("VmRSS", 0)
121
+ except Exception:
122
+ pass
123
+
124
+ # System-wide.
125
+ mem_total = mem_avail = swap_total = swap_free = 0
126
+ try:
127
+ m = _read_kv_bytes(
128
+ "/proc/meminfo",
129
+ ("MemTotal", "MemAvailable", "SwapTotal", "SwapFree"),
130
+ )
131
+ mem_total = m.get("MemTotal", 0)
132
+ mem_avail = m.get("MemAvailable", 0)
133
+ swap_total = m.get("SwapTotal", 0)
134
+ swap_free = m.get("SwapFree", 0)
135
+ except Exception:
136
+ pass
137
+
138
+ # glibc allocator breakdown — live vs reserved-free.
139
+ g_inuse = g_free = g_mmap = -1
140
+ if _mallinfo2 is not None:
141
+ try:
142
+ g_inuse, g_free, g_mmap = _mallinfo2()
143
+ except Exception:
144
+ pass
145
+
146
+ return {
147
+ "rss_gb": rss / _GB,
148
+ "pss_gb": pss / _GB,
149
+ "priv_dirty_gb": priv_dirty / _GB,
150
+ "proc_swap_gb": proc_swap / _GB,
151
+ "available_gb": mem_avail / _GB,
152
+ "mem_used_gb": (mem_total - mem_avail) / _GB,
153
+ "swap_used_gb": (swap_total - swap_free) / _GB,
154
+ "glibc_inuse_gb": g_inuse / _GB if g_inuse >= 0 else -1.0,
155
+ "glibc_free_gb": g_free / _GB if g_free >= 0 else -1.0,
156
+ "glibc_mmap_gb": g_mmap / _GB if g_mmap >= 0 else -1.0,
157
+ }
158
+
159
+ started_at = datetime.datetime.now(datetime.UTC).isoformat()
160
+ # Stable column order so post-hoc parsing is trivial.
161
+ _cols = (
162
+ "rss_gb", "pss_gb", "priv_dirty_gb", "proc_swap_gb",
163
+ "available_gb", "mem_used_gb", "swap_used_gb",
164
+ "glibc_inuse_gb", "glibc_free_gb", "glibc_mmap_gb",
165
+ )
166
+
167
+ def _loop(log_path: str, interval_s: float) -> None:
168
+ mono_start = time.monotonic()
169
+ with open(log_path, "a", buffering=1) as f:
170
+ f.write(
171
+ f"# flextool memory sampler pid={pid} started_at={started_at} "
172
+ f"interval_ms={interval_ms} mallinfo2={'yes' if _mallinfo2 else 'no'}\n"
173
+ )
174
+ f.write("# priv_dirty_gb+proc_swap_gb = true private cost; "
175
+ "glibc_free_gb = held-free (malloc_trim-able); "
176
+ "mem_used_gb = system used (monitor metric)\n")
177
+ while True:
178
+ ts = datetime.datetime.now(datetime.UTC).isoformat()
179
+ mono_s = time.monotonic() - mono_start
180
+ try:
181
+ s = _sample()
182
+ parts = "\t".join(f"{c}={s[c]:.4f}" for c in _cols)
183
+ line = f"ts={ts}\tmono_s={mono_s:.3f}\tepoch={time.time():.3f}\t{parts}\n"
184
+ except Exception as exc:
185
+ line = f"ts={ts}\tmono_s={mono_s:.3f}\tepoch={time.time():.3f}\terror={repr(exc)}\n"
186
+ f.write(line)
187
+ # Explicit flush so the last samples before SIGKILL reach disk;
188
+ # line-buffered mode alone won't flush until the process exits cleanly.
189
+ f.flush()
190
+ time.sleep(interval_s)
191
+
192
+ t = threading.Thread(target=_loop, args=(log_path, interval_s), name="flextool-mem-sampler", daemon=True)
193
+ t.start()
@@ -0,0 +1,43 @@
1
+ """Locate bundled FlexTool package data files.
2
+
3
+ Static FlexTool resources — schema JSONs, YAML/text templates, and
4
+ canonical-database sources (all under ``flextool/schemas/``), plus the HiGHS
5
+ options template (``flextool/bin/``) — must be reached through
6
+ :mod:`importlib.resources` rather than ``Path(__file__).resolve()``
7
+ walks. After ``pip install flextool`` the package is the only thing
8
+ on ``sys.path``; the historical layout where these dirs sat at the
9
+ *repo root* alongside the package no longer exists.
10
+
11
+ Two helpers are exposed:
12
+
13
+ ``package_data_path(relative)``
14
+ Returns a :class:`pathlib.Path` to a resource inside the
15
+ ``flextool`` package, e.g.
16
+ ``package_data_path("schemas/default_plots.yaml")``.
17
+ Editable installs return the real on-disk path; wheel installs
18
+ return the unpacked site-packages path (importlib.resources
19
+ materialises zipped data on demand, but we never ship zipped so
20
+ in practice this is always a stable filesystem path).
21
+
22
+ ``package_data_text(relative)``
23
+ Convenience wrapper that returns the file contents as a string —
24
+ handy when the caller doesn't actually need a path (YAML/JSON
25
+ parsing).
26
+ """
27
+
28
+ from __future__ import annotations
29
+
30
+ from importlib import resources
31
+ from pathlib import Path
32
+
33
+ import flextool
34
+
35
+
36
+ def package_data_path(relative: str) -> Path:
37
+ """Path to ``flextool/<relative>`` inside the installed package."""
38
+ return Path(resources.files(flextool).joinpath(relative))
39
+
40
+
41
+ def package_data_text(relative: str) -> str:
42
+ """Text contents of ``flextool/<relative>``."""
43
+ return resources.files(flextool).joinpath(relative).read_text(encoding="utf-8")