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,621 @@
1
+ """Quantity-type lookup table for FlexTool parameters.
2
+
3
+ Derived from parameter_definition.description in
4
+ templates/input_data_template.sqlite (see specs/h2_trade_scaling_regression_handoff.md
5
+ and the autoscaler design). This module maps every (parameter_name, entity_class)
6
+ pair declared in the template to a QuantityType so the autoscaler can apply a
7
+ semantically appropriate scaling factor per quantity.
8
+
9
+ UNKNOWN entries are intentional: the source description either gives no unit or
10
+ gives a context-dependent unit (e.g. [MW or MWh] on a group whose composition
11
+ decides the unit). The autoscaler MUST refuse to scale UNKNOWN parameters and
12
+ instead surface them to the operator.
13
+ """
14
+ from __future__ import annotations
15
+
16
+ from enum import Enum
17
+
18
+
19
+ class QuantityType(Enum):
20
+ """Physical quantity carried by a FlexTool parameter value.
21
+
22
+ Values are stable strings so they can appear in scaling-report CSVs without
23
+ leaking Python identifiers.
24
+ """
25
+
26
+ ENERGY = "energy" # [MWh]
27
+ POWER = "power" # [MW]
28
+ APPARENT_POWER = "apparent_power" # [MVA]
29
+ INERTIA = "inertia" # [MWs] synchronous inertia stock
30
+ INERTIA_PER_CAPACITY = "inertia_per_capacity" # [MWs/MW] (= seconds)
31
+ CURRENCY = "currency" # [CUR] reserved; no template parameter uses it directly
32
+ PRICE_PER_ENERGY = "price_per_energy" # [CUR/MWh]
33
+ PRICE_PER_CAPACITY = "price_per_capacity" # [CUR/kW], [CUR/MW]
34
+ PRICE_PER_STORAGE = "price_per_storage" # [CUR/kWh]
35
+ PRICE_PER_MASS = "price_per_mass" # [CUR/ton]
36
+ PRICE_PER_INERTIA = "price_per_inertia" # [CUR/MWs]
37
+ EMISSION_MASS = "emission_mass" # [tCO2]
38
+ EMISSION_INTENSITY = "emission_intensity" # [CO2 ton per MWh]
39
+ FRACTION = "fraction" # [factor], [0-1], [%], availability/efficiency-style
40
+ DURATION = "duration" # [hours], [years], "Hours."
41
+ RAMP_RATE = "ramp_rate" # [per unit / minute]
42
+ DIMENSIONLESS = "dimensionless" # method choices, flags, name arrays, user-supplied coefficients
43
+ UNKNOWN = "unknown" # unit cannot be determined from the description; requires operator action
44
+
45
+
46
+ PARAMETER_TYPES: dict[tuple[str, str], QuantityType] = {
47
+ ('annual_flow', 'node'): QuantityType.ENERGY,
48
+ # [MWh] Annual flow in energy units (always positive, the sign of inflow defines in/out). Inflow time series can be scaled to match annual flo...
49
+ ('availability', 'connection'): QuantityType.FRACTION,
50
+ # [e.g. 0.9 means 90%] Fraction of capacity available for connection flows. Constant or time.
51
+ ('availability', 'node'): QuantityType.FRACTION,
52
+ # [e.g. 0.9 means 90%] Fraction of capacity available for storage. Constant or time.
53
+ ('availability', 'unit'): QuantityType.FRACTION,
54
+ # [e.g. 0.9 means 90%] Fraction of capacity available for flows from/to the unit. For online units, the online variable is multiplied by the a...
55
+ ('base_MVA', 'group'): QuantityType.APPARENT_POWER,
56
+ # [MVA] Base power for the per-unit system used in DC power flow. Default 100. susceptance = base_MVA / reactance_pu.
57
+ ('benders_max_iter', 'solve'): QuantityType.DIMENSIONLESS,
58
+ # Benders max outer (master/subproblem) iterations. Solver-algorithm knob, never an LP coefficient.
59
+ ('benders_tolerance', 'solve'): QuantityType.DIMENSIONLESS,
60
+ # Benders relative optimality-gap convergence threshold. Solver-algorithm knob, never an LP coefficient.
61
+ ('benders_in_out_weight', 'solve'): QuantityType.DIMENSIONLESS,
62
+ # Benders in-out separation stabilization weight (lambda). Solver-algorithm knob, never an LP coefficient.
63
+ ('candidate_precapacity_to_avoid_big_m', 'group'): QuantityType.POWER,
64
+ # [MW] Small pre-existing capacity assigned to investment candidate connections in DC power flow groups that have zero existing capacity. This...
65
+ ('capacity_margin', 'group'): QuantityType.POWER,
66
+ # [MW] How much capacity a node group is required to have in addition to the peak net load in the investment time series. Used only by the inv...
67
+ ('capacity_max_coeff', 'unit__inputNode'): QuantityType.FRACTION,
68
+ # [factor, default 1.0] Fraction of the unit's capacity available to this edge's upper cap (maxToSink / maxFromSource / ramp). For extraction ...
69
+ ('capacity_max_coeff', 'unit__outputNode'): QuantityType.FRACTION,
70
+ # [factor, default 1.0] Fraction of the unit's capacity available to this edge's upper cap (maxToSink / maxFromSource / ramp). For extraction ...
71
+ ('capacity_min_coeff', 'unit__inputNode'): QuantityType.FRACTION,
72
+ # [factor, default 1.0] Fraction of the unit's capacity imposed as a lower cap on this edge when online (combined multiplicatively with the un...
73
+ ('capacity_min_coeff', 'unit__outputNode'): QuantityType.FRACTION,
74
+ # [factor, default 1.0] Fraction of the unit's capacity imposed as a lower cap on this edge when online (combined multiplicatively with the un...
75
+ # Legacy long-form aliases still present in the template DB until db_migration.py renames
76
+ # max_capacity_coefficient → capacity_max_coeff and min_capacity_coefficient → capacity_min_coeff.
77
+ ('max_capacity_coefficient', 'unit__inputNode'): QuantityType.FRACTION,
78
+ ('max_capacity_coefficient', 'unit__outputNode'): QuantityType.FRACTION,
79
+ ('min_capacity_coefficient', 'unit__inputNode'): QuantityType.FRACTION,
80
+ ('min_capacity_coefficient', 'unit__outputNode'): QuantityType.FRACTION,
81
+ ('co2_content', 'commodity'): QuantityType.EMISSION_INTENSITY,
82
+ # [CO2 ton per MWh] Constant.
83
+ ('co2_max_period', 'group'): QuantityType.EMISSION_MASS,
84
+ # [tCO2] Annualized maximum limit for emitted CO2 in each period.
85
+ ('co2_max_total', 'group'): QuantityType.EMISSION_MASS,
86
+ # [tCO2] Maximum limit for emitted CO2 in the whole solve.
87
+ ('co2_method', 'group'): QuantityType.DIMENSIONLESS,
88
+ # Choice of the CO2 method: none, price, period, total, price_period, price_total, period_total, price_period_total
89
+ ('co2_price', 'group'): QuantityType.PRICE_PER_MASS,
90
+ # [CUR/ton] CO2 price for a group of nodes. Constant, period or time.
91
+ ('debug', 'model'): QuantityType.DIMENSIONLESS,
92
+ # Instruction set for performing model debugging and testing — a string toggle, no physical unit.
93
+ ('constant', 'constraint'): QuantityType.DIMENSIONLESS,
94
+ # A constant offset for a user constraint (typically zero). The constant will be on the right side of the equation.
95
+ # User constraints aggregate LHS terms whose units depend on the user's coefficient choices; the RHS constant lives
96
+ # in that same composite coordinate. Layer-2 does not apply a per-row factor — column scalers propagate via the LHS.
97
+ ('constraint_cumulative_pre_built_capacity_coeff', 'connection'): QuantityType.DIMENSIONLESS,
98
+ # A map of coefficients (index: constraint name, value: coefficient) that places the cumulative pre-built capacity at period d — data baseline...
99
+ ('constraint_cumulative_pre_built_capacity_coeff', 'node'): QuantityType.DIMENSIONLESS,
100
+ # A map of coefficients (index: constraint name, value: coefficient) that places the cumulative pre-built capacity at period d — data baseline...
101
+ ('constraint_cumulative_pre_built_capacity_coeff', 'unit'): QuantityType.DIMENSIONLESS,
102
+ # A map of coefficients (index: constraint name, value: coefficient) that places the cumulative pre-built capacity at period d — data baseline...
103
+ # Legacy long-form names still present in the template DB until migration runs (db_migration.py renames *_coefficient → *_coeff).
104
+ ('constraint_cumulative_pre_built_capacity_coefficient', 'connection'): QuantityType.DIMENSIONLESS,
105
+ ('constraint_cumulative_pre_built_capacity_coefficient', 'node'): QuantityType.DIMENSIONLESS,
106
+ ('constraint_cumulative_pre_built_capacity_coefficient', 'unit'): QuantityType.DIMENSIONLESS,
107
+ ('constraint_flow_coeff', 'connection__node'): QuantityType.DIMENSIONLESS,
108
+ # A map of coefficients (Index: constraint name, value: coefficient) to represent the participation of the flow from the connection to a node ...
109
+ ('constraint_flow_coeff', 'unit__inputNode'): QuantityType.DIMENSIONLESS,
110
+ # A map of coefficients (Index: constraint name, value: coefficient) to represent the participation of the flow between unit and node in user-...
111
+ ('constraint_flow_coeff', 'unit__outputNode'): QuantityType.DIMENSIONLESS,
112
+ # A map of coefficients (Index: constraint name, value: coefficient) to represent the participation of the flow between unit and node in user-...
113
+ # Legacy long-form names still present in the template DB until migration runs.
114
+ ('constraint_flow_coefficient', 'connection__node'): QuantityType.DIMENSIONLESS,
115
+ ('constraint_flow_coefficient', 'unit__inputNode'): QuantityType.DIMENSIONLESS,
116
+ ('constraint_flow_coefficient', 'unit__outputNode'): QuantityType.DIMENSIONLESS,
117
+ ('constraint_invested_capacity_coeff', 'connection'): QuantityType.DIMENSIONLESS,
118
+ # A map of coefficients (index: constraint name, value: coefficient) that places v_invest[e, d] — new-build capacity decided in the current pe...
119
+ ('constraint_invested_capacity_coeff', 'node'): QuantityType.DIMENSIONLESS,
120
+ # A map of coefficients (index: constraint name, value: coefficient) that places v_invest[e, d] — new-build capacity decided in the current pe...
121
+ ('constraint_invested_capacity_coeff', 'unit'): QuantityType.DIMENSIONLESS,
122
+ # A map of coefficients (index: constraint name, value: coefficient) that places v_invest[e, d] — new-build capacity decided in the current pe...
123
+ # Legacy long-form names still present in the template DB until migration runs.
124
+ ('constraint_invested_capacity_coefficient', 'connection'): QuantityType.DIMENSIONLESS,
125
+ ('constraint_invested_capacity_coefficient', 'node'): QuantityType.DIMENSIONLESS,
126
+ ('constraint_invested_capacity_coefficient', 'unit'): QuantityType.DIMENSIONLESS,
127
+ ('constraint_state_coeff', 'node'): QuantityType.DIMENSIONLESS,
128
+ # A map of coefficients (Index: constraint name, value: coefficient) to represent the participation of the storage state in user-defined const...
129
+ ('constraint_state_coefficient', 'node'): QuantityType.DIMENSIONLESS,
130
+ # Legacy long-form name still present in the template DB until migration runs (renamed to constraint_state_coeff).
131
+ ('contains_solves', 'solve'): QuantityType.DIMENSIONLESS,
132
+ # Array of solves - used for nested solve sequencesArray of solves - used for nested solve sequences
133
+ ('conversion_flow_coeff', 'unit__inputNode'): QuantityType.FRACTION,
134
+ # [factor] Energy-unit conversion factor for this flow in the node balance and conversion_indirect equations. Value of 0 removes the edge from...
135
+ ('conversion_flow_coeff', 'unit__outputNode'): QuantityType.FRACTION,
136
+ # [factor] Energy-unit conversion factor for this flow in the node balance and conversion_indirect equations. Value of 0 removes the edge from...
137
+ # Legacy long-form alias still present in the template DB until db_migration.py renames flow_coefficient → conversion_flow_coeff.
138
+ ('flow_coefficient', 'unit__inputNode'): QuantityType.FRACTION,
139
+ ('flow_coefficient', 'unit__outputNode'): QuantityType.FRACTION,
140
+ ('conversion_method', 'unit'): QuantityType.DIMENSIONLESS,
141
+ # Choice of conversion method.
142
+ ('cumulative_max_capacity', 'connection'): QuantityType.POWER,
143
+ # [MW] Maximum cumulative capacity (considers existing, invested and retired capacity). Constant or period.
144
+ ('cumulative_max_capacity', 'group'): QuantityType.UNKNOWN,
145
+ # [MW or MWh] Maximum cumulative capacity for a group of entities (considers existing, invested and retired capacity). Constant or period.
146
+ ('cumulative_max_capacity', 'node'): QuantityType.ENERGY,
147
+ # [MWh] Maximum cumulative capacity (considers existing, invested and retired capacity). Constant or period.
148
+ ('cumulative_max_capacity', 'unit'): QuantityType.POWER,
149
+ # [MW] Maximum cumulative capacity (considers existing, invested and retired capacity). Constant or period.
150
+ ('cumulative_min_capacity', 'connection'): QuantityType.POWER,
151
+ # [MW] Minimum cumulative capacity (considers existing, invested and retired capacity). Constant or period.
152
+ ('cumulative_min_capacity', 'group'): QuantityType.UNKNOWN,
153
+ # [MW or MWh] Minimum cumulative capacity for a group of entities (considers existing, invested and retired capacity). Constant or period.
154
+ ('cumulative_min_capacity', 'node'): QuantityType.ENERGY,
155
+ # [MWh] Minimum cumulative capacity (considers existing, invested and retired capacity). Constant or period.
156
+ ('cumulative_min_capacity', 'unit'): QuantityType.POWER,
157
+ # [MW] Minimum cumulative capacity (considers existing, invested and retired capacity). Constant or period.
158
+ ('decomposition', 'solve'): QuantityType.DIMENSIONLESS,
159
+ # Decomposition scheme for this solve: 'none' (monolithic, default) or 'benders' (region decomposition). Solver-config choice, never an LP coefficient.
160
+ ('decomposition_method', 'group'): QuantityType.DIMENSIONLESS,
161
+ # Decomposition strategy to apply to this group. Currently supported: 'none' (no decomposition — default), 'benders_regional' (group is solve...
162
+ ('delay', 'connection'): QuantityType.DURATION,
163
+ # [hours] A time delay between the input node and the output node - works only with one-way connections (or units). Either a constant indicati...
164
+ ('delay', 'unit'): QuantityType.DURATION,
165
+ # [hours] A time delay between the input nodes and the output nodes. Either a constant indicating the time difference in hours or a map of tim...
166
+ ('discount_rate', 'connection'): QuantityType.FRACTION,
167
+ # [e.g. 0.05 equals 5%] Discount rate for investments (WACC). Reflects the financing cost and risk premium for this technology. When the model...
168
+ ('discount_rate', 'node'): QuantityType.FRACTION,
169
+ # [e.g. 0.05 equals 5%] Discount rate for investments (WACC). Reflects the financing cost and risk premium for this technology. When the model...
170
+ ('discount_rate', 'unit'): QuantityType.FRACTION,
171
+ # [e.g. 0.05 equals 5%] Discount rate for investments (WACC). Reflects the financing cost and risk premium for this technology. When the model...
172
+ ('efficiency', 'connection'): QuantityType.FRACTION,
173
+ # [factor, typically between 0-1] Efficiency of a connection. Constant or time.
174
+ ('efficiency', 'unit'): QuantityType.FRACTION,
175
+ # [factor] Efficiency of a unit. Constant or time.
176
+ ('efficiency_at_min_load', 'unit'): QuantityType.FRACTION,
177
+ # [e.g. 0.4 means 40%] Efficiency of the unit at minimum load. Applies only if the unit has an online variable. Constant.
178
+ ('energy_margin_multiplier', 'node'): QuantityType.DIMENSIONLESS,
179
+ # [factor] Multiplier applied to this node's inflow in the investment solve only (ratio, consumed at preprocessing; 1 = off).
180
+ ('energy_margin', 'node'): QuantityType.DIMENSIONLESS,
181
+ # Deprecated v65 name for energy_margin_multiplier; carried during the v65->v66 migration window so a pre-migration DB read is not silently un-scaled.
182
+ ('energy_margin_adder', 'node'): QuantityType.ENERGY,
183
+ # [MWh] Extra demand (negative inflow) added to this node in the investment solve only (real energy; consumed at preprocessing; 0 = off).
184
+ ('energy_margin_method', 'node'): QuantityType.DIMENSIONLESS,
185
+ # Method choice {none, inflow_multiplier, inflow_adder} selecting whether/how the energy margin is applied.
186
+ ('existing', 'connection'): QuantityType.POWER,
187
+ # [MW] Existing capacity. Constant or Period
188
+ ('existing', 'node'): QuantityType.ENERGY,
189
+ # [MWh] Existing storage capacity. Constant or Period
190
+ ('existing', 'unit'): QuantityType.POWER,
191
+ # [MW] Existing capacity. Constant or Period
192
+ ('exclude_entity_outputs', 'model'): QuantityType.DIMENSIONLESS,
193
+ # Boolean-style toggle: excludes node/unit/connection-level results while preserving group-level results.
194
+ ('fix_storage_periods', 'solve'): QuantityType.DIMENSIONLESS,
195
+ # Array of periods where the storage_values are fixed when the node has storage_include_solve_fix_method is set
196
+ ('fixed_cost', 'connection'): QuantityType.PRICE_PER_CAPACITY,
197
+ # [CUR/kW] Annual fixed cost. Constant or period.
198
+ ('fixed_cost', 'node'): QuantityType.PRICE_PER_STORAGE,
199
+ # [CUR/kWh] Annual fixed cost for storage. Constant or period.
200
+ ('fixed_cost', 'unit'): QuantityType.PRICE_PER_CAPACITY,
201
+ # [CUR/kW] Annual fixed cost. Constant or period.
202
+ ('flow_aggregator', 'flowGroup'): QuantityType.DIMENSIONLESS,
203
+ # Used with group_unit_node or group_connection_node to combine the flows when producing the dispatch output of a node group. Renamed from out...
204
+ ('has_capacity_margin', 'group'): QuantityType.DIMENSIONLESS,
205
+ # A flag whether the group of nodes has a capacity margin constraint in the investment mode.
206
+ ('capacity_margin_method', 'group'): QuantityType.DIMENSIONLESS,
207
+ # Method for the group's capacity-margin constraint (none|manual).
208
+ ('has_inertia', 'group'): QuantityType.DIMENSIONLESS,
209
+ # A flag whether the group of nodes has an inertia constraint active.
210
+ ('has_non_synchronous', 'group'): QuantityType.DIMENSIONLESS,
211
+ # A flag whether the group of nodes has the non-synchronous share constraint active.
212
+ ('include_stochastics', 'group'): QuantityType.DIMENSIONLESS,
213
+ # Includes the stochastic branches to be used for the nodes/units/connections in this group
214
+ ('increase_reserve_ratio', 'reserve__upDown__connection__node'): QuantityType.FRACTION,
215
+ # [factor] The reserve requirement is increased by the flow from the connection to the node multiplied by this ratio. Constant.
216
+ ('increase_reserve_ratio', 'reserve__upDown__group'): QuantityType.FRACTION,
217
+ # [factor] The reserve is increased by the sum of demands from the group members multiplied by this ratio. Constant.
218
+ ('increase_reserve_ratio', 'reserve__upDown__unit__node'): QuantityType.FRACTION,
219
+ # [factor] The reserve requirement is increased by generation from the unit to the node multiplied by this ratio. Constant.
220
+ ('inertia_constant', 'unit__inputNode'): QuantityType.INERTIA_PER_CAPACITY,
221
+ # [MWs/MW] Inertia constant for a synchronously connected unit to this node. Constant.
222
+ ('inertia_constant', 'unit__outputNode'): QuantityType.INERTIA_PER_CAPACITY,
223
+ # [MWs/MW] Inertia constant for a synchronously connected unit to this node. Constant.
224
+ ('inertia_limit', 'group'): QuantityType.INERTIA,
225
+ # [MWs] Minimum for synchronous inertia in the group of nodes. Constant or period.
226
+ ('inflation_offset_investment', 'model'): QuantityType.DURATION,
227
+ # [years] Offset for when investment costs occur within a year. Default 0 (beginning of year).
228
+ ('inflation_offset_operations', 'model'): QuantityType.DURATION,
229
+ # [years] Offset for when operational costs occur within a year. Default 0.5 (middle of year).
230
+ ('inflation_rate', 'model'): QuantityType.FRACTION,
231
+ # [e.g. 0.02 for 2%] Model-wide inflation rate applied to all future costs. When inputs are in real (constant-price) terms, set to 0. When inp...
232
+ ('inflow', 'node'): QuantityType.ENERGY,
233
+ # [MWh] Inflow into the node (negative is outflow). Constant or time.
234
+ ('inflow_method', 'node'): QuantityType.DIMENSIONLESS,
235
+ # Choice how to treat inflow time series. Empty defaults to 'use_original', which does not scale the time series. 'no_inflow' ignores the infl...
236
+ ('invest_cost', 'connection'): QuantityType.PRICE_PER_CAPACITY,
237
+ # [CUR/kW] Investment cost for new 'virtual' capacity. Constant or period.
238
+ ('invest_cost', 'node'): QuantityType.PRICE_PER_STORAGE,
239
+ # [CUR/kWh] Investment cost for new storage capacity. Constant or period.
240
+ ('invest_cost', 'unit'): QuantityType.PRICE_PER_CAPACITY,
241
+ # [CUR/kW] Investment cost of the unit. Constant or period.
242
+ ('invest_forced', 'node'): QuantityType.ENERGY,
243
+ # (empty description) — inferred from ('existing', 'node') = [MWh]: forced storage-capacity investment.
244
+ ('invest_max_period', 'connection'): QuantityType.POWER,
245
+ # [MW] Maximum investment. Period.
246
+ ('invest_max_period', 'group'): QuantityType.UNKNOWN,
247
+ # [MW or MWh] Maximum investment per period to the virtual capacity of a group of units or to the storage capacity of a group of nodes. Period...
248
+ ('invest_max_period', 'node'): QuantityType.ENERGY,
249
+ # [MWh] Maximum storage investment. Period.
250
+ ('invest_max_period', 'unit'): QuantityType.POWER,
251
+ # [MW] Maximum investment. Period.
252
+ ('invest_max_total', 'connection'): QuantityType.POWER,
253
+ # [MW] Maximum investment over all solves. Constant.
254
+ ('invest_max_total', 'group'): QuantityType.UNKNOWN,
255
+ # [MW or MWh] Maximum investment to the virtual capacity of a group of units or to the storage capacity of a group of nodes. Total over all so...
256
+ ('invest_max_total', 'node'): QuantityType.ENERGY,
257
+ # [MWh] Maximum investment over all solves. Constant.
258
+ ('invest_max_total', 'unit'): QuantityType.POWER,
259
+ # [MW] Maximum investment over all solves. Constant.
260
+ ('invest_method', 'connection'): QuantityType.DIMENSIONLESS,
261
+ # Choice of investment method: not_allowed, invest and retire indicate availability of investment and retirement. no_limit removes all limits ...
262
+ ('invest_method', 'group'): QuantityType.DIMENSIONLESS,
263
+ # Choice of investment method: not_allowed, invest and retire indicate availability of investment and retirement. no_limit removes all limits ...
264
+ ('invest_method', 'node'): QuantityType.DIMENSIONLESS,
265
+ # Choice of investment method: either not_allowed or then a combination of 1) investment or retirement and 2) investment limits per period, al...
266
+ ('invest_method', 'unit'): QuantityType.DIMENSIONLESS,
267
+ # Choice of investment method: not_allowed, invest and retire indicate availability of investment and retirement. no_limit removes all limits ...
268
+ ('invest_min_period', 'connection'): QuantityType.POWER,
269
+ # [MW] Minimum investment. Period.
270
+ ('invest_min_period', 'group'): QuantityType.UNKNOWN,
271
+ # [MW or MWh] Minimum investment per period to the virtual capacity of a group of units or to the storage capacity of a group of nodes. Period...
272
+ ('invest_min_period', 'node'): QuantityType.ENERGY,
273
+ # [MWh] Minimum storage investment. Period.
274
+ ('invest_min_period', 'unit'): QuantityType.POWER,
275
+ # [MW] Minimum investment. Period.
276
+ ('invest_min_total', 'connection'): QuantityType.POWER,
277
+ # [MW] Minimum investment over all solves. Constant.
278
+ ('invest_min_total', 'group'): QuantityType.UNKNOWN,
279
+ # [MW or MWh] Minimum investment to the virtual capacity of a group of units or to the storage capacity of a group of nodes. Total over all so...
280
+ ('invest_min_total', 'node'): QuantityType.ENERGY,
281
+ # [MWh] Minimum investment over all solves. Constant.
282
+ ('invest_min_total', 'unit'): QuantityType.POWER,
283
+ # [MW] Minimum investment over all solves. Constant.
284
+ ('invest_periods', 'solve'): QuantityType.DIMENSIONLESS,
285
+ # Array of periods where investments are allowed.
286
+ ('is_DC', 'connection'): QuantityType.DIMENSIONLESS,
287
+ # A flag whether the connection is DC (the flow will not be counted as synchronous if there is a non_synchronous_limit). Default false.
288
+ ('is_non_synchronous', 'unit__inputNode'): QuantityType.DIMENSIONLESS,
289
+ # Chooses whether the unit is synchronously connected to this node.
290
+ ('is_non_synchronous', 'unit__outputNode'): QuantityType.DIMENSIONLESS,
291
+ # Chooses whether the unit is synchronously connected to this node.
292
+ # v56 (fb444974) re-added is_enabled on these three classes; it is a
293
+ # yes_no gate consumed at read time (_spinedb_reader.py:205 drops
294
+ # is_enabled="no" entities) and never enters the LP as a coefficient,
295
+ # so DIMENSIONLESS per the flag convention (is_DC / is_non_synchronous).
296
+ ('is_enabled', 'constraint'): QuantityType.DIMENSIONLESS,
297
+ # Flag whether the constraint is enabled; disabled rows are dropped.
298
+ ('is_enabled', 'reserve__upDown__connection__node'): QuantityType.DIMENSIONLESS,
299
+ # Flag whether the reserve participation is enabled; disabled rows dropped.
300
+ ('is_enabled', 'reserve__upDown__unit__node'): QuantityType.DIMENSIONLESS,
301
+ # Flag whether the reserve participation is enabled; disabled rows dropped.
302
+ ('large_failure_ratio', 'reserve__upDown__connection__node'): QuantityType.FRACTION,
303
+ # [factor] Each connection using the N-1 failure method will have a separate constraint to require sufficient reserve to cover a failure of th...
304
+ ('large_failure_ratio', 'reserve__upDown__unit__node'): QuantityType.FRACTION,
305
+ # [factor] Each unit using the N-1 failure method will have a separate constraint to require sufficient reserve to cover a failure of the unit...
306
+ ('lifetime', 'connection'): QuantityType.DURATION,
307
+ # [years] Used to calculate annuity together with interest rate. Constant or period.
308
+ ('lifetime', 'node'): QuantityType.DURATION,
309
+ # [years] Used to calculate annuity together with interest rate. Constant or period.
310
+ ('lifetime', 'unit'): QuantityType.DURATION,
311
+ # [years] Used to calculate annuity together with interest rate. Constant or period.
312
+ ('lifetime_method', 'connection'): QuantityType.DIMENSIONLESS,
313
+ # Choice how the investments behave after unit runs out of lifetime. Automatic reinvestment causes the model to keep the capacity until the en...
314
+ ('lifetime_method', 'node'): QuantityType.DIMENSIONLESS,
315
+ # Choice how the investments behave after unit runs out of lifetime. Automatic reinvestment causes the model to keep the capacity until the en...
316
+ ('lifetime_method', 'unit'): QuantityType.DIMENSIONLESS,
317
+ # Choice how the investments behave after unit runs out of lifetime. Automatic reinvestment causes the model to keep the capacity until the en...
318
+ ('max_cumulative_flow', 'flowGroup'): QuantityType.POWER,
319
+ # [MW] Maximum average flow, which limits the cumulative flow for a group of connection_nodes and/or unit_nodes. The average value is multipli...
320
+ ('max_flow_for_unconstrained_variables', 'model'): QuantityType.POWER,
321
+ # [MW] Upper bound assigned to LP variables that have no other cap (invest_no_limit capacity; flows through edges whose capacity_max_coeff is ...
322
+ ('max_instant_flow', 'flowGroup'): QuantityType.POWER,
323
+ # [MW] Maximum instantenous flow for the aggregated flow of all group members. Constant or period.
324
+ ('max_share', 'reserve__upDown__connection__node'): QuantityType.FRACTION,
325
+ # [factor] Maximum ratio for the transfer of reserve to this node. Constant.
326
+ ('max_share', 'reserve__upDown__unit__node'): QuantityType.FRACTION,
327
+ # [factor] Maximum ratio for the transfer of reserve to this node. Constant.
328
+ ('min_cumulative_flow', 'flowGroup'): QuantityType.POWER,
329
+ # [MW] Minimum average flow, which limits the cumulative flow for a group of connection_nodes and/or unit_nodes. The average value is multipli...
330
+ ('min_downtime', 'unit'): QuantityType.DURATION,
331
+ # [hours] Minimum time the unit must stay offline after shutting down. Requires minimum_time_method set to 'min_downtime' or 'both'. Constant.
332
+ ('min_instant_flow', 'flowGroup'): QuantityType.POWER,
333
+ # [MW] Minimum instantenous flow for the aggregated flow of all group members. Constant or period.
334
+ ('min_load', 'unit'): QuantityType.FRACTION,
335
+ # [0-1] Minimum load of the unit. Applies only if the unit has an online variable. With linear startups, it is the share of capacity started u...
336
+ ('min_uptime', 'unit'): QuantityType.DURATION,
337
+ # [hours] Minimum time the unit must stay online after starting up. Requires minimum_time_method set to 'min_uptime' or 'both'. Constant.
338
+ ('minimum_time_method', 'unit'): QuantityType.DIMENSIONLESS,
339
+ # Choice between minimum up- and downtimes (none, min_downtime, min_uptime, both). Setting this to anything other than 'none' will activate on...
340
+ ('new_stepduration', 'group'): QuantityType.DURATION,
341
+ # Hours. Members of this group operate at this step duration. Overrides the solve-level new_stepduration for these entities. Used for multi-re...
342
+ ('new_stepduration', 'solve'): QuantityType.DURATION,
343
+ # Hours. Creates a new `timeline` from the old for this `solve` with this timestep duration. The new timeline will sum or average the other ti...
344
+ ('node_type', 'node'): QuantityType.DIMENSIONLESS,
345
+ # Role of this node in the LP. 'commodity' = price-exposed source/sink with no balance constraint (e.g. fuel imports, no storage); 'balance' ...
346
+ ('non_synchronous_limit', 'group'): QuantityType.FRACTION,
347
+ # [share, e.g. 0.8 means 80%] The maximum share of non-synchronous generation in the node group. Constant or period.
348
+ ('other_operational_cost', 'connection'): QuantityType.PRICE_PER_ENERGY,
349
+ # [CUR/MWh] Other operational variable cost for trasferring over the connection. Constant, Period or time.
350
+ ('other_operational_cost', 'unit__inputNode'): QuantityType.PRICE_PER_ENERGY,
351
+ # [CUR/MWh] Other operational variable cost for energy flows. Constant, Period or Time.
352
+ ('other_operational_cost', 'unit__outputNode'): QuantityType.PRICE_PER_ENERGY,
353
+ # [CUR/MWh] Other operational variable cost for energy flows. Constant, Period or Time.
354
+ ('output_connection__node__node_flow_t', 'model'): QuantityType.DIMENSIONLESS,
355
+ # Boolean output toggle: per-timestep connection→node flows. Flag, no physical unit.
356
+ ('output_connection_flow_separate', 'model'): QuantityType.DIMENSIONLESS,
357
+ # Boolean output toggle: produce connection flows separately for each direction. Flag.
358
+ ('output_horizon', 'model'): QuantityType.DIMENSIONLESS,
359
+ # Outputs the flows in the horizons. Used for testing the model.
360
+ ('output_node_balance_t', 'model'): QuantityType.DIMENSIONLESS,
361
+ # Boolean output toggle: per-timestep node balance (inflows/outflows) for diagnostics. Flag.
362
+ ('print_dispatch', 'group'): QuantityType.DIMENSIONLESS,
363
+ # Creates the timewise flow output for this node group (node-group dispatch table). Renamed from output_nodeGroup_dispatch.
364
+ ('print_indicators', 'group'): QuantityType.DIMENSIONLESS,
365
+ # Flag to output node-group indicator results for groups whose members are nodes (group__node). Renamed from output_nodeGroup_indicators.
366
+ ('output_ramp_envelope', 'model'): QuantityType.DIMENSIONLESS,
367
+ # Boolean output toggle: include the seven-parameter ramp-envelope diagnostic outputs. Flag.
368
+ ('output_unit__node_flow_t', 'model'): QuantityType.DIMENSIONLESS,
369
+ # Boolean output toggle: per-timestep unit→node flows. Flag.
370
+ ('output_unit__node_ramp_t', 'model'): QuantityType.DIMENSIONLESS,
371
+ # Boolean output toggle: per-timestep per-unit ramps. Flag.
372
+ ('peak_inflow', 'node'): QuantityType.ENERGY,
373
+ # [MWh] Highest flow for scaling the inflow. Used only with inflow_method scale_to_annual_and_peak_flow. Constant or period.
374
+ ('penalty_capacity_margin', 'group'): QuantityType.PRICE_PER_CAPACITY,
375
+ # [CUR/kW] Penalty for violating the capacity margin constraint. Uses operational discounting (not annualized over lifetime like investment co...
376
+ ('penalty_down', 'node'): QuantityType.PRICE_PER_ENERGY,
377
+ # [CUR/MWh] Penalty cost for increasing consumption in the node (excess energy). Constant, Period or Time.
378
+ ('penalty_inertia', 'group'): QuantityType.PRICE_PER_INERTIA,
379
+ # [CUR/MWs] Penalty for violating the inertia constraint. Cost scales with the duration of the violation. Constant or period.
380
+ ('penalty_method', 'node'): QuantityType.DIMENSIONLESS,
381
+ # Per-node toggle: 'regular' keeps balance-slack (penalty) vars, 'off' removes them.
382
+ ('penalty_non_synchronous', 'group'): QuantityType.PRICE_PER_ENERGY,
383
+ # [CUR/MWh] Penalty for violating the non synchronous constraint. Constant or period.
384
+ ('penalty_reserve', 'reserve__upDown__group'): QuantityType.PRICE_PER_CAPACITY,
385
+ # [CUR/MW] Penalty for violating a reserve constraint. Constant.
386
+ ('penalty_up', 'node'): QuantityType.PRICE_PER_ENERGY,
387
+ # [CUR/MWh] Penalty cost for decreasing consumption in the node (energy not served). Constant, Period or Time.
388
+ ('period_timeset', 'solve'): QuantityType.DIMENSIONLESS,
389
+ # Map of periods with associated timesets that will be included in the solve. Index: period name, value: timeset name.
390
+ ('periods_available', 'model'): QuantityType.DIMENSIONLESS,
391
+ # (Optional) Array of periods available for the model. Periods that are in the data, but are not in period_timeset.
392
+ ('price', 'commodity'): QuantityType.PRICE_PER_ENERGY,
393
+ # [CUR/MWh or other unit] Price of the commodity. Constant, period or time.
394
+ ('price_ladder_annual', 'commodity'): QuantityType.DIMENSIONLESS,
395
+ # Stepped supply curve for price_method='price_ladder_annual'. Two forms accepted: 2d map with rows 'tier,price,quantity' applies the same pe...
396
+ ('price_ladder_cumulative', 'commodity'): QuantityType.DIMENSIONLESS,
397
+ # Stepped supply curve for price_method='price_ladder_cumulative'. 2d map with rows 'tier,price,quantity' — one row per tier, giving the tier...
398
+ ('price_method', 'commodity'): QuantityType.DIMENSIONLESS,
399
+ # How the commodity's price enters the LP. 'price' = scalar or time-series price x flow (current behaviour); 'price_ladder_annual' = stepped ...
400
+ ('profile', 'profile'): QuantityType.FRACTION,
401
+ # [factor, typically between 0-1]. Availability time series for a fluctuating resource like wind power. Time.
402
+ ('profile_method', 'connection__profile'): QuantityType.DIMENSIONLESS,
403
+ # Choice of profile method (upper_limit, lower_limit, fixed). Negative values also possible.
404
+ ('profile_method', 'node__profile'): QuantityType.DIMENSIONLESS,
405
+ # Choice of profile method (upper_limit, lower_limit, fixed). Negative values also possible.
406
+ ('profile_method', 'unit__node__profile'): QuantityType.DIMENSIONLESS,
407
+ # Choice of profile method (upper_limit, lower_limit, fixed). Negative values also possible.
408
+ ('ramp_cost', 'unit__inputNode'): QuantityType.PRICE_PER_CAPACITY,
409
+ # [CUR/MW] Cost of ramping the unit. Constant.
410
+ ('ramp_cost', 'unit__outputNode'): QuantityType.PRICE_PER_CAPACITY,
411
+ # [CUR/MW] Cost of ramping the unit. Constant.
412
+ ('ramp_method', 'unit__inputNode'): QuantityType.DIMENSIONLESS,
413
+ # Choice of ramp method. 'ramp_limit' poses a limit on the speed of ramp. 'ramp_cost' poses a cost on ramping the flow (NOT FUNCTIONAL AS OF 1...
414
+ ('ramp_method', 'unit__outputNode'): QuantityType.DIMENSIONLESS,
415
+ # Choice of ramp methods.
416
+ ('ramp_speed_down', 'unit__inputNode'): QuantityType.RAMP_RATE,
417
+ # [per unit / minute] Maximum ramp down speed. Constant.
418
+ ('ramp_speed_down', 'unit__outputNode'): QuantityType.RAMP_RATE,
419
+ # [per unit / minute] Maximum ramp down speed. Constant.
420
+ ('ramp_speed_up', 'unit__inputNode'): QuantityType.RAMP_RATE,
421
+ # [per unit / minute] Maximum ramp up speed. Constant.
422
+ ('ramp_speed_up', 'unit__outputNode'): QuantityType.RAMP_RATE,
423
+ # [per unit / minute] Maximum ramp up speed. Constant.
424
+ ('reactance', 'connection'): QuantityType.DIMENSIONLESS,
425
+ # [p.u.] Per-unit reactance of the transmission line. Used for DC power flow when the connection is within a nodeGroup that has transfer_metho...
426
+ ('realized_invest_periods', 'solve'): QuantityType.DIMENSIONLESS,
427
+ # Array of the periods that will realize the investment decisions. If not used when the invest_periods exist. The realized_periods are used to...
428
+ ('realized_periods', 'solve'): QuantityType.DIMENSIONLESS,
429
+ # Array of periods that will be realized in the solve.
430
+ ('reference_node', 'group'): QuantityType.DIMENSIONLESS,
431
+ # Name of the reference bus node (angle fixed to zero) for DC power flow. Optional — if not specified, automatically selected as the node with...
432
+ ('reliability', 'reserve__upDown__connection__node'): QuantityType.FRACTION,
433
+ # [factor] The share of the reservation that is counted to reserves (sometimes reserve sources are not fully trusted). Constant.
434
+ ('reliability', 'reserve__upDown__unit__node'): QuantityType.FRACTION,
435
+ # [factor] The share of the reservation that is counted to reserves (sometimes reserve sources are not fully trusted). Constant.
436
+ ('reservation', 'reserve__upDown__group'): QuantityType.POWER,
437
+ # [MW] Amount of reserve to be reserved. Constant or time.
438
+ ('reserve_method', 'reserve__upDown__group'): QuantityType.DIMENSIONLESS,
439
+ # Choice of reserve method: no_reserve, timeseries_only, dynamic_only, large_failure_only, timeseries_and_dynamic, timeseries_and_large_failur...
440
+ ('retire_max_period', 'connection'): QuantityType.POWER,
441
+ # [MW] Maximum retired capacity. Period.
442
+ ('retire_max_period', 'node'): QuantityType.ENERGY,
443
+ # [MWh] Maximum retired storage capacity. Period.
444
+ ('retire_max_period', 'unit'): QuantityType.POWER,
445
+ # [MW] Maximum retired capacity. Period.
446
+ ('retire_max_total', 'connection'): QuantityType.POWER,
447
+ # [MW] Maximum retired capacity over all solves. Constant.
448
+ ('retire_max_total', 'node'): QuantityType.ENERGY,
449
+ # [MWh] Maximum retired storage capacity over all solves. Constant.
450
+ ('retire_max_total', 'unit'): QuantityType.POWER,
451
+ # [MW] Maximum retired capacity over all solves. Constant.
452
+ ('retire_min_period', 'connection'): QuantityType.POWER,
453
+ # [MW] Maximum retired capacity. Period.
454
+ ('retire_min_period', 'node'): QuantityType.ENERGY,
455
+ # [MWh] Minimum retired storage capacity. Period.
456
+ ('retire_min_period', 'unit'): QuantityType.POWER,
457
+ # [MW] Minimum retired capacity. Period.
458
+ ('retire_min_total', 'connection'): QuantityType.POWER,
459
+ # [MW] Minimum retired capacity over all solves. Constant.
460
+ ('retire_min_total', 'node'): QuantityType.ENERGY,
461
+ # [MWh] Minimum retired storage capacity over all solves. Constant.
462
+ ('retire_min_total', 'unit'): QuantityType.POWER,
463
+ # [MW] Minimum retired capacity over all solves. Constant.
464
+ ('rolling_duration', 'solve'): QuantityType.DURATION,
465
+ # Hours (Optional). Duration of rolling, if not stated, assumed to be the whole timeline of the solve
466
+ ('rolling_solve_horizon', 'solve'): QuantityType.DURATION,
467
+ # Hours (Required if rolling_window solve). How long into the future the roll sees
468
+ ('rolling_solve_jump', 'solve'): QuantityType.DURATION,
469
+ # Hours, (Required if rolling_window solve). Interval between the start points of the rolls. Should be smaller than the horizon
470
+ ('salvage_value', 'connection'): QuantityType.PRICE_PER_CAPACITY,
471
+ # [CUR/kW] Salvage value for retiring capacity. Constant or period.
472
+ ('salvage_value', 'node'): QuantityType.PRICE_PER_STORAGE,
473
+ # [CUR/kWh] Salvage value of the storage. Constant or period.
474
+ ('salvage_value', 'unit'): QuantityType.PRICE_PER_CAPACITY,
475
+ # [CUR/kW] Salvage value of the unit. Constant or period.
476
+ ('scaling', 'solve'): QuantityType.DIMENSIONLESS,
477
+ # Numerical LP-autoscaler mode for this solve: 'off'/'solver_only'/'basic'/'full' (default). Solver-config choice, never an LP coefficient.
478
+ ('self_discharge_loss', 'node'): QuantityType.FRACTION,
479
+ # [e.g. 0.01 means 1% every hour] Loss of stored energy over time. Constant or time.
480
+ ('sense', 'constraint'): QuantityType.DIMENSIONLESS,
481
+ # The sense of the constraint ('greater_than', 'less_than' or 'equal').
482
+ ('share_loss_of_load', 'group'): QuantityType.DIMENSIONLESS,
483
+ # Force the upward slack of the nodes in this group to be equal or inflow (demand) weighted
484
+ ('solve_mode', 'solve'): QuantityType.DIMENSIONLESS,
485
+ # A single_solve or rolling_window for a set of rolling optimisation windows solved in a sequence.
486
+ ('solver', 'solve'): QuantityType.DIMENSIONLESS,
487
+ # Choice of solver. HIGHs used as default. GLPSOL is another open source option. Out of commercial solvers CPLEX has been tested.
488
+ ('solver_arguments', 'solve'): QuantityType.DIMENSIONLESS,
489
+ # Array of text commands for passing command line arguments to a solver. Can be used to set additional solver parameters.
490
+ # HiGHS-specific config strings ('simplex'/'ipm'/'choose', 'on'/'off') — all DIMENSIONLESS toggles, no physical unit.
491
+ ('highs_method', 'solve'): QuantityType.DIMENSIONLESS,
492
+ ('highs_parallel', 'solve'): QuantityType.DIMENSIONLESS,
493
+ ('highs_presolve', 'solve'): QuantityType.DIMENSIONLESS,
494
+ # Cross-solver configuration knobs normalised by polar-high. All are config (strings/maps/integers) or unitless
495
+ # tolerances/counts; none carry a physical [MW/MWh/CUR] unit.
496
+ ('solver_io_api', 'solve'): QuantityType.DIMENSIONLESS,
497
+ # 'direct' | 'mps' | 'lp' — how the model is handed to the solver. String choice.
498
+ ('solver_log_level', 'solve'): QuantityType.DIMENSIONLESS,
499
+ # 'silent' | 'normal' | 'verbose' — solver-log verbosity. String choice.
500
+ ('solver_mip_gap', 'solve'): QuantityType.FRACTION,
501
+ # Relative MIP optimality gap (dimensionless ratio in [0, 1]); classified as FRACTION because it is a unitless share.
502
+ ('solver_options', 'solve'): QuantityType.DIMENSIONLESS,
503
+ # Map of solver-specific option name -> raw value. Composite, no physical unit.
504
+ ('solver_threads', 'solve'): QuantityType.DIMENSIONLESS,
505
+ # Maximum number of solver worker threads — an integer count, not a physical quantity.
506
+ ('solver_time_limit', 'solve'): QuantityType.DURATION,
507
+ # Wall-clock time limit in seconds. DURATION carries time semantics; autoscaler treats it like other durations.
508
+ ('solver_precommand', 'solve'): QuantityType.DIMENSIONLESS,
509
+ # Additional command to execute before calling the solver. Can be used to e.g. reserve a floating license for a commercial solver.
510
+ ('use_row_scaling', 'solve'): QuantityType.DIMENSIONLESS,
511
+ # Boolean-style toggle ('yes'/'no') enabling experimental automatic row scaling. Flag, no physical unit.
512
+ ('solves', 'model'): QuantityType.DIMENSIONLESS,
513
+ # Sequence of solves in the model. Array.
514
+ ('small_number_threshold', 'model'): QuantityType.DIMENSIONLESS,
515
+ # Config scalar: LP coefficients/RHS below this magnitude are floored to 0. Not an LP coefficient itself.
516
+ ('startup_cost', 'connection'): QuantityType.PRICE_PER_CAPACITY,
517
+ # [CUR/MW] Cost of starting up one MW of 'virtual' capacity. Constant.
518
+ ('startup_cost', 'unit'): QuantityType.PRICE_PER_CAPACITY,
519
+ # [CUR/MW] Cost of starting up one MW of 'virtual' capacity. Constant.
520
+ ('startup_method', 'connection'): QuantityType.DIMENSIONLESS,
521
+ # Choice of startup method
522
+ ('startup_method', 'unit'): QuantityType.DIMENSIONLESS,
523
+ # Choice of startup method. Linear startup means that the unit can start partially (anything between 0 and full capacity) but will face startu...
524
+ ('stochastic_branches', 'solve'): QuantityType.DIMENSIONLESS,
525
+ # [4d-Map], Sets branches included in the solve. [Period, branch, start_time (time_step), realized (yes/no), weight (number)]. Only one of the...
526
+ ('storage_binding_method', 'node'): QuantityType.DIMENSIONLESS,
527
+ # Choice how the storage state will be maintained over discontinuos timelines. The default value 'bind_forward_only' will bind forward over an...
528
+ ('storage_nested_fix_method', 'node'): QuantityType.DIMENSIONLESS,
529
+ # Used in nested solve sequences. Set this storage as a long term storage, which end state is passed to the lower level solves as a target. *F...
530
+ ('storage_solve_horizon_method', 'node'): QuantityType.DIMENSIONLESS,
531
+ # Choice how to treat storage state at the end of time horizon of each solve. 'free' lets the model choose the end state. 'use_reference_value...
532
+ ('storage_start_end_method', 'node'): QuantityType.DIMENSIONLESS,
533
+ # Choice whether the start and end states of storage are fixed in the beginning and end of the whole model timeline (not between solves). Uses...
534
+ ('storage_state_end', 'node'): QuantityType.FRACTION,
535
+ # [0-1] Relative state of storage at the end of the last model solve (overrides 'storage_state_end_reference'). Constant.
536
+ ('storage_state_reference_price', 'node'): QuantityType.PRICE_PER_ENERGY,
537
+ # [CUR/MWh] Price for the stored energy at the end of the solve horizon. Requires 'use_reference_price' in 'storage_solve_horizon_method'. Con...
538
+ ('storage_state_reference_value', 'node'): QuantityType.FRACTION,
539
+ # [0-1] Relative state of storage at then end of each solve (can be overwritten in the next solve). Requires 'use_reference_value' in 'storag...
540
+ ('storage_state_start', 'node'): QuantityType.FRACTION,
541
+ # [0-1] Relative state of storage at the beginning of the first model solve (irrespective of when the model starts). Constant.
542
+ ('timeline', 'timeset'): QuantityType.DIMENSIONLESS,
543
+ # The name of the timeline that the timeset uses. (String)
544
+ ('timeline_hole_multiplier', 'solve'): QuantityType.DIMENSIONLESS,
545
+ # [unitless] Multiplier applied to the inverse-step-duration term in nodeBalance_eq and storage-binding constraints across timeline gaps (hole...
546
+ ('timeset_duration', 'timeset'): QuantityType.DURATION,
547
+ # Index: name of the the timestep that starts the timeset, value: duration of the block in timesteps
548
+ ('timeset_weights', 'timeset'): QuantityType.FRACTION,
549
+ # Per-timestep weight map (index: timestep name, value: float) applied to cost and slack terms in the objective. Use for non-RP models where t...
550
+ ('representative_period_weights', 'timeset'): QuantityType.FRACTION,
551
+ # Rank-2 nested Map (base period -> representative period -> weight); RP weighting, mutually exclusive with timeset_weights.
552
+ ('timestep_duration', 'timeline'): QuantityType.DURATION,
553
+ # Map of time steps in the timeline. Index: time step name, value: time step duration in hours.
554
+ ('transfer_method', 'connection'): QuantityType.DIMENSIONLESS,
555
+ # Choice of transfer method. Options: regular (default), no_losses_no_variable_cost, variable_cost_only, exact, unidirectional. 'unidirectiona...
556
+ ('transfer_method', 'group'): QuantityType.DIMENSIONLESS,
557
+ # Override transfer_method for all connections within this nodeGroup. Options: use_connection_transfer_methods (default, no override), no_loss...
558
+ ('unitsize', 'commodity'): QuantityType.DIMENSIONLESS,
559
+ # Numeric scaling for the v_trade variable column (analogous to virtual_unitsize on process/node entities). The variable is in user-MWh divid...
560
+ # Enable automatic row scaling (experimental): derive node_capacity_for_scaling / group_capacity_for_scaling from connected-unit unitsizes (ro...
561
+ ('use_for_representative_periods', 'group'): QuantityType.DIMENSIONLESS,
562
+ # yes_no flag marking a group as an aggregation unit for net-load representative-period selection (preprocessor-only; not read by the LP model).
563
+ ('version', 'model'): QuantityType.DIMENSIONLESS,
564
+ # Contains database version information.
565
+ ('virtual_unitsize', 'connection'): QuantityType.POWER,
566
+ # [MW] Size of single connection - used for integer (lumped) investments.
567
+ ('virtual_unitsize', 'node'): QuantityType.ENERGY,
568
+ # [MWh] Size of a single storage unit - used for integer investments (lumped investments). If not given, assumed from the existing storage cap...
569
+ ('virtual_unitsize', 'unit'): QuantityType.POWER,
570
+ # [MW] Size of a single unit - used for integer investments (lumped investments), minimum loads and start-up costs. If not given, assumed from...
571
+ ('years_represented', 'solve'): QuantityType.DURATION,
572
+ # Map to indicate how many years the period represents before the next period in the solve. Used for discounting. Can be below one (multiple p...
573
+ }
574
+
575
+
576
+ def lookup(parameter_name: str, entity_class: str) -> QuantityType:
577
+ """Return the QuantityType for a (parameter_name, entity_class) key.
578
+
579
+ Raises KeyError if no type is registered. The autoscaler MUST refuse to run
580
+ on a parameter whose type is not declared here — silently defaulting risks
581
+ rescaling currency as energy or vice-versa.
582
+ """
583
+ return PARAMETER_TYPES[(parameter_name, entity_class)]
584
+
585
+
586
+ # Parameters whose unit is ``[MW or MWh]`` — the description is context-dependent
587
+ # on whether the group's members are units/connections (POWER) or nodes (ENERGY).
588
+ # Layer 2 must call :func:`resolve_group_capacity_type` with the group's member
589
+ # entity-class to pick the correct QuantityType at apply time.
590
+ _GROUP_CAPACITY_PARAMS: frozenset[str] = frozenset({
591
+ "cumulative_max_capacity",
592
+ "cumulative_min_capacity",
593
+ "invest_max_period",
594
+ "invest_max_total",
595
+ "invest_min_period",
596
+ "invest_min_total",
597
+ })
598
+
599
+
600
+ def resolve_group_capacity_type(parameter_name: str, member_class: str) -> QuantityType:
601
+ """Resolve `[MW or MWh]` group-level capacity parameters at apply time.
602
+
603
+ Group capacity parameters carry ``[MW or MWh]``: ``POWER`` when the group
604
+ aggregates ``unit`` / ``connection`` capacity, ``ENERGY`` when it aggregates
605
+ ``node`` storage capacity. Raises ``ValueError`` if called for a parameter
606
+ that is not on the context-dependent list, and ``ValueError`` for an
607
+ unrecognised member_class.
608
+ """
609
+ if parameter_name not in _GROUP_CAPACITY_PARAMS:
610
+ raise ValueError(
611
+ f"resolve_group_capacity_type: {parameter_name!r} is not a "
612
+ f"context-dependent group capacity parameter"
613
+ )
614
+ if member_class in ("unit", "connection"):
615
+ return QuantityType.POWER
616
+ if member_class == "node":
617
+ return QuantityType.ENERGY
618
+ raise ValueError(
619
+ f"resolve_group_capacity_type: unrecognised member_class {member_class!r} "
620
+ f"for parameter {parameter_name!r}"
621
+ )