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.
- flextool/__init__.py +41 -0
- flextool/_mem_sampler.py +193 -0
- flextool/_resources.py +43 -0
- flextool/calibrate/__init__.py +51 -0
- flextool/calibrate/__main__.py +11 -0
- flextool/calibrate/_cli.py +316 -0
- flextool/calibrate/_db_alt.py +166 -0
- flextool/calibrate/_final_outputs.py +110 -0
- flextool/calibrate/_guard.py +151 -0
- flextool/calibrate/_loop.py +558 -0
- flextool/calibrate/_readers.py +223 -0
- flextool/calibrate/_report.py +263 -0
- flextool/calibrate/_sizing.py +699 -0
- flextool/calibrate/_solve.py +134 -0
- flextool/calibrate/_solve_status.py +495 -0
- flextool/cli/__init__.py +9 -0
- flextool/cli/_console.py +51 -0
- flextool/cli/_timing.py +147 -0
- flextool/cli/cmd_execute_flextool_workflow.py +187 -0
- flextool/cli/cmd_export_to_tabular.py +56 -0
- flextool/cli/cmd_import_sensitivities.py +75 -0
- flextool/cli/cmd_migrate_database.py +13 -0
- flextool/cli/cmd_open_results_db.py +269 -0
- flextool/cli/cmd_read_matpower.py +66 -0
- flextool/cli/cmd_read_old_flextool.py +63 -0
- flextool/cli/cmd_read_self_describing_tabular_input.py +50 -0
- flextool/cli/cmd_read_tabular_input.py +81 -0
- flextool/cli/cmd_run_flextool.py +1095 -0
- flextool/cli/cmd_scenario_results.py +284 -0
- flextool/cli/cmd_solve_mps.py +169 -0
- flextool/cli/cmd_update_flextool.py +17 -0
- flextool/cli/cmd_write_outputs.py +125 -0
- flextool/common_utils/__init__.py +1 -0
- flextool/common_utils/plot_mem_shape.py +77 -0
- flextool/common_utils/precision.py +451 -0
- flextool/decomposition/__init__.py +0 -0
- flextool/decomposition/region_decomposition.py +128 -0
- flextool/decomposition/region_filter.py +1261 -0
- flextool/engine_polars/__init__.py +110 -0
- flextool/engine_polars/_axis_enums.py +742 -0
- flextool/engine_polars/_benders.py +3462 -0
- flextool/engine_polars/_block_layout.py +1479 -0
- flextool/engine_polars/_blocks.py +1515 -0
- flextool/engine_polars/_commodity_ladder.py +660 -0
- flextool/engine_polars/_cumulative_invest.py +1165 -0
- flextool/engine_polars/_db_loader.py +153 -0
- flextool/engine_polars/_db_reader.py +127 -0
- flextool/engine_polars/_dc_power_flow.py +445 -0
- flextool/engine_polars/_delay.py +442 -0
- flextool/engine_polars/_derived_arithmetic.py +432 -0
- flextool/engine_polars/_derived_block.py +990 -0
- flextool/engine_polars/_derived_branch.py +769 -0
- flextool/engine_polars/_derived_existing.py +1353 -0
- flextool/engine_polars/_derived_npv.py +1297 -0
- flextool/engine_polars/_derived_params.py +9850 -0
- flextool/engine_polars/_derived_profile.py +881 -0
- flextool/engine_polars/_derived_walks.py +276 -0
- flextool/engine_polars/_determinism.py +70 -0
- flextool/engine_polars/_direct_params.py +2186 -0
- flextool/engine_polars/_dump_csvs.py +1009 -0
- flextool/engine_polars/_emit_arc_unions.py +1631 -0
- flextool/engine_polars/_emit_calc_params.py +729 -0
- flextool/engine_polars/_emit_chain_params.py +709 -0
- flextool/engine_polars/_emit_co2_accumulators.py +400 -0
- flextool/engine_polars/_emit_dispatchers.py +690 -0
- flextool/engine_polars/_emit_energy_margin.py +125 -0
- flextool/engine_polars/_emit_energy_margin_adder.py +290 -0
- flextool/engine_polars/_emit_entity_annual.py +428 -0
- flextool/engine_polars/_emit_inflow_scaling.py +1420 -0
- flextool/engine_polars/_emit_leaf_sets.py +550 -0
- flextool/engine_polars/_emit_lp_scaling.py +665 -0
- flextool/engine_polars/_emit_mid_sets.py +859 -0
- flextool/engine_polars/_emit_pdt_params.py +759 -0
- flextool/engine_polars/_emit_per_solve.py +774 -0
- flextool/engine_polars/_emit_period_calc.py +504 -0
- flextool/engine_polars/_emit_period_params.py +2398 -0
- flextool/engine_polars/_emit_provider_io.py +141 -0
- flextool/engine_polars/_emit_reserve.py +574 -0
- flextool/engine_polars/_emit_solve_time.py +311 -0
- flextool/engine_polars/_emit_solve_writers.py +1249 -0
- flextool/engine_polars/_flex_data_accumulator.py +388 -0
- flextool/engine_polars/_flex_data_provider.py +478 -0
- flextool/engine_polars/_group_slack.py +1253 -0
- flextool/engine_polars/_inmemory_reader.py +140 -0
- flextool/engine_polars/_input_source.py +336 -0
- flextool/engine_polars/_invest_seeds.py +191 -0
- flextool/engine_polars/_native_input_writer.py +100 -0
- flextool/engine_polars/_native_run_model.py +1348 -0
- flextool/engine_polars/_orchestration.py +4314 -0
- flextool/engine_polars/_output_writer.py +439 -0
- flextool/engine_polars/_param_shapes.py +1595 -0
- flextool/engine_polars/_parquet_bundle.py +723 -0
- flextool/engine_polars/_pdt_join.py +167 -0
- flextool/engine_polars/_pdt_lookup.py +547 -0
- flextool/engine_polars/_per_solve_sets.py +335 -0
- flextool/engine_polars/_projection_params.py +2056 -0
- flextool/engine_polars/_provider_keys.py +173 -0
- flextool/engine_polars/_provider_translators.py +225 -0
- flextool/engine_polars/_recursive_solve.py +703 -0
- flextool/engine_polars/_region_filter.py +2508 -0
- flextool/engine_polars/_reserve.py +649 -0
- flextool/engine_polars/_solve_acceptance.py +331 -0
- flextool/engine_polars/_solve_config.py +1001 -0
- flextool/engine_polars/_solve_context.py +885 -0
- flextool/engine_polars/_solve_handoff.py +164 -0
- flextool/engine_polars/_solve_state.py +232 -0
- flextool/engine_polars/_solver_base.py +36 -0
- flextool/engine_polars/_solver_dispatch.py +511 -0
- flextool/engine_polars/_spinedb_reader.py +1165 -0
- flextool/engine_polars/_stochastic.py +593 -0
- flextool/engine_polars/_subprocess_solve.py +1838 -0
- flextool/engine_polars/_timeline.py +1416 -0
- flextool/engine_polars/_vectorize.py +438 -0
- flextool/engine_polars/_warm.py +858 -0
- flextool/engine_polars/autoscale/__init__.py +107 -0
- flextool/engine_polars/autoscale/_config.py +218 -0
- flextool/engine_polars/autoscale/_layer2.py +1253 -0
- flextool/engine_polars/autoscale/_layer2_types.py +584 -0
- flextool/engine_polars/autoscale/_quantity_types.py +621 -0
- flextool/engine_polars/autoscale/_report.py +336 -0
- flextool/engine_polars/chain.py +259 -0
- flextool/engine_polars/input.py +6638 -0
- flextool/engine_polars/model.py +4754 -0
- flextool/env_check.py +388 -0
- flextool/export_to_tabular/__init__.py +5 -0
- flextool/export_to_tabular/db_reader.py +224 -0
- flextool/export_to_tabular/excel_writer.py +3559 -0
- flextool/export_to_tabular/export_settings.yaml +377 -0
- flextool/export_to_tabular/export_to_excel.py +227 -0
- flextool/export_to_tabular/formatting.py +543 -0
- flextool/export_to_tabular/sheet_config.py +876 -0
- flextool/gui/__init__.py +0 -0
- flextool/gui/__main__.py +118 -0
- flextool/gui/calibrate_commands.py +184 -0
- flextool/gui/calibrate_jobs.py +424 -0
- flextool/gui/check_tree.py +142 -0
- flextool/gui/cli_format.py +83 -0
- flextool/gui/config_parser.py +68 -0
- flextool/gui/data_models.py +362 -0
- flextool/gui/db_editor_integration.py +202 -0
- flextool/gui/db_version_check.py +269 -0
- flextool/gui/dialogs/__init__.py +0 -0
- flextool/gui/dialogs/add_dialog.py +1098 -0
- flextool/gui/dialogs/calibrate_dialog.py +1259 -0
- flextool/gui/dialogs/file_picker.py +473 -0
- flextool/gui/dialogs/group_picker.py +299 -0
- flextool/gui/dialogs/migration_consent_dialog.py +106 -0
- flextool/gui/dialogs/migration_progress_dialog.py +237 -0
- flextool/gui/dialogs/plot_dialog.py +459 -0
- flextool/gui/dialogs/plot_settings_picker.py +2184 -0
- flextool/gui/dialogs/project_dialog.py +426 -0
- flextool/gui/dialogs/update_dialog.py +212 -0
- flextool/gui/downsampling.py +88 -0
- flextool/gui/error_handling.py +50 -0
- flextool/gui/execution_manager.py +1715 -0
- flextool/gui/execution_window.py +1377 -0
- flextool/gui/hover_tooltip.py +111 -0
- flextool/gui/input_sources.py +730 -0
- flextool/gui/main_window.py +6181 -0
- flextool/gui/network_graph.py +215 -0
- flextool/gui/output_actions.py +393 -0
- flextool/gui/output_log_window.py +159 -0
- flextool/gui/platform_utils.py +421 -0
- flextool/gui/plot_cache.py +88 -0
- flextool/gui/plot_canvas.py +543 -0
- flextool/gui/plot_config_reader.py +272 -0
- flextool/gui/project_utils.py +100 -0
- flextool/gui/result_viewer.py +4394 -0
- flextool/gui/scenario_key.py +162 -0
- flextool/gui/scenario_lists.py +516 -0
- flextool/gui/settings_io.py +360 -0
- flextool/gui/solve_reader.py +103 -0
- flextool/gui/tree_reorder.py +88 -0
- flextool/gui/ui_metrics.py +420 -0
- flextool/input_derivation/__init__.py +281 -0
- flextool/input_derivation/_commodity_ladder.py +375 -0
- flextool/input_derivation/_commodity_ladder_sets.py +70 -0
- flextool/input_derivation/_dc_power_flow.py +377 -0
- flextool/input_derivation/_method_constants.py +77 -0
- flextool/input_derivation/_process_method.py +258 -0
- flextool/input_derivation/_specs.py +1026 -0
- flextool/input_derivation/_validators.py +321 -0
- flextool/lean_parquet.py +159 -0
- flextool/model_builder/__init__.py +5 -0
- flextool/model_builder/build_model.py +589 -0
- flextool/model_builder/encoding.py +67 -0
- flextool/model_builder/names.py +34 -0
- flextool/model_builder/profiles.py +129 -0
- flextool/plot_outputs/__init__.py +14 -0
- flextool/plot_outputs/axis_helpers.py +355 -0
- flextool/plot_outputs/color_template.py +888 -0
- flextool/plot_outputs/config.py +171 -0
- flextool/plot_outputs/format_helpers.py +345 -0
- flextool/plot_outputs/legend_helpers.py +143 -0
- flextool/plot_outputs/orchestrator.py +1141 -0
- flextool/plot_outputs/perf.py +37 -0
- flextool/plot_outputs/plan.py +1787 -0
- flextool/plot_outputs/plot_bars.py +1510 -0
- flextool/plot_outputs/plot_bars_detail.py +753 -0
- flextool/plot_outputs/plot_lines.py +951 -0
- flextool/plot_outputs/shared_manifest.py +564 -0
- flextool/plot_outputs/subplot_helpers.py +137 -0
- flextool/process_inputs/__init__.py +188 -0
- flextool/process_inputs/import_old_excel_input.json +4159 -0
- flextool/process_inputs/read_matpower.py +451 -0
- flextool/process_inputs/read_old_flextool.py +1288 -0
- flextool/process_inputs/read_self_describing_excel.py +1423 -0
- flextool/process_inputs/read_tabular_with_specification.py +1114 -0
- flextool/process_inputs/write_old_flextool_to_db.py +3077 -0
- flextool/process_inputs/write_self_describing_to_db.py +977 -0
- flextool/process_inputs/write_to_input_db.py +269 -0
- flextool/process_outputs/__init__.py +7 -0
- flextool/process_outputs/_annualize.py +55 -0
- flextool/process_outputs/_inmemory_helpers.py +292 -0
- flextool/process_outputs/_output_meta.py +672 -0
- flextool/process_outputs/calc_capacity_flows.py +107 -0
- flextool/process_outputs/calc_connections.py +136 -0
- flextool/process_outputs/calc_costs.py +260 -0
- flextool/process_outputs/calc_group_flows.py +192 -0
- flextool/process_outputs/calc_slacks.py +103 -0
- flextool/process_outputs/calc_storage_vre.py +160 -0
- flextool/process_outputs/drop_levels.py +208 -0
- flextool/process_outputs/handoff_writers.py +1315 -0
- flextool/process_outputs/out_ancillary.py +544 -0
- flextool/process_outputs/out_capacity.py +179 -0
- flextool/process_outputs/out_costs.py +334 -0
- flextool/process_outputs/out_flowgroup.py +189 -0
- flextool/process_outputs/out_flows.py +301 -0
- flextool/process_outputs/out_group.py +475 -0
- flextool/process_outputs/out_node.py +190 -0
- flextool/process_outputs/persist_realized_slice.py +601 -0
- flextool/process_outputs/process_results.py +24 -0
- flextool/process_outputs/read_highs_solution.py +2256 -0
- flextool/process_outputs/read_parameters.py +1799 -0
- flextool/process_outputs/read_sets.py +1095 -0
- flextool/process_outputs/read_variables.py +553 -0
- flextool/process_outputs/solve_order.py +81 -0
- flextool/process_outputs/spinedb_replay.py +412 -0
- flextool/process_outputs/union_realized_slice.py +224 -0
- flextool/process_outputs/write_outputs.py +1286 -0
- flextool/process_outputs/write_spinedb.py +1267 -0
- flextool/representative_periods/__init__.py +5 -0
- flextool/representative_periods/clustering.py +165 -0
- flextool/representative_periods/force_include.py +563 -0
- flextool/representative_periods/netload.py +365 -0
- flextool/representative_periods/netload_inputs.py +345 -0
- flextool/representative_periods/netload_iterate.py +722 -0
- flextool/representative_periods/preprocess.py +948 -0
- flextool/representative_periods/scenario_stack.py +195 -0
- flextool/representative_periods/weights.py +124 -0
- flextool/scenario_comparison/__init__.py +13 -0
- flextool/scenario_comparison/config_builder.py +158 -0
- flextool/scenario_comparison/constants.py +20 -0
- flextool/scenario_comparison/data_models.py +222 -0
- flextool/scenario_comparison/db_reader.py +399 -0
- flextool/scenario_comparison/dispatch_data.py +1002 -0
- flextool/scenario_comparison/dispatch_mappings.py +205 -0
- flextool/scenario_comparison/dispatch_plots.py +691 -0
- flextool/scenario_comparison/input_entity_colors.py +319 -0
- flextool/scenario_comparison/orchestrator.py +453 -0
- flextool/scenario_comparison/plan_union.py +244 -0
- flextool/scenario_comparison/plot_settings_seed.py +205 -0
- flextool/schemas/AXIS_CONTRACT.md +71 -0
- flextool/schemas/canonical_databases/howto_aggregate_output.json +6225 -0
- flextool/schemas/canonical_databases/howto_connections.json +5606 -0
- flextool/schemas/canonical_databases/howto_demand.json +5518 -0
- flextool/schemas/canonical_databases/howto_hydro_reservoir.json +6239 -0
- flextool/schemas/canonical_databases/howto_hydro_reservoir_with_pump.json +5933 -0
- flextool/schemas/canonical_databases/howto_non_sync_and_curtailment.json +5794 -0
- flextool/schemas/canonical_databases/howto_ramp_and_start_up.json +5707 -0
- flextool/schemas/canonical_databases/howto_stochastics.json +6032 -0
- flextool/schemas/canonical_databases/templates_examples.json +13532 -0
- flextool/schemas/canonical_databases/templates_time_settings_only.json +5340 -0
- flextool/schemas/comparison_settings_template.json +197 -0
- flextool/schemas/default_plot_settings.yaml +260 -0
- flextool/schemas/default_plots.yaml +2293 -0
- flextool/schemas/flextool_axis_contract.json +303 -0
- flextool/schemas/flextool_axis_contract.schema.json +247 -0
- flextool/schemas/old_flextool_import_template.json +4443 -0
- flextool/schemas/output_info_template.json +48 -0
- flextool/schemas/output_settings_template.json +256 -0
- flextool/schemas/pre_v26/flextool_template_constant_default.json +2105 -0
- flextool/schemas/pre_v26/flextool_template_default_optional_output.json +2152 -0
- flextool/schemas/pre_v26/flextool_template_default_value.json +2094 -0
- flextool/schemas/pre_v26/flextool_template_drop_down.json +2080 -0
- flextool/schemas/pre_v26/flextool_template_lifetime_method.json +1990 -0
- flextool/schemas/pre_v26/flextool_template_optional_outputs.json +2094 -0
- flextool/schemas/pre_v26/flextool_template_output_node_flows.json +2105 -0
- flextool/schemas/pre_v26/flextool_template_results_master.json +493 -0
- flextool/schemas/pre_v26/flextool_template_rolling_start_remove.json +2087 -0
- flextool/schemas/pre_v26/flextool_template_rolling_window.json +2059 -0
- flextool/schemas/pre_v26/flextool_template_storage_binding_defaults.json +46 -0
- flextool/schemas/pre_v26/flextool_template_v2.json +1990 -0
- flextool/schemas/pre_v26/flextool_template_v25.json +3864 -0
- flextool/schemas/spinedb_results_schema.json +581 -0
- flextool/schemas/spinedb_schema.json +4636 -0
- flextool/solver_config/copt.opt.template +18 -0
- flextool/solver_config/cplex.opt.template +25 -0
- flextool/solver_config/gurobi.opt.template +18 -0
- flextool/solver_config/highs.opt.template +18 -0
- flextool/solver_config/xpress.opt.template +26 -0
- flextool/spinedb_backend/__init__.py +26 -0
- flextool/spinedb_backend/_axis_enums.py +1119 -0
- flextool/spinedb_backend/_backend.py +1139 -0
- flextool/update_flextool/__init__.py +12 -0
- flextool/update_flextool/canonical_databases.py +251 -0
- flextool/update_flextool/db_migration.py +7108 -0
- flextool/update_flextool/ensure_settings_db.py +138 -0
- flextool/update_flextool/export_database.py +103 -0
- flextool/update_flextool/extend_tests_fixture.py +772 -0
- flextool/update_flextool/generate_canonical.py +274 -0
- flextool/update_flextool/initialize_database.py +42 -0
- flextool/update_flextool/install_info.py +225 -0
- flextool/update_flextool/self_update.py +464 -0
- flextool/update_flextool/sync_master_json_template.py +125 -0
- flextool/update_flextool/test_fixtures.py +187 -0
- flextool-4.0.0.dist-info/METADATA +217 -0
- flextool-4.0.0.dist-info/RECORD +322 -0
- flextool-4.0.0.dist-info/WHEEL +5 -0
- flextool-4.0.0.dist-info/entry_points.txt +17 -0
- flextool-4.0.0.dist-info/licenses/LICENSE.txt +19 -0
- flextool-4.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# Entity classes that need an Entity Alternative column (active_by_default=False).
|
|
2
|
+
entity_alternative_classes:
|
|
3
|
+
- node
|
|
4
|
+
- unit
|
|
5
|
+
- connection
|
|
6
|
+
|
|
7
|
+
# Parameters that must appear before the Entity Alternative column.
|
|
8
|
+
# Ensures roundtrip compatibility with the position-based importer.
|
|
9
|
+
pre_entity_alternative_params:
|
|
10
|
+
node: ["node_type"]
|
|
11
|
+
|
|
12
|
+
# Column width settings (unit ≈ width of character '0' in Calibri 11, ~7px).
|
|
13
|
+
minimum_parameter_column_width: 10
|
|
14
|
+
width_non_parameter_columns: 22
|
|
15
|
+
definition_column_width: 11
|
|
16
|
+
index_column_width: 12
|
|
17
|
+
|
|
18
|
+
# Default index column names per layout type (replaces 'x' from the DB).
|
|
19
|
+
index_name_defaults:
|
|
20
|
+
periodic: "period"
|
|
21
|
+
timeseries: "time"
|
|
22
|
+
nested_periodic: "period"
|
|
23
|
+
|
|
24
|
+
# Default index names for stochastic (nested Map) dimensions.
|
|
25
|
+
# Applied when the DB index_name is empty or 'x'.
|
|
26
|
+
stochastic_index_names:
|
|
27
|
+
- "forecast"
|
|
28
|
+
- "branch_time"
|
|
29
|
+
- "time"
|
|
30
|
+
- "is_realized"
|
|
31
|
+
|
|
32
|
+
# Entity classes that define time structure. Their Map parameters use timestep
|
|
33
|
+
# names as indexes but should NOT be filtered out by the time-indexed check.
|
|
34
|
+
time_structure_classes:
|
|
35
|
+
- timeline
|
|
36
|
+
- timeset
|
|
37
|
+
|
|
38
|
+
# Merge two entity classes onto one sheet set, adding a direction column.
|
|
39
|
+
merge_classes:
|
|
40
|
+
unit_node:
|
|
41
|
+
classes:
|
|
42
|
+
unit__inputNode: "input"
|
|
43
|
+
unit__outputNode: "output"
|
|
44
|
+
direction_column: "input_output"
|
|
45
|
+
|
|
46
|
+
# Unpack a Map parameter's indexes as an extra row-identifier column.
|
|
47
|
+
# Creates a separate sheet from the main entity class sheet.
|
|
48
|
+
unpack_map_params:
|
|
49
|
+
unit_node_constraint:
|
|
50
|
+
source_classes: ["unit__inputNode", "unit__outputNode"]
|
|
51
|
+
direction_column: "input_output"
|
|
52
|
+
params: ["constraint_flow_coeff"]
|
|
53
|
+
index_column: "constraint"
|
|
54
|
+
connection_node_constraint:
|
|
55
|
+
source_classes: ["connection__node"]
|
|
56
|
+
params: ["constraint_flow_coeff"]
|
|
57
|
+
index_column: "constraint"
|
|
58
|
+
node_constraint:
|
|
59
|
+
source_classes: ["node"]
|
|
60
|
+
params: ["constraint_invested_capacity_coeff", "constraint_cumulative_pre_built_capacity_coeff", "constraint_state_coeff"]
|
|
61
|
+
index_column: "constraint"
|
|
62
|
+
unit_constraint:
|
|
63
|
+
source_classes: ["unit"]
|
|
64
|
+
params: ["constraint_invested_capacity_coeff", "constraint_cumulative_pre_built_capacity_coeff"]
|
|
65
|
+
index_column: "constraint"
|
|
66
|
+
connection_constraint:
|
|
67
|
+
source_classes: ["connection"]
|
|
68
|
+
params: ["constraint_invested_capacity_coeff", "constraint_cumulative_pre_built_capacity_coeff"]
|
|
69
|
+
index_column: "constraint"
|
|
70
|
+
|
|
71
|
+
# Add columns from a related multi-dim entity class to a sheet.
|
|
72
|
+
add_entity_elements:
|
|
73
|
+
connection:
|
|
74
|
+
from_class: "connection__node__node"
|
|
75
|
+
columns: ["left_node", "right_node"]
|
|
76
|
+
|
|
77
|
+
# Split an entity class across multiple sheet groups with different param subsets.
|
|
78
|
+
split_params:
|
|
79
|
+
solve:
|
|
80
|
+
solve:
|
|
81
|
+
params: ["solve_mode", "solver",
|
|
82
|
+
"contains_solves", "solver_precommand",
|
|
83
|
+
"solver_arguments", "new_stepduration",
|
|
84
|
+
"solver_mip_gap", "scaling",
|
|
85
|
+
"timeline_hole_multiplier",
|
|
86
|
+
"decomposition", "benders_tolerance",
|
|
87
|
+
"benders_max_iter", "benders_in_out_weight"]
|
|
88
|
+
solve_period:
|
|
89
|
+
params: ["period_timeset", "years_represented", "realized_periods",
|
|
90
|
+
"invest_periods", "realized_invest_periods", "fix_storage_periods"]
|
|
91
|
+
rolling_parameters:
|
|
92
|
+
params: ["rolling_solve_jump", "rolling_solve_horizon", "rolling_duration"]
|
|
93
|
+
solve_s_stochastic_branches:
|
|
94
|
+
params: ["stochastic_branches"]
|
|
95
|
+
model:
|
|
96
|
+
model:
|
|
97
|
+
params: ["inflation_rate",
|
|
98
|
+
"inflation_offset_investment", "inflation_offset_operations",
|
|
99
|
+
"max_flow_for_unconstrained_variables",
|
|
100
|
+
"small_number_threshold",
|
|
101
|
+
"output_horizon"]
|
|
102
|
+
model_solve_sequence:
|
|
103
|
+
params: ["solves"]
|
|
104
|
+
model_periods_available:
|
|
105
|
+
params: ["periods_available"]
|
|
106
|
+
timeline:
|
|
107
|
+
timeline:
|
|
108
|
+
params: ["timestep_duration"]
|
|
109
|
+
timeset:
|
|
110
|
+
timeset_c:
|
|
111
|
+
params: ["timeline"]
|
|
112
|
+
timeset_t:
|
|
113
|
+
params: ["timeset_duration", "timeset_weights"]
|
|
114
|
+
timeset_s:
|
|
115
|
+
# representative_period_weights is a rank-2 nested Map (schema type
|
|
116
|
+
# 2d_map) — route it to the multi-index stochastic writer so the Map
|
|
117
|
+
# value survives round-trip (the periodic writer would drop it).
|
|
118
|
+
layout: stochastic
|
|
119
|
+
params: ["representative_period_weights"]
|
|
120
|
+
|
|
121
|
+
# Dimensions to drop from generated sheet names (always-present relationship dims).
|
|
122
|
+
drop_dimensions: ["upDown"]
|
|
123
|
+
|
|
124
|
+
# Rename generated base sheet names to match template conventions.
|
|
125
|
+
rename_sheets:
|
|
126
|
+
constraint: "constraint_sense"
|
|
127
|
+
|
|
128
|
+
# Sheet ordering
|
|
129
|
+
sheet_order:
|
|
130
|
+
- navigate
|
|
131
|
+
- scenario
|
|
132
|
+
- model
|
|
133
|
+
- model_solve_sequence
|
|
134
|
+
- model_periods_available
|
|
135
|
+
- solve
|
|
136
|
+
- solve_period
|
|
137
|
+
- solve_period_period
|
|
138
|
+
- rolling_parameters
|
|
139
|
+
- timeline
|
|
140
|
+
- timeset_c
|
|
141
|
+
- timeset_t
|
|
142
|
+
- timeset_s
|
|
143
|
+
- node_c
|
|
144
|
+
- node_p
|
|
145
|
+
- node_t
|
|
146
|
+
- node_s_inflow
|
|
147
|
+
- node_s_availability
|
|
148
|
+
- commodity_c
|
|
149
|
+
- commodity_p
|
|
150
|
+
- commodity_node
|
|
151
|
+
- connection_c
|
|
152
|
+
- connection_p
|
|
153
|
+
- connection_t
|
|
154
|
+
- unit_c
|
|
155
|
+
- unit_p
|
|
156
|
+
- unit_t
|
|
157
|
+
- unit_s_availability
|
|
158
|
+
- unit_s_efficiency
|
|
159
|
+
- unit_s_min_load
|
|
160
|
+
- unit_node_c
|
|
161
|
+
- unit_node_p
|
|
162
|
+
- unit_node_t
|
|
163
|
+
- unit_node_s
|
|
164
|
+
- profile_t
|
|
165
|
+
- profile_s
|
|
166
|
+
- node_profile_c
|
|
167
|
+
- connection_profile_c
|
|
168
|
+
- unit_node_profile_c
|
|
169
|
+
- group_c
|
|
170
|
+
- group_p
|
|
171
|
+
- group_connection
|
|
172
|
+
- group_connection_node
|
|
173
|
+
- group_node
|
|
174
|
+
- group_unit
|
|
175
|
+
- group_unit_node
|
|
176
|
+
- reserve_connection_node_c
|
|
177
|
+
- reserve_group_c
|
|
178
|
+
- reserve_group_p
|
|
179
|
+
- reserve_group_t
|
|
180
|
+
- reserve_group_s
|
|
181
|
+
- reserve_unit_node_c
|
|
182
|
+
- solve_s_stochastic_branches
|
|
183
|
+
- constraint_sense_c
|
|
184
|
+
- unit_node_constraint_c
|
|
185
|
+
- connection_node_constraint_c
|
|
186
|
+
- node_constraint_c
|
|
187
|
+
- unit_constraint_c
|
|
188
|
+
- connection_constraint_c
|
|
189
|
+
- connection_node
|
|
190
|
+
|
|
191
|
+
# Navigate sheet: groups of related sheets, each row lists sheet names
|
|
192
|
+
# placed across columns A (Constants), B (Periodic), C (Timeseries), D (Stochastic).
|
|
193
|
+
navigate_groups:
|
|
194
|
+
- name: "Configuration"
|
|
195
|
+
color: "D9D9D9"
|
|
196
|
+
rows:
|
|
197
|
+
- [scenario]
|
|
198
|
+
- [model]
|
|
199
|
+
- [model_solve_sequence]
|
|
200
|
+
- [model_periods_available]
|
|
201
|
+
- [solve, solve_period, solve_period_period, solve_s_stochastic_branches]
|
|
202
|
+
- [rolling_parameters]
|
|
203
|
+
- name: "Time"
|
|
204
|
+
color: "D9D9D9"
|
|
205
|
+
rows:
|
|
206
|
+
- [timeline]
|
|
207
|
+
- [timeset_c, timeset_t, "", timeset_s]
|
|
208
|
+
- name: "Core entities"
|
|
209
|
+
color: "D5C2EE"
|
|
210
|
+
rows:
|
|
211
|
+
- [node_c, node_p, node_t, node_s_inflow]
|
|
212
|
+
- ["", "", "", node_s_availability]
|
|
213
|
+
- [commodity_c, commodity_p]
|
|
214
|
+
- [commodity_node]
|
|
215
|
+
- [connection_c, connection_p, connection_t]
|
|
216
|
+
- [connection_node]
|
|
217
|
+
- [unit_c, unit_p, unit_t, unit_s_availability]
|
|
218
|
+
- ["", "", "", unit_s_efficiency]
|
|
219
|
+
- ["", "", "", unit_s_min_load]
|
|
220
|
+
- [unit_node_c, unit_node_p, unit_node_t, unit_node_s]
|
|
221
|
+
- name: "Profiles"
|
|
222
|
+
color: "C5E0B4"
|
|
223
|
+
rows:
|
|
224
|
+
- [profile_t, "", "", profile_s]
|
|
225
|
+
- [node_profile_c]
|
|
226
|
+
- [connection_profile_c]
|
|
227
|
+
- [unit_node_profile_c]
|
|
228
|
+
- name: "Groups"
|
|
229
|
+
color: "BDD7EE"
|
|
230
|
+
rows:
|
|
231
|
+
- [group_c, group_p]
|
|
232
|
+
- [group_connection]
|
|
233
|
+
- [group_connection_node]
|
|
234
|
+
- [group_node]
|
|
235
|
+
- [group_unit]
|
|
236
|
+
- [group_unit_node]
|
|
237
|
+
- name: "Reserves"
|
|
238
|
+
color: "F8CBAD"
|
|
239
|
+
rows:
|
|
240
|
+
- [reserve_connection_node_c, reserve_group_p, reserve_group_t, reserve_group_s]
|
|
241
|
+
- [reserve_group_c]
|
|
242
|
+
- [reserve_unit_node_c]
|
|
243
|
+
- name: "Constraints"
|
|
244
|
+
color: "FFE699"
|
|
245
|
+
rows:
|
|
246
|
+
- [constraint_sense_c]
|
|
247
|
+
- [unit_node_constraint_c]
|
|
248
|
+
- [connection_node_constraint_c]
|
|
249
|
+
- [node_constraint_c]
|
|
250
|
+
- [unit_constraint_c]
|
|
251
|
+
- [connection_constraint_c]
|
|
252
|
+
|
|
253
|
+
# Sheets that require the --include-advanced flag.
|
|
254
|
+
# Omitted by default; included when advanced features are needed or when the
|
|
255
|
+
# database contains data that requires them.
|
|
256
|
+
advanced_sheets:
|
|
257
|
+
- model_periods_available
|
|
258
|
+
- solve_period_period
|
|
259
|
+
|
|
260
|
+
# Parameter groups required for a functioning FlexTool model. The GUI
|
|
261
|
+
# group picker highlights these and ships them pre-checked. A user can
|
|
262
|
+
# still produce a valid input file without them by combining data from
|
|
263
|
+
# multiple input sources in Spine Toolbox. Order here is the display
|
|
264
|
+
# order in the picker (most foundational first).
|
|
265
|
+
required_groups:
|
|
266
|
+
- timeline
|
|
267
|
+
- model
|
|
268
|
+
- solve_basics
|
|
269
|
+
- basics
|
|
270
|
+
|
|
271
|
+
# When --groups is used, sheets listed here are kept even if all their
|
|
272
|
+
# parameters were filtered out, provided one of the listed parent groups
|
|
273
|
+
# is in the selection. Use this for structural sheets whose presence is
|
|
274
|
+
# needed to wire up a group's data even when the columns themselves live
|
|
275
|
+
# in another group.
|
|
276
|
+
#
|
|
277
|
+
# Example:
|
|
278
|
+
# solve_basics:
|
|
279
|
+
# - solve_period
|
|
280
|
+
always_include_with_groups: {}
|
|
281
|
+
|
|
282
|
+
# INFO rows: optional information text shown at the top of specific sheets.
|
|
283
|
+
# The reader ignores rows starting with "INFO:".
|
|
284
|
+
info_rows:
|
|
285
|
+
model: "INFO: There can be only one model per scenario - best to just use same model name always."
|
|
286
|
+
model_solve_sequence: "INFO: FlexTool can define one or more solves to be executed in sequence. They will be executed in top to down order from the column the model uses."
|
|
287
|
+
|
|
288
|
+
# Data type overrides for parameters where the DB type_list doesn't capture
|
|
289
|
+
# the full convention. Spine DB uses 2d-map with string "yes" values as a
|
|
290
|
+
# workaround for map-of-arrays (which it doesn't support natively).
|
|
291
|
+
data_type_overrides:
|
|
292
|
+
nested_periodic:
|
|
293
|
+
invest_periods: "string (2d-map)"
|
|
294
|
+
realized_invest_periods: "string (2d-map)"
|
|
295
|
+
realized_periods: "string (2d-map)"
|
|
296
|
+
fix_storage_periods: "string (2d-map)"
|
|
297
|
+
|
|
298
|
+
# Parameters that are ONLY period-indexed (never time-indexed).
|
|
299
|
+
# Used to filter them out of _t sheets. Derived from parameter descriptions.
|
|
300
|
+
# Parameters not listed here but having 1d_map in type_list are assumed to be
|
|
301
|
+
# usable on both _p and _t sheets.
|
|
302
|
+
period_only_params:
|
|
303
|
+
node:
|
|
304
|
+
- constraint_invested_capacity_coeff
|
|
305
|
+
- constraint_cumulative_pre_built_capacity_coeff
|
|
306
|
+
- constraint_state_coeff
|
|
307
|
+
- annual_flow
|
|
308
|
+
- cumulative_max_capacity
|
|
309
|
+
- cumulative_min_capacity
|
|
310
|
+
- existing
|
|
311
|
+
- fixed_cost
|
|
312
|
+
- interest_rate
|
|
313
|
+
- invest_cost
|
|
314
|
+
- invest_forced
|
|
315
|
+
- invest_max_period
|
|
316
|
+
- invest_min_period
|
|
317
|
+
- lifetime
|
|
318
|
+
- peak_inflow
|
|
319
|
+
- retire_max_period
|
|
320
|
+
- retire_min_period
|
|
321
|
+
- penalty_down
|
|
322
|
+
- penalty_up
|
|
323
|
+
- salvage_value
|
|
324
|
+
- self_discharge_loss
|
|
325
|
+
- storage_state_reference_price
|
|
326
|
+
- storage_state_reference_value
|
|
327
|
+
unit:
|
|
328
|
+
- constraint_invested_capacity_coeff
|
|
329
|
+
- constraint_cumulative_pre_built_capacity_coeff
|
|
330
|
+
- cumulative_max_capacity
|
|
331
|
+
- cumulative_min_capacity
|
|
332
|
+
- existing
|
|
333
|
+
- fixed_cost
|
|
334
|
+
- interest_rate
|
|
335
|
+
- invest_cost
|
|
336
|
+
- invest_max_period
|
|
337
|
+
- invest_min_period
|
|
338
|
+
- lifetime
|
|
339
|
+
- retire_max_period
|
|
340
|
+
- retire_min_period
|
|
341
|
+
- salvage_value
|
|
342
|
+
connection:
|
|
343
|
+
- constraint_invested_capacity_coeff
|
|
344
|
+
- constraint_cumulative_pre_built_capacity_coeff
|
|
345
|
+
- cumulative_max_capacity
|
|
346
|
+
- cumulative_min_capacity
|
|
347
|
+
- existing
|
|
348
|
+
- fixed_cost
|
|
349
|
+
- interest_rate
|
|
350
|
+
- invest_cost
|
|
351
|
+
- invest_max_period
|
|
352
|
+
- invest_min_period
|
|
353
|
+
- lifetime
|
|
354
|
+
- retire_max_period
|
|
355
|
+
- retire_min_period
|
|
356
|
+
- salvage_value
|
|
357
|
+
group:
|
|
358
|
+
- co2_max_period
|
|
359
|
+
- inertia_limit
|
|
360
|
+
- invest_max_period
|
|
361
|
+
- invest_min_period
|
|
362
|
+
- max_cumulative_flow
|
|
363
|
+
- max_instant_flow
|
|
364
|
+
- min_cumulative_flow
|
|
365
|
+
- min_instant_flow
|
|
366
|
+
- non_synchronous_limit
|
|
367
|
+
- penalty_capacity_margin
|
|
368
|
+
- penalty_inertia
|
|
369
|
+
- penalty_non_synchronous
|
|
370
|
+
solve:
|
|
371
|
+
- years_represented
|
|
372
|
+
unit__inputNode:
|
|
373
|
+
- constraint_flow_coeff
|
|
374
|
+
unit__outputNode:
|
|
375
|
+
- constraint_flow_coeff
|
|
376
|
+
connection__node:
|
|
377
|
+
- constraint_flow_coeff
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Top-level orchestrator for exporting a FlexTool Spine DB to an Excel file."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from openpyxl import Workbook
|
|
9
|
+
|
|
10
|
+
from flextool.export_to_tabular.db_reader import DatabaseContents, read_database
|
|
11
|
+
from flextool.export_to_tabular.sheet_config import SheetSpec, build_sheet_specs, load_settings
|
|
12
|
+
from flextool.export_to_tabular.excel_writer import (
|
|
13
|
+
IndexClassifier,
|
|
14
|
+
write_array_transposed_sheet_v2,
|
|
15
|
+
write_constant_sheet,
|
|
16
|
+
write_constant_sheet_v2,
|
|
17
|
+
write_link_sheet,
|
|
18
|
+
write_link_sheet_v2,
|
|
19
|
+
write_navigate_sheet,
|
|
20
|
+
write_nested_periodic_sheet,
|
|
21
|
+
write_nested_periodic_sheet_v2,
|
|
22
|
+
write_periodic_sheet,
|
|
23
|
+
write_periodic_sheet_v2,
|
|
24
|
+
write_scenario_sheet,
|
|
25
|
+
write_ladder_sheet_v2,
|
|
26
|
+
write_stochastic_sheet_v2,
|
|
27
|
+
write_timeseries_sheet,
|
|
28
|
+
write_timeseries_sheet_v2,
|
|
29
|
+
write_version_sheet,
|
|
30
|
+
)
|
|
31
|
+
import flextool.export_to_tabular.excel_writer as _ew
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _build_tab_color_map(navigate_groups: list[dict[str, Any]]) -> dict[str, str]:
|
|
35
|
+
"""Build a mapping from sheet name to tab color (hex RGB string) from navigate groups."""
|
|
36
|
+
color_map: dict[str, str] = {}
|
|
37
|
+
for group in navigate_groups:
|
|
38
|
+
color = group["color"]
|
|
39
|
+
for row in group.get("rows", []):
|
|
40
|
+
for sheet_name in row:
|
|
41
|
+
color_map[sheet_name] = color
|
|
42
|
+
return color_map
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def export_to_excel(
|
|
46
|
+
db_url: str,
|
|
47
|
+
output_path: str,
|
|
48
|
+
include_advanced: bool = False,
|
|
49
|
+
use_new_format: bool = True,
|
|
50
|
+
include_stochastics: bool = False, # deprecated alias for include_advanced
|
|
51
|
+
include_groups: list[str] | None = None,
|
|
52
|
+
) -> None:
|
|
53
|
+
"""Export a FlexTool Spine DB to an Excel (.xlsx) file.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
db_url: Spine DB URL (e.g., 'sqlite:///path/to/db.sqlite')
|
|
57
|
+
output_path: Output .xlsx file path
|
|
58
|
+
include_advanced: Whether to include advanced sheets (solve sequences,
|
|
59
|
+
periods_available, stochastic data).
|
|
60
|
+
use_new_format: If True, use the v2 self-describing format with embedded
|
|
61
|
+
metadata. If False, use the original v1 format.
|
|
62
|
+
include_stochastics: Deprecated alias for include_advanced.
|
|
63
|
+
include_groups: When given, restrict output to parameters whose
|
|
64
|
+
``parameter_group_name`` is in the list (e.g. ``['basics',
|
|
65
|
+
'model', 'timeline', 'solve_basics']``). Parameters with no
|
|
66
|
+
group are dropped. Sheets that lose all their columns are
|
|
67
|
+
dropped, unless listed under a selected group in
|
|
68
|
+
``always_include_with_groups`` in ``export_settings.yaml``.
|
|
69
|
+
"""
|
|
70
|
+
if include_stochastics:
|
|
71
|
+
include_advanced = True
|
|
72
|
+
|
|
73
|
+
# 1. Read all data from the database
|
|
74
|
+
print(f"Reading database: {db_url}")
|
|
75
|
+
db_contents: DatabaseContents = read_database(db_url)
|
|
76
|
+
|
|
77
|
+
# 1b. Build index classifier and entity alternatives index
|
|
78
|
+
_ew._index_classifier = IndexClassifier(db_contents)
|
|
79
|
+
_ew._build_entity_alts_index(db_contents)
|
|
80
|
+
|
|
81
|
+
# 2. Load settings and build ordered sheet specifications
|
|
82
|
+
print("Building sheet specifications...")
|
|
83
|
+
settings = load_settings()
|
|
84
|
+
|
|
85
|
+
# 2b. Load data type overrides and width settings
|
|
86
|
+
_ew._data_type_overrides = settings.get("data_type_overrides", {})
|
|
87
|
+
_ew._min_param_width = settings.get("minimum_parameter_column_width", 10)
|
|
88
|
+
_ew._non_param_width = settings.get("width_non_parameter_columns", 22)
|
|
89
|
+
_ew._def_col_width = settings.get("definition_column_width", 11)
|
|
90
|
+
_ew._index_col_width = settings.get("index_column_width", 12)
|
|
91
|
+
_ew._period_only_params = settings.get("period_only_params", {})
|
|
92
|
+
_ew._stochastic_index_names = settings.get("stochastic_index_names",
|
|
93
|
+
["forecast", "branch_time", "time", "is_realized"])
|
|
94
|
+
_ew._time_structure_classes = set(settings.get("time_structure_classes", []))
|
|
95
|
+
specs: list[SheetSpec] = build_sheet_specs(
|
|
96
|
+
db_contents,
|
|
97
|
+
settings,
|
|
98
|
+
include_groups=set(include_groups) if include_groups else None,
|
|
99
|
+
)
|
|
100
|
+
navigate_groups: list[dict[str, Any]] = settings.get("navigate_groups", [])
|
|
101
|
+
tab_color_map = _build_tab_color_map(navigate_groups) if navigate_groups else {}
|
|
102
|
+
|
|
103
|
+
# Filter out advanced sheets unless requested OR they have data
|
|
104
|
+
advanced_sheets: set[str] = set(settings.get("advanced_sheets", []))
|
|
105
|
+
if not include_advanced:
|
|
106
|
+
# Pre-build a set of (class, param) pairs that have data for fast lookup
|
|
107
|
+
_data_keys: set[tuple[str, str]] = set()
|
|
108
|
+
for (cls, _bn, pn, _alt) in db_contents.parameter_values:
|
|
109
|
+
_data_keys.add((cls, pn))
|
|
110
|
+
|
|
111
|
+
def _has_data(spec: SheetSpec) -> bool:
|
|
112
|
+
for cls in spec.entity_classes:
|
|
113
|
+
for pn in spec.parameter_names:
|
|
114
|
+
if (cls, pn) in _data_keys:
|
|
115
|
+
return True
|
|
116
|
+
return False
|
|
117
|
+
|
|
118
|
+
specs = [s for s in specs
|
|
119
|
+
if s.sheet_name not in advanced_sheets or _has_data(s)]
|
|
120
|
+
|
|
121
|
+
# Load info rows setting
|
|
122
|
+
_ew._info_rows = settings.get("info_rows", {})
|
|
123
|
+
|
|
124
|
+
# 3. Create the workbook
|
|
125
|
+
wb = Workbook()
|
|
126
|
+
|
|
127
|
+
# 4. Delete the default 'Sheet' worksheet
|
|
128
|
+
if "Sheet" in wb.sheetnames:
|
|
129
|
+
del wb["Sheet"]
|
|
130
|
+
|
|
131
|
+
# Track counts by layout for the summary
|
|
132
|
+
layout_counts: dict[str, int] = {}
|
|
133
|
+
|
|
134
|
+
# 5. Write each sheet
|
|
135
|
+
for spec in specs:
|
|
136
|
+
ws = wb.create_sheet(spec.sheet_name)
|
|
137
|
+
layout = spec.layout
|
|
138
|
+
|
|
139
|
+
if layout == "constant":
|
|
140
|
+
if use_new_format:
|
|
141
|
+
# Check if this is a pure-array sheet (model_solve_sequence etc.)
|
|
142
|
+
_is_array_sheet = (
|
|
143
|
+
len(spec.parameter_names) == 1
|
|
144
|
+
and _ew._get_param_data_type(
|
|
145
|
+
spec.parameter_names[0], spec.entity_classes,
|
|
146
|
+
db_contents, layout="constant",
|
|
147
|
+
) == "boolean (array)"
|
|
148
|
+
)
|
|
149
|
+
if _is_array_sheet:
|
|
150
|
+
write_array_transposed_sheet_v2(ws, spec, db_contents)
|
|
151
|
+
else:
|
|
152
|
+
write_constant_sheet_v2(ws, spec, db_contents)
|
|
153
|
+
else:
|
|
154
|
+
write_constant_sheet(ws, spec, db_contents)
|
|
155
|
+
elif layout == "periodic":
|
|
156
|
+
if use_new_format:
|
|
157
|
+
write_periodic_sheet_v2(ws, spec, db_contents)
|
|
158
|
+
else:
|
|
159
|
+
write_periodic_sheet(ws, spec, db_contents)
|
|
160
|
+
elif layout == "nested_periodic":
|
|
161
|
+
if use_new_format:
|
|
162
|
+
write_nested_periodic_sheet_v2(ws, spec, db_contents)
|
|
163
|
+
else:
|
|
164
|
+
write_nested_periodic_sheet(ws, spec, db_contents)
|
|
165
|
+
elif layout == "timeseries":
|
|
166
|
+
if use_new_format:
|
|
167
|
+
write_timeseries_sheet_v2(ws, spec, db_contents)
|
|
168
|
+
else:
|
|
169
|
+
write_timeseries_sheet(ws, spec, db_contents)
|
|
170
|
+
elif layout == "stochastic":
|
|
171
|
+
if use_new_format:
|
|
172
|
+
write_stochastic_sheet_v2(ws, spec, db_contents)
|
|
173
|
+
else:
|
|
174
|
+
# v1 has no stochastic writer — skip
|
|
175
|
+
del wb[spec.sheet_name]
|
|
176
|
+
continue
|
|
177
|
+
elif layout == "ladder":
|
|
178
|
+
if use_new_format:
|
|
179
|
+
write_ladder_sheet_v2(ws, spec, db_contents)
|
|
180
|
+
else:
|
|
181
|
+
# v1 has no ladder writer — skip
|
|
182
|
+
del wb[spec.sheet_name]
|
|
183
|
+
continue
|
|
184
|
+
elif layout == "link":
|
|
185
|
+
if use_new_format:
|
|
186
|
+
write_link_sheet_v2(ws, spec, db_contents)
|
|
187
|
+
else:
|
|
188
|
+
write_link_sheet(ws, spec, db_contents)
|
|
189
|
+
elif layout == "scenario":
|
|
190
|
+
write_scenario_sheet(ws, db_contents, include_stochastics=include_advanced)
|
|
191
|
+
elif layout == "version":
|
|
192
|
+
if use_new_format:
|
|
193
|
+
# In v2, version info is on the navigate sheet — skip separate sheet
|
|
194
|
+
del wb[spec.sheet_name]
|
|
195
|
+
continue
|
|
196
|
+
write_version_sheet(ws, db_contents.version)
|
|
197
|
+
elif layout == "navigate":
|
|
198
|
+
write_navigate_sheet(
|
|
199
|
+
ws, specs, navigate_groups=navigate_groups,
|
|
200
|
+
version=db_contents.version if use_new_format else None,
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
print(f" Warning: unknown layout '{layout}' for sheet '{spec.sheet_name}', skipping.")
|
|
204
|
+
continue
|
|
205
|
+
|
|
206
|
+
# Set tab color from navigate group config
|
|
207
|
+
if spec.sheet_name in tab_color_map:
|
|
208
|
+
ws.sheet_properties.tabColor = tab_color_map[spec.sheet_name]
|
|
209
|
+
|
|
210
|
+
layout_counts[layout] = layout_counts.get(layout, 0) + 1
|
|
211
|
+
|
|
212
|
+
# 6. Ensure the output directory exists and save
|
|
213
|
+
output_dir = Path(output_path).parent
|
|
214
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
215
|
+
|
|
216
|
+
wb.save(output_path)
|
|
217
|
+
|
|
218
|
+
# 7. Print summary
|
|
219
|
+
format_label = "v2 (self-describing)" if use_new_format else "v1 (original)"
|
|
220
|
+
total = len(specs)
|
|
221
|
+
print(f"\nExport complete: {output_path} [{format_label}]")
|
|
222
|
+
print(f" Total sheets: {total}")
|
|
223
|
+
for layout_type, count in sorted(layout_counts.items()):
|
|
224
|
+
print(f" {layout_type}: {count}")
|
|
225
|
+
if db_contents.version is not None:
|
|
226
|
+
version_display = int(db_contents.version) if db_contents.version == int(db_contents.version) else db_contents.version
|
|
227
|
+
print(f" DB version: {version_display}")
|