flextool 4.0.1__tar.gz → 4.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flextool-4.0.1 → flextool-4.0.4}/PKG-INFO +2 -2
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_derived_params.py +73 -6
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_solve_writers.py +7 -2
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_solve_context.py +54 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_stochastic.py +58 -13
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_timeline.py +85 -71
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/calc_storage_vre.py +22 -1
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/out_costs.py +12 -2
- {flextool-4.0.1 → flextool-4.0.4}/flextool/spinedb_backend/_axis_enums.py +44 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/self_update.py +3 -4
- {flextool-4.0.1 → flextool-4.0.4}/flextool.egg-info/PKG-INFO +2 -2
- {flextool-4.0.1 → flextool-4.0.4}/flextool.egg-info/requires.txt +1 -1
- {flextool-4.0.1 → flextool-4.0.4}/pyproject.toml +10 -5
- {flextool-4.0.1 → flextool-4.0.4}/LICENSE.txt +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/README.md +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/_mem_sampler.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/_resources.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/__main__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_cli.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_db_alt.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_final_outputs.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_guard.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_loop.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_readers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_report.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_sizing.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_solve.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/calibrate/_solve_status.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/_console.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/_timing.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_execute_flextool_workflow.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_export_to_tabular.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_import_sensitivities.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_migrate_database.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_open_results_db.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_read_matpower.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_read_old_flextool.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_read_self_describing_tabular_input.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_read_tabular_input.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_run_flextool.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_scenario_results.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_solve_mps.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_update_flextool.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/cli/cmd_write_outputs.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/common_utils/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/common_utils/plot_mem_shape.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/common_utils/precision.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/decomposition/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/decomposition/region_decomposition.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/decomposition/region_filter.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_axis_enums.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_benders.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_block_layout.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_blocks.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_commodity_ladder.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_cumulative_invest.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_db_loader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_db_reader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_dc_power_flow.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_delay.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_derived_arithmetic.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_derived_block.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_derived_branch.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_derived_existing.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_derived_npv.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_derived_profile.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_derived_walks.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_determinism.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_direct_params.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_dump_csvs.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_arc_unions.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_calc_params.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_chain_params.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_co2_accumulators.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_dispatchers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_energy_margin.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_energy_margin_adder.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_entity_annual.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_inflow_scaling.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_leaf_sets.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_lp_scaling.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_mid_sets.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_pdt_params.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_per_solve.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_period_calc.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_period_params.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_provider_io.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_reserve.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_emit_solve_time.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_flex_data_accumulator.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_flex_data_provider.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_group_slack.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_inmemory_reader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_input_source.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_invest_seeds.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_native_input_writer.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_native_run_model.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_orchestration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_output_writer.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_param_shapes.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_parquet_bundle.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_pdt_join.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_pdt_lookup.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_per_solve_sets.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_projection_params.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_provider_keys.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_provider_translators.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_recursive_solve.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_region_filter.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_reserve.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_solve_acceptance.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_solve_config.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_solve_handoff.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_solve_state.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_solver_base.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_solver_dispatch.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_spinedb_reader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_subprocess_solve.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_vectorize.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/_warm.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/autoscale/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/autoscale/_config.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/autoscale/_layer2.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/autoscale/_layer2_types.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/autoscale/_quantity_types.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/autoscale/_report.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/chain.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/input.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/engine_polars/model.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/env_check.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/export_to_tabular/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/export_to_tabular/db_reader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/export_to_tabular/excel_writer.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/export_to_tabular/export_settings.yaml +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/export_to_tabular/export_to_excel.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/export_to_tabular/formatting.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/export_to_tabular/sheet_config.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/__main__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/calibrate_commands.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/calibrate_jobs.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/check_tree.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/cli_format.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/config_parser.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/data_models.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/db_editor_integration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/db_version_check.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/add_dialog.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/calibrate_dialog.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/file_picker.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/group_picker.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/migration_consent_dialog.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/migration_progress_dialog.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/plot_dialog.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/plot_settings_picker.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/project_dialog.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/dialogs/update_dialog.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/downsampling.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/error_handling.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/execution_manager.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/execution_window.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/hover_tooltip.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/input_sources.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/main_window.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/network_graph.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/output_actions.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/output_log_window.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/platform_utils.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/plot_cache.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/plot_canvas.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/plot_config_reader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/project_utils.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/result_viewer.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/scenario_key.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/scenario_lists.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/settings_io.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/solve_reader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/tree_reorder.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/gui/ui_metrics.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/input_derivation/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/input_derivation/_commodity_ladder.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/input_derivation/_commodity_ladder_sets.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/input_derivation/_dc_power_flow.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/input_derivation/_method_constants.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/input_derivation/_process_method.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/input_derivation/_specs.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/input_derivation/_validators.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/lean_parquet.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/model_builder/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/model_builder/build_model.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/model_builder/encoding.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/model_builder/names.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/model_builder/profiles.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/axis_helpers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/color_template.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/config.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/format_helpers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/legend_helpers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/orchestrator.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/perf.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/plan.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/plot_bars.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/plot_bars_detail.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/plot_lines.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/shared_manifest.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/plot_outputs/subplot_helpers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_inputs/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_inputs/import_old_excel_input.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_inputs/read_matpower.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_inputs/read_old_flextool.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_inputs/read_self_describing_excel.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_inputs/read_tabular_with_specification.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_inputs/write_old_flextool_to_db.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_inputs/write_self_describing_to_db.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_inputs/write_to_input_db.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/_annualize.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/_inmemory_helpers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/_output_meta.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/calc_capacity_flows.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/calc_connections.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/calc_costs.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/calc_group_flows.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/calc_slacks.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/drop_levels.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/handoff_writers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/out_ancillary.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/out_capacity.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/out_flowgroup.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/out_flows.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/out_group.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/out_node.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/persist_realized_slice.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/process_results.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/read_highs_solution.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/read_parameters.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/read_sets.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/read_variables.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/solve_order.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/spinedb_replay.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/union_realized_slice.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/write_outputs.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/process_outputs/write_spinedb.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/representative_periods/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/representative_periods/clustering.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/representative_periods/force_include.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/representative_periods/netload.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/representative_periods/netload_inputs.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/representative_periods/netload_iterate.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/representative_periods/preprocess.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/representative_periods/scenario_stack.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/representative_periods/weights.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/config_builder.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/constants.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/data_models.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/db_reader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/dispatch_data.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/dispatch_mappings.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/dispatch_plots.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/input_entity_colors.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/orchestrator.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/plan_union.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/scenario_comparison/plot_settings_seed.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/AXIS_CONTRACT.md +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/howto_aggregate_output.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/howto_connections.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/howto_demand.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/howto_hydro_reservoir.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/howto_hydro_reservoir_with_pump.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/howto_non_sync_and_curtailment.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/howto_ramp_and_start_up.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/howto_stochastics.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/templates_examples.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/canonical_databases/templates_time_settings_only.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/comparison_settings_template.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/default_plot_settings.yaml +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/default_plots.yaml +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/flextool_axis_contract.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/flextool_axis_contract.schema.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/old_flextool_import_template.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/output_info_template.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/output_settings_template.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_constant_default.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_default_optional_output.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_default_value.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_drop_down.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_lifetime_method.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_optional_outputs.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_output_node_flows.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_results_master.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_rolling_start_remove.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_rolling_window.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_storage_binding_defaults.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_v2.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/pre_v26/flextool_template_v25.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/spinedb_results_schema.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/schemas/spinedb_schema.json +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/solver_config/copt.opt.template +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/solver_config/cplex.opt.template +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/solver_config/gurobi.opt.template +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/solver_config/highs.opt.template +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/solver_config/xpress.opt.template +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/spinedb_backend/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/spinedb_backend/_backend.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/__init__.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/canonical_databases.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/db_migration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/ensure_settings_db.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/export_database.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/extend_tests_fixture.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/generate_canonical.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/initialize_database.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/install_info.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/sync_master_json_template.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool/update_flextool/test_fixtures.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool.egg-info/SOURCES.txt +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool.egg-info/dependency_links.txt +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool.egg-info/entry_points.txt +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/flextool.egg-info/top_level.txt +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/setup.cfg +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_bar_layout.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_bar_sub_pixel_labels.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_blocks.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_branch_weights.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_color_template.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_commodity_ladder_rolling.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_comparison_excel.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_comparison_project_root.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_complete_period_share_reduction.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_connection_node_warnings.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_console_run_tool.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_cost_aggregation_semantics.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_cost_breakdown_by_entity.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_csv_dump_roundtrip.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_dc_power_flow.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_dispatch_consumer_and_connection_signs.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_dispatch_node_debug_and_tree.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_dispatch_order_specials.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_dispatch_scenario_tag.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_ensure_settings_db.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_entity_unitsize_invariant.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_entity_universe_carrier.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_env_check.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_export_to_tabular.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_extend_tests_fixture.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_flowgroup_indicators.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_force_include.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_group_output_warnings.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_handoff_writers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_input_entity_colors.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_lh2_three_region.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_min_load_rolling.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_non_anticipativity.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_old_flextool_importer_schema_drift.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_open_results_db.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_output_column_metadata.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_output_metadata_renderers.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_parameter_group_coverage.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_plan_union.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_plot_draw_order.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_plot_pipeline.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_plot_plan_chunk_reconstruction.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_plot_plan_time_labels_roundtrip.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_plot_settings_seed.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_precision.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_read_highs_solution.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_regional_filter.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_representative_periods.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_rp_alternative_name.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_rp_force_include_wiring.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_rp_solves_filter.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_scenario_colors.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_scenario_stack.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_scenarios.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_self_describing_reader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_shared_axis_manifest.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_simple_bar_entity_color.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_solve_config.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_solve_reader.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_synthesize_invest_dual.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_timings_csv.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_unidirectional_connection.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_unit_online_dt_columns.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_v52_migration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_v63_migration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_v64_migration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_v65_migration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_v66_migration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_v67_migration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_v68_migration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_v69_migration.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_workdir_empty_default.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_xlsx_workflow.py +0 -0
- {flextool-4.0.1 → flextool-4.0.4}/tests/test_years_represented.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flextool
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.4
|
|
4
4
|
Summary: IRENA FlexTool is an energy and power systems model for understanding the role of variable power generation in future energy systems.
|
|
5
5
|
Author: IRENA FlexTool contributors
|
|
6
6
|
License: © International Renewable Energy Agency 2018-2022
|
|
@@ -39,7 +39,7 @@ Requires-Python: >=3.11
|
|
|
39
39
|
Description-Content-Type: text/markdown
|
|
40
40
|
License-File: LICENSE.txt
|
|
41
41
|
Requires-Dist: polar-high>=3.7.0
|
|
42
|
-
Requires-Dist: highspy<=1.14.0
|
|
42
|
+
Requires-Dist: highspy<=1.14.0,>=1.14.0
|
|
43
43
|
Requires-Dist: polars>=1.40
|
|
44
44
|
Requires-Dist: pandas
|
|
45
45
|
Requires-Dist: spinedb-api
|
|
@@ -6572,12 +6572,23 @@ def dtttdt_from_source(source: "InputSource",
|
|
|
6572
6572
|
# period__branch.csv (workdir-aware) to discover which periods are
|
|
6573
6573
|
# branches off a non-self anchor.
|
|
6574
6574
|
branch_anchor: dict[str, str] = {}
|
|
6575
|
+
anchors_with_self: set[str] = set()
|
|
6575
6576
|
pbf: pl.DataFrame | None = None
|
|
6576
6577
|
period_col, branch_col = "period", "branch"
|
|
6577
6578
|
# Path B Cat B: prefer ctx.period_branch (schema [d_anchor, b]).
|
|
6579
|
+
# Null-guard: the accessor casts against the live axis enums, and
|
|
6580
|
+
# the ``d_anchor`` enum vocabulary is empty by contract ("built at
|
|
6581
|
+
# Phase 3 handoff") — under enum activation every anchor value
|
|
6582
|
+
# casts to null, which would poison the anchors_with_self /
|
|
6583
|
+
# branch_anchor classification below (a null==null self-row turns
|
|
6584
|
+
# EVERY period into a "branch"). Only trust the ctx frame when
|
|
6585
|
+
# both columns are fully populated; otherwise fall through to the
|
|
6586
|
+
# provider/workdir CSV (raw Utf8 columns).
|
|
6578
6587
|
if ctx is not None:
|
|
6579
6588
|
pb_ctx = ctx.period_branch
|
|
6580
|
-
if pb_ctx.height > 0
|
|
6589
|
+
if (pb_ctx.height > 0
|
|
6590
|
+
and pb_ctx["d_anchor"].null_count() == 0
|
|
6591
|
+
and pb_ctx["b"].null_count() == 0):
|
|
6581
6592
|
pbf = pb_ctx
|
|
6582
6593
|
period_col, branch_col = "d_anchor", "b"
|
|
6583
6594
|
if pbf is None and workdir is not None:
|
|
@@ -6590,13 +6601,52 @@ def dtttdt_from_source(source: "InputSource",
|
|
|
6590
6601
|
if pbf is not None and pbf.height > 0:
|
|
6591
6602
|
pb_rows = list(zip(pbf[period_col].to_list(),
|
|
6592
6603
|
pbf[branch_col].to_list()))
|
|
6593
|
-
anchors_with_self: set[str] = set()
|
|
6594
6604
|
for a, b in pb_rows:
|
|
6595
6605
|
if a == b:
|
|
6596
6606
|
anchors_with_self.add(a)
|
|
6597
6607
|
for a, b in pb_rows:
|
|
6598
6608
|
if b != a and a in anchors_with_self:
|
|
6599
6609
|
branch_anchor[b] = a
|
|
6610
|
+
# Continuation fan-out: the time-branch map (period -> time_branch,
|
|
6611
|
+
# from solve_branch__time_branch.csv) lets a continuation branch
|
|
6612
|
+
# period wrap to the nearest earlier period of the SAME time-branch
|
|
6613
|
+
# instead of self-wrapping, mirroring ``make_step_jump``. Ctx-first
|
|
6614
|
+
# (typed accessor), provider/workdir fallback.
|
|
6615
|
+
time_branch_of: dict[str, str] = {}
|
|
6616
|
+
if ctx is not None:
|
|
6617
|
+
sbtb_ctx = ctx.solve_branch_time_branch
|
|
6618
|
+
if sbtb_ctx.height > 0:
|
|
6619
|
+
time_branch_of = dict(zip(sbtb_ctx["d"].to_list(),
|
|
6620
|
+
sbtb_ctx["time_branch"].to_list()))
|
|
6621
|
+
if not time_branch_of and workdir is not None:
|
|
6622
|
+
sbtb_path = (Path(workdir) / "solve_data"
|
|
6623
|
+
/ "solve_branch__time_branch.csv")
|
|
6624
|
+
if _provider_has_key(provider, sbtb_path): # Phase E-j — seed-aware
|
|
6625
|
+
try:
|
|
6626
|
+
sbtb_df = _provider_read(provider, sbtb_path)
|
|
6627
|
+
except Exception:
|
|
6628
|
+
sbtb_df = None
|
|
6629
|
+
if (sbtb_df is not None and sbtb_df.height > 0
|
|
6630
|
+
and "period" in sbtb_df.columns
|
|
6631
|
+
and "branch" in sbtb_df.columns):
|
|
6632
|
+
time_branch_of = dict(zip(sbtb_df["period"].to_list(),
|
|
6633
|
+
sbtb_df["branch"].to_list()))
|
|
6634
|
+
if branch_anchor and not time_branch_of:
|
|
6635
|
+
# Failure semantics (do NOT fall back to self-wrap): a
|
|
6636
|
+
# period__branch with branch rows but no time-branch map means
|
|
6637
|
+
# the per-solve preprocessing artefacts are inconsistent. A
|
|
6638
|
+
# silent self-wrap fallback would sever continuation branch
|
|
6639
|
+
# periods from their own branch chain — the exact silent
|
|
6640
|
+
# truncation bug this walk repairs — so raise instead.
|
|
6641
|
+
raise ValueError(
|
|
6642
|
+
"dtttdt_from_source: period__branch declares branch periods "
|
|
6643
|
+
f"({sorted(branch_anchor)}) but solve_branch__time_branch is "
|
|
6644
|
+
"missing or empty — cannot resolve cross-period wrap "
|
|
6645
|
+
"predecessors for branch periods. The per-solve emitter "
|
|
6646
|
+
"(emit_branch_weights_and_map) writes this frame for every "
|
|
6647
|
+
"solve; its absence indicates an inconsistent workdir/"
|
|
6648
|
+
"Provider state."
|
|
6649
|
+
)
|
|
6600
6650
|
out_rows: list[tuple[str, str, str, str, str, str]] = []
|
|
6601
6651
|
len(period_order)
|
|
6602
6652
|
# Find within-solve predecessor for first-of-period rows.
|
|
@@ -6606,19 +6656,36 @@ def dtttdt_from_source(source: "InputSource",
|
|
|
6606
6656
|
continue
|
|
6607
6657
|
# Determine the predecessor period for the j=0 wrap row.
|
|
6608
6658
|
# Mirrors ``timeline_config.make_step_jump``:
|
|
6609
|
-
# * branch (non-anchor) period →
|
|
6659
|
+
# * branch (non-anchor) period → nearest earlier period with
|
|
6660
|
+
# the SAME time-branch (continuation linkage); self-wrap
|
|
6661
|
+
# when none exists (the branching period's own fan);
|
|
6610
6662
|
# * first anchor period (== first_period_name in flextool's
|
|
6611
6663
|
# reversed walk) → wrap to last_period_name (last in
|
|
6612
|
-
# active_time_list iteration order, == period_order[-1]
|
|
6613
|
-
#
|
|
6614
|
-
#
|
|
6664
|
+
# active_time_list iteration order, == period_order[-1] —
|
|
6665
|
+
# retained quirk);
|
|
6666
|
+
# * non-first anchor period → nearest earlier ANCHOR period
|
|
6667
|
+
# (skips branch periods so a realized continuation period
|
|
6668
|
+
# wraps to the previous realized period); positional
|
|
6669
|
+
# ``period_order[pi - 1]`` fallback when there is no
|
|
6670
|
+
# period__branch data (deterministic path, where
|
|
6671
|
+
# positional ≡ previous-anchor anyway).
|
|
6615
6672
|
if period in branch_anchor:
|
|
6616
6673
|
wrap_period = period
|
|
6674
|
+
tb = time_branch_of.get(period)
|
|
6675
|
+
if tb is not None:
|
|
6676
|
+
for qi in range(pi - 1, -1, -1):
|
|
6677
|
+
if time_branch_of.get(period_order[qi]) == tb:
|
|
6678
|
+
wrap_period = period_order[qi]
|
|
6679
|
+
break
|
|
6617
6680
|
else:
|
|
6618
6681
|
if pi == 0:
|
|
6619
6682
|
wrap_period = period_order[-1]
|
|
6620
6683
|
else:
|
|
6621
6684
|
wrap_period = period_order[pi - 1]
|
|
6685
|
+
for qi in range(pi - 1, -1, -1):
|
|
6686
|
+
if period_order[qi] in anchors_with_self:
|
|
6687
|
+
wrap_period = period_order[qi]
|
|
6688
|
+
break
|
|
6622
6689
|
prev_solve_steps = per_period.get(wrap_period) or []
|
|
6623
6690
|
prev_solve_last_t = prev_solve_steps[-1][0] if prev_solve_steps \
|
|
6624
6691
|
else steps[-1][0]
|
|
@@ -241,7 +241,10 @@ def derive_years_represented(
|
|
|
241
241
|
str(years_to_cover_within_year),
|
|
242
242
|
))
|
|
243
243
|
for pd_pair in period__branch:
|
|
244
|
-
|
|
244
|
+
# Equality, not substring containment (``in``): with
|
|
245
|
+
# prefix-overlapping period names (p1 / p10), ``in``
|
|
246
|
+
# would copy p10's year rows to p1's branches.
|
|
247
|
+
if pd_pair[0] == period__years[0] and pd_pair[0] != pd_pair[1]:
|
|
245
248
|
rows.append((
|
|
246
249
|
pd_pair[1], str(year_count), str(year_count),
|
|
247
250
|
str(years_to_cover_within_year),
|
|
@@ -278,7 +281,9 @@ def derive_period_years(
|
|
|
278
281
|
for period__year in years_represented:
|
|
279
282
|
rows.append((period__year[0], str(year_count)))
|
|
280
283
|
for pd_pair in stochastic_branches:
|
|
281
|
-
|
|
284
|
+
# Equality, not substring containment — see
|
|
285
|
+
# derive_years_represented.
|
|
286
|
+
if pd_pair[0] == period__year[0] and pd_pair[0] != pd_pair[1]:
|
|
282
287
|
rows.append((pd_pair[1], str(year_count)))
|
|
283
288
|
year_count += float(period__year[1])
|
|
284
289
|
return _to_utf8_frame(("period", "param"), rows)
|
|
@@ -192,6 +192,13 @@ class SolveContext:
|
|
|
192
192
|
),
|
|
193
193
|
repr=False,
|
|
194
194
|
)
|
|
195
|
+
_solve_branch_time_branch_loaded: bool = field(default=False, repr=False)
|
|
196
|
+
_solve_branch_time_branch: pl.DataFrame = field(
|
|
197
|
+
default_factory=lambda: pl.DataFrame(
|
|
198
|
+
schema={"d": pl.Utf8, "time_branch": pl.Utf8}
|
|
199
|
+
),
|
|
200
|
+
repr=False,
|
|
201
|
+
)
|
|
195
202
|
|
|
196
203
|
# Internal: ad-hoc CSV cache. Keyed by ``str(path)`` (no syscall).
|
|
197
204
|
# Values are ``None`` when the file is absent so we don't re-stat
|
|
@@ -452,6 +459,27 @@ class SolveContext:
|
|
|
452
459
|
self._solve_branch_weight_loaded = True
|
|
453
460
|
return self._solve_branch_weight
|
|
454
461
|
|
|
462
|
+
@property
|
|
463
|
+
def solve_branch_time_branch(self) -> pl.DataFrame:
|
|
464
|
+
"""``[d, time_branch]`` from ``solve_branch__time_branch.csv``
|
|
465
|
+
(rename ``period`` → ``d``, ``branch`` → ``time_branch``).
|
|
466
|
+
Empty when the file is missing.
|
|
467
|
+
|
|
468
|
+
Continuation fan-out: ``dtttdt_from_source`` walks this map so
|
|
469
|
+
a continuation branch period wraps to the nearest earlier
|
|
470
|
+
period sharing the same time-branch instead of self-wrapping.
|
|
471
|
+
Columns stay ``Utf8`` — the ``time_branch`` vocabulary (DB
|
|
472
|
+
branch names like ``rlz`` / ``low``) is not a canonical axis
|
|
473
|
+
enum, and consumers use the frame as a plain dict.
|
|
474
|
+
"""
|
|
475
|
+
if not self._solve_branch_time_branch_loaded:
|
|
476
|
+
self._solve_branch_time_branch = _load_solve_branch_time_branch(
|
|
477
|
+
self.solve_data_dir / "solve_branch__time_branch.csv",
|
|
478
|
+
provider=self.provider,
|
|
479
|
+
)
|
|
480
|
+
self._solve_branch_time_branch_loaded = True
|
|
481
|
+
return self._solve_branch_time_branch
|
|
482
|
+
|
|
455
483
|
# ------------------------------------------------------------------
|
|
456
484
|
# Typed-field convenience accessors
|
|
457
485
|
# ------------------------------------------------------------------
|
|
@@ -856,6 +884,32 @@ def _load_period_share(solve_data_dir: Path,
|
|
|
856
884
|
return empty
|
|
857
885
|
|
|
858
886
|
|
|
887
|
+
def _load_solve_branch_time_branch(path: Path,
|
|
888
|
+
*, provider: "object | None" = None,
|
|
889
|
+
) -> pl.DataFrame:
|
|
890
|
+
"""Load ``solve_branch__time_branch.csv`` as ``[d, time_branch]``.
|
|
891
|
+
|
|
892
|
+
Plain ``Utf8`` columns — the time-branch vocabulary is not a
|
|
893
|
+
canonical axis enum (see :pyattr:`SolveContext.solve_branch_time_branch`).
|
|
894
|
+
"""
|
|
895
|
+
empty = pl.DataFrame(schema={"d": pl.Utf8, "time_branch": pl.Utf8})
|
|
896
|
+
df = _read_frame(path, provider=provider,
|
|
897
|
+
consumer="SolveContext.solve_branch_time_branch")
|
|
898
|
+
if df is None:
|
|
899
|
+
return empty
|
|
900
|
+
rename = {}
|
|
901
|
+
if "period" in df.columns:
|
|
902
|
+
rename["period"] = "d"
|
|
903
|
+
if "branch" in df.columns:
|
|
904
|
+
rename["branch"] = "time_branch"
|
|
905
|
+
df = df.rename(rename)
|
|
906
|
+
cols = [c for c in ("d", "time_branch") if c in df.columns]
|
|
907
|
+
if len(cols) < 2:
|
|
908
|
+
return empty
|
|
909
|
+
return (df.select(pl.col(c).cast(pl.Utf8) for c in cols)
|
|
910
|
+
.unique(maintain_order=True))
|
|
911
|
+
|
|
912
|
+
|
|
859
913
|
def _load_solve_branch_weight(path: Path,
|
|
860
914
|
*, provider: "object | None" = None) -> pl.DataFrame:
|
|
861
915
|
"""Load ``solve_branch_weight.csv`` and rename to canonical
|
|
@@ -30,10 +30,15 @@ Behavioural quirks
|
|
|
30
30
|
``branch != period`` AND the realized flag is not ``"yes"``. Subtle
|
|
31
31
|
three-way condition.
|
|
32
32
|
|
|
33
|
-
* **Continuation-after-branching**:
|
|
34
|
-
period
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
* **Continuation-after-branching**: every period after the branching
|
|
34
|
+
period continues the fan. The realized branch keeps the REAL period
|
|
35
|
+
name (with its active / realized / fix-storage windows); each
|
|
36
|
+
non-realized nonzero-weight branch gets a synthetic
|
|
37
|
+
``period + "_" + branch`` copy of the period's active time (LP
|
|
38
|
+
variables, never committed). The naming is "sticky" across periods.
|
|
39
|
+
``period__branch`` mirrors the branching period's metadata rows
|
|
40
|
+
(``(period, period)`` self-row plus one row per fan member,
|
|
41
|
+
realized and zero-weight included).
|
|
37
42
|
|
|
38
43
|
* **Single realized branch per period**: each period must have
|
|
39
44
|
exactly one ``realized: yes`` row (or zero before any branching has
|
|
@@ -378,6 +383,17 @@ class StochasticSolver:
|
|
|
378
383
|
but NOT to ``realized_time_lists`` / ``fix_storage_time_lists``;
|
|
379
384
|
branches are future scenarios that aren't committed.
|
|
380
385
|
|
|
386
|
+
After the branching period, the fan extends through every
|
|
387
|
+
remaining period of the solve: the realized branch keeps the
|
|
388
|
+
REAL period names (and their realized / fix-storage windows),
|
|
389
|
+
while each non-realized nonzero-weight branch ``b`` gets one
|
|
390
|
+
synthetic ``period + "_" + b`` period per continuation period
|
|
391
|
+
carrying a full copy of that period's active time.
|
|
392
|
+
``period__branch`` mirrors the branching period's metadata rows
|
|
393
|
+
(self-row plus one row per fan member including the realized
|
|
394
|
+
and zero-weight branches); ``solve_branch__time_branch`` gains
|
|
395
|
+
rows only for branches that received active time.
|
|
396
|
+
|
|
381
397
|
IMPORTANT: branches do NOT enter ``invest_periods`` either.
|
|
382
398
|
Per ``audit/a6_b_dim_alternative.md`` (R-O6), ``v_invest`` is
|
|
383
399
|
realized-only — recourse investment is a future capability.
|
|
@@ -418,6 +434,10 @@ class StochasticSolver:
|
|
|
418
434
|
|
|
419
435
|
branched = False
|
|
420
436
|
branches: list[str] = []
|
|
437
|
+
# Branches that received active time at the branching period
|
|
438
|
+
# (nonzero weight, != period, not realized). Only these are
|
|
439
|
+
# extended with active time into continuation periods.
|
|
440
|
+
active_branches: list[str] = []
|
|
421
441
|
branch_start_time_lists[solve] = None
|
|
422
442
|
|
|
423
443
|
# First step of the solve (used for validation below).
|
|
@@ -446,9 +466,14 @@ class StochasticSolver:
|
|
|
446
466
|
raise FlexToolConfigError(message)
|
|
447
467
|
|
|
448
468
|
# Walk periods — at the first branch trigger, fan out into
|
|
449
|
-
# branches; subsequent periods continue the same branches
|
|
450
|
-
#
|
|
451
|
-
#
|
|
469
|
+
# branches; subsequent periods continue the same branches.
|
|
470
|
+
# NB: the reference implementation's continuation block
|
|
471
|
+
# (flextoolrunner.py:1745-1752) was dead code — it never
|
|
472
|
+
# wrote active/realized/fix-storage time for continuation
|
|
473
|
+
# periods, silently truncating the horizon. This module
|
|
474
|
+
# intentionally diverges by actually emitting the
|
|
475
|
+
# continuation timelines (realized branch keeps real period
|
|
476
|
+
# names; active branches get synthetic copies).
|
|
452
477
|
for period, active_time in active_time_list.items():
|
|
453
478
|
if not branched:
|
|
454
479
|
period__branch_lists[solve].append((period, period))
|
|
@@ -491,6 +516,7 @@ class StochasticSolver:
|
|
|
491
516
|
and branch != period
|
|
492
517
|
and branch__weight__real[2] != "yes"
|
|
493
518
|
):
|
|
519
|
+
active_branches.append(branch)
|
|
494
520
|
new_active_time_list[solve_branch] = active_time[0:]
|
|
495
521
|
solve_branch__time_branch_lists[solve].append(
|
|
496
522
|
(solve_branch, branch)
|
|
@@ -506,15 +532,34 @@ class StochasticSolver:
|
|
|
506
532
|
)
|
|
507
533
|
break
|
|
508
534
|
else:
|
|
509
|
-
# Continuation-after-branching:
|
|
510
|
-
#
|
|
511
|
-
#
|
|
535
|
+
# Continuation-after-branching: the realized branch
|
|
536
|
+
# keeps the REAL period name; each active branch is
|
|
537
|
+
# extended into this period as a synthetic
|
|
538
|
+
# '<period>_<branch>' copy. Append order mirrors
|
|
539
|
+
# the branching period: the (period, period)
|
|
540
|
+
# self-row first, then fan rows in DB order. The
|
|
541
|
+
# self-row is load-bearing for the branch-weight
|
|
542
|
+
# normalization (the realized member of each cohort
|
|
543
|
+
# is detected via its (d, d) row) — see
|
|
544
|
+
# derive_solve_branch_weight.
|
|
545
|
+
period__branch_lists[solve].append((period, period))
|
|
546
|
+
new_active_time_list[period] = active_time_list[period]
|
|
547
|
+
if period in realized_time_list:
|
|
548
|
+
new_realized_time_list[period] = realized_time_list[period]
|
|
549
|
+
if period in fix_storage_time_list:
|
|
550
|
+
new_fix_storage_time_list[period] = (
|
|
551
|
+
fix_storage_time_list[period]
|
|
552
|
+
)
|
|
512
553
|
for branch in branches:
|
|
513
554
|
solve_branch = period + "_" + branch
|
|
514
555
|
period__branch_lists[solve].append((period, solve_branch))
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
556
|
+
if branch in active_branches:
|
|
557
|
+
new_active_time_list[solve_branch] = (
|
|
558
|
+
active_time_list[period][0:]
|
|
559
|
+
)
|
|
560
|
+
solve_branch__time_branch_lists[solve].append(
|
|
561
|
+
(solve_branch, branch)
|
|
562
|
+
)
|
|
518
563
|
for i in active_time_list[period]:
|
|
519
564
|
self.state.timeline.stochastic_timesteps[solve].append(
|
|
520
565
|
(solve_branch, i.timestep)
|
|
@@ -561,9 +561,18 @@ class TimelineConfig:
|
|
|
561
561
|
solve_config.realized_periods[solve]
|
|
562
562
|
+ solve_config.invest_periods[solve]
|
|
563
563
|
)
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
564
|
+
# Ordered de-dup (first occurrence) — a set here would make
|
|
565
|
+
# the seeded row order vary with PYTHONHASHSEED.
|
|
566
|
+
all_periods = list(
|
|
567
|
+
dict.fromkeys(
|
|
568
|
+
item for tup in all_periods_tuples for item in tup
|
|
569
|
+
)
|
|
570
|
+
)
|
|
571
|
+
# Membership check OUTSIDE the loop: the first append below
|
|
572
|
+
# inserts the solve key into the defaultdict, so an in-loop
|
|
573
|
+
# check would stop after seeding a single period.
|
|
574
|
+
if solve not in solve_config.solve_period_years_represented:
|
|
575
|
+
for period in all_periods:
|
|
567
576
|
solve_config.solve_period_years_represented[solve].append(
|
|
568
577
|
[period, 1.0]
|
|
569
578
|
)
|
|
@@ -1097,14 +1106,29 @@ def make_step_jump(
|
|
|
1097
1106
|
"""Build a list of step-jump entries for the solver.
|
|
1098
1107
|
|
|
1099
1108
|
Each entry describes the jump from one simulation step to the
|
|
1100
|
-
next, including cross-period jumps.
|
|
1101
|
-
|
|
1109
|
+
next, including cross-period jumps.
|
|
1110
|
+
|
|
1111
|
+
Cross-period linkage for the first step of a period:
|
|
1112
|
+
|
|
1113
|
+
* **anchor periods** (``(period, period) ∈ period__branch``): the
|
|
1114
|
+
nearest previous ANCHOR key in ``active_time_list`` order (skips
|
|
1115
|
+
branch periods, so a realized continuation period links to the
|
|
1116
|
+
previous realized period). The solve's FIRST period keeps the
|
|
1117
|
+
cyclic wrap to the last dict key (a branch period in stochastic
|
|
1118
|
+
solves — retained quirk, consumed only by cyclic storage-binding
|
|
1119
|
+
paths).
|
|
1120
|
+
* **branch periods**: the nearest earlier period sharing the same
|
|
1121
|
+
time-branch in ``solve_branch__time_branch_list`` (a continuation
|
|
1122
|
+
branch period continues from the same branch's previous period);
|
|
1123
|
+
when no such period exists (the branching period's own fan) the
|
|
1124
|
+
period self-cycles on its own last step.
|
|
1102
1125
|
"""
|
|
1103
1126
|
step_lengths: list[tuple] = []
|
|
1104
1127
|
period_start_pos = 0
|
|
1105
1128
|
period_counter = -1
|
|
1106
|
-
|
|
1107
|
-
|
|
1129
|
+
period_names = list(active_time_list)
|
|
1130
|
+
first_period_name = period_names[0]
|
|
1131
|
+
last_period_name = period_names[-1]
|
|
1108
1132
|
for period, active_time in reversed(active_time_list.items()):
|
|
1109
1133
|
period_counter -= 1
|
|
1110
1134
|
period_last = len(active_time)
|
|
@@ -1112,7 +1136,18 @@ def make_step_jump(
|
|
|
1112
1136
|
if period == first_period_name:
|
|
1113
1137
|
previous_period_name = last_period_name
|
|
1114
1138
|
else:
|
|
1115
|
-
previous_period_name =
|
|
1139
|
+
previous_period_name = period_names[period_counter]
|
|
1140
|
+
if (period, period) in period__branch:
|
|
1141
|
+
# Anchor period: skip branch periods — link to the
|
|
1142
|
+
# nearest previous key that is itself an anchor.
|
|
1143
|
+
# Deterministic solves (every key an anchor) reduce to
|
|
1144
|
+
# the positional previous; when no earlier anchor
|
|
1145
|
+
# exists the positional previous is kept as fallback.
|
|
1146
|
+
pos = period_names.index(period)
|
|
1147
|
+
for k in range(pos - 1, -1, -1):
|
|
1148
|
+
if (period_names[k], period_names[k]) in period__branch:
|
|
1149
|
+
previous_period_name = period_names[k]
|
|
1150
|
+
break
|
|
1116
1151
|
for i, step in enumerate(reversed(active_time)):
|
|
1117
1152
|
j = period_last - i - 1
|
|
1118
1153
|
if j > 0:
|
|
@@ -1146,18 +1181,45 @@ def make_step_jump(
|
|
|
1146
1181
|
)
|
|
1147
1182
|
else:
|
|
1148
1183
|
if (period, period) not in period__branch:
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1184
|
+
# Branch period: find the nearest EARLIER period
|
|
1185
|
+
# sharing this period's time-branch — the
|
|
1186
|
+
# continuation linkage (a continuation branch
|
|
1187
|
+
# period continues from the same branch's previous
|
|
1188
|
+
# period). When none exists (the branching
|
|
1189
|
+
# period's own fan: earlier periods carry only the
|
|
1190
|
+
# realized time-branch), fall back to the
|
|
1191
|
+
# self-cycle row on the period's own last step.
|
|
1192
|
+
time_branch = None
|
|
1193
|
+
for sb_tb in solve_branch__time_branch_list:
|
|
1194
|
+
if sb_tb[0] == period:
|
|
1195
|
+
time_branch = sb_tb[1]
|
|
1196
|
+
past = False
|
|
1197
|
+
found = False
|
|
1198
|
+
previous_period_with_branch = None
|
|
1199
|
+
for solve_period, _a_t in reversed(
|
|
1200
|
+
active_time_list.items()
|
|
1158
1201
|
):
|
|
1202
|
+
if past:
|
|
1203
|
+
for sb_tb in solve_branch__time_branch_list:
|
|
1204
|
+
if (
|
|
1205
|
+
sb_tb[0] == solve_period
|
|
1206
|
+
and sb_tb[1] == time_branch
|
|
1207
|
+
):
|
|
1208
|
+
previous_period_with_branch = (
|
|
1209
|
+
solve_period
|
|
1210
|
+
)
|
|
1211
|
+
found = True
|
|
1212
|
+
if found:
|
|
1213
|
+
break
|
|
1214
|
+
else:
|
|
1215
|
+
if solve_period == period:
|
|
1216
|
+
past = True
|
|
1217
|
+
if found:
|
|
1159
1218
|
jump = (
|
|
1160
|
-
active_time[j].index
|
|
1219
|
+
active_time[j].index
|
|
1220
|
+
- active_time_list[
|
|
1221
|
+
previous_period_with_branch
|
|
1222
|
+
][-1].index
|
|
1161
1223
|
)
|
|
1162
1224
|
step_lengths.insert(
|
|
1163
1225
|
period_start_pos,
|
|
@@ -1166,16 +1228,14 @@ def make_step_jump(
|
|
|
1166
1228
|
step.timestep,
|
|
1167
1229
|
active_time[j - 1].timestep,
|
|
1168
1230
|
active_time[block_last].timestep,
|
|
1169
|
-
|
|
1170
|
-
active_time_list[
|
|
1231
|
+
previous_period_with_branch,
|
|
1232
|
+
active_time_list[
|
|
1233
|
+
previous_period_with_branch
|
|
1234
|
+
][-1].timestep,
|
|
1171
1235
|
jump,
|
|
1172
1236
|
),
|
|
1173
1237
|
)
|
|
1174
|
-
|
|
1175
|
-
original_period is not None
|
|
1176
|
-
and (original_period, original_period)
|
|
1177
|
-
in period__branch
|
|
1178
|
-
):
|
|
1238
|
+
else:
|
|
1179
1239
|
jump = (
|
|
1180
1240
|
active_time[j].index - active_time[-1].index
|
|
1181
1241
|
)
|
|
@@ -1191,52 +1251,6 @@ def make_step_jump(
|
|
|
1191
1251
|
jump,
|
|
1192
1252
|
),
|
|
1193
1253
|
)
|
|
1194
|
-
else:
|
|
1195
|
-
time_branch = None
|
|
1196
|
-
for sb_tb in solve_branch__time_branch_list:
|
|
1197
|
-
if sb_tb[0] == period:
|
|
1198
|
-
time_branch = sb_tb[1]
|
|
1199
|
-
past = False
|
|
1200
|
-
found = False
|
|
1201
|
-
previous_period_with_branch = None
|
|
1202
|
-
for solve_period, _a_t in reversed(
|
|
1203
|
-
active_time_list.items()
|
|
1204
|
-
):
|
|
1205
|
-
if past:
|
|
1206
|
-
for sb_tb in solve_branch__time_branch_list:
|
|
1207
|
-
if (
|
|
1208
|
-
sb_tb[0] == solve_period
|
|
1209
|
-
and sb_tb[1] == time_branch
|
|
1210
|
-
):
|
|
1211
|
-
previous_period_with_branch = (
|
|
1212
|
-
solve_period
|
|
1213
|
-
)
|
|
1214
|
-
found = True
|
|
1215
|
-
if found:
|
|
1216
|
-
break
|
|
1217
|
-
else:
|
|
1218
|
-
if solve_period == period:
|
|
1219
|
-
past = True
|
|
1220
|
-
jump = (
|
|
1221
|
-
active_time[j].index
|
|
1222
|
-
- active_time_list[
|
|
1223
|
-
previous_period_with_branch
|
|
1224
|
-
][-1].index
|
|
1225
|
-
)
|
|
1226
|
-
step_lengths.insert(
|
|
1227
|
-
period_start_pos,
|
|
1228
|
-
(
|
|
1229
|
-
period,
|
|
1230
|
-
step.timestep,
|
|
1231
|
-
active_time[j - 1].timestep,
|
|
1232
|
-
active_time[block_last].timestep,
|
|
1233
|
-
previous_period_with_branch,
|
|
1234
|
-
active_time_list[
|
|
1235
|
-
previous_period_with_branch
|
|
1236
|
-
][-1].timestep,
|
|
1237
|
-
jump,
|
|
1238
|
-
),
|
|
1239
|
-
)
|
|
1240
1254
|
else:
|
|
1241
1255
|
jump = (
|
|
1242
1256
|
active_time[j].index
|
|
@@ -126,12 +126,33 @@ def compute_storage_and_vre(par, s, v, r) -> None:
|
|
|
126
126
|
profile_level_of_vre_node_profile_upper = vre_node_profile_upper.get_level_values('profile')
|
|
127
127
|
vre_processes_in_use = vre_profiles_in_use[profile_level_of_vre_node_profile_upper]
|
|
128
128
|
vre_processes_in_use.columns = vre_node_profile_upper
|
|
129
|
+
# par.process_availability only carries processes that authored
|
|
130
|
+
# unit/connection.availability. A profile-driven VRE process typically
|
|
131
|
+
# leaves the availability *parameter* at its 1.0 default and is therefore
|
|
132
|
+
# absent from the frame. A plain ``.mul`` aligns on the shared 'process'
|
|
133
|
+
# level and drops those columns to NaN, silently zeroing VRE potential /
|
|
134
|
+
# curtailment. The engine applies availability=1.0 to every unauthored
|
|
135
|
+
# process (model.py: left-join + fill_null(1.0)); reproduce that here by
|
|
136
|
+
# densifying availability to 1.0 over the VRE process set before the mul.
|
|
137
|
+
vre_procs = vre_processes_in_use.columns.get_level_values('process').unique()
|
|
138
|
+
availability_dt = par.process_availability.reindex(columns=vre_procs).fillna(1.0)
|
|
129
139
|
r.potentialVREgen_dt = (
|
|
130
140
|
vre_processes_in_use
|
|
131
|
-
.mul(
|
|
141
|
+
.mul(availability_dt, level='process')
|
|
132
142
|
.mul(r.entity_all_capacity, axis=1, level=0)
|
|
133
143
|
.droplevel(axis=1, level=['profile', 'profile_method'])
|
|
134
144
|
)
|
|
145
|
+
# A (process, node) flow may carry more than one ``upper_limit`` profile
|
|
146
|
+
# (e.g. an availability profile plus a scheduling upper bound under a
|
|
147
|
+
# ``free_schedule`` alternative). The engine enforces flow ≤ profile ×
|
|
148
|
+
# capacity for each, so the achievable potential is the tightest (min)
|
|
149
|
+
# cap; collapsing to the min also keeps curtailment = potential − actual
|
|
150
|
+
# ≥ 0. Only rewrite when duplicates exist so single-profile models stay
|
|
151
|
+
# byte-identical (min-collapse preserves first-seen column order).
|
|
152
|
+
if r.potentialVREgen_dt.columns.duplicated().any():
|
|
153
|
+
r.potentialVREgen_dt = (
|
|
154
|
+
r.potentialVREgen_dt.T.groupby(level=['process', 'node'], sort=False).min().T
|
|
155
|
+
)
|
|
135
156
|
# potentialVREgen_dt is MW (profile × availability × capacity); multiply
|
|
136
157
|
# by step_duration to get MWh per step before summing to MWh per period.
|
|
137
158
|
r.potentialVREgen_d = (
|
|
@@ -41,8 +41,18 @@ def cost_summaries(par, s, v, r, debug):
|
|
|
41
41
|
costs_dt['co2'] = r.cost_co2_dt
|
|
42
42
|
costs_dt['other operational'] = r.cost_process_other_operational_cost_dt.sum(axis=1)
|
|
43
43
|
costs_dt['starts'] = r.cost_startup_dt.sum(axis=1)
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
# With penalty_method=off on every node, v.q_state_up/down have no
|
|
45
|
+
# columns, so costPenalty_node_state_upDown_dt is empty and the 'up'/
|
|
46
|
+
# 'down' keys never attach to the upDown level (KeyError on .xs). Guard
|
|
47
|
+
# like the reserve slack terms below and the per-entity path (line ~265).
|
|
48
|
+
try:
|
|
49
|
+
costs_dt['upward slack penalty'] = r.costPenalty_node_state_upDown_dt.xs('up', level='upDown', axis=1).sum(axis=1)
|
|
50
|
+
except KeyError:
|
|
51
|
+
costs_dt['upward slack penalty'] = 0
|
|
52
|
+
try:
|
|
53
|
+
costs_dt['downward slack penalty'] = r.costPenalty_node_state_upDown_dt.xs('down', level='upDown', axis=1).sum(axis=1)
|
|
54
|
+
except KeyError:
|
|
55
|
+
costs_dt['downward slack penalty'] = 0
|
|
46
56
|
costs_dt['inertia slack penalty'] = r.costPenalty_inertia_dt.sum(axis=1)
|
|
47
57
|
costs_dt['non-synchronous slack penalty'] = r.costPenalty_non_synchronous_dt.sum(axis=1)
|
|
48
58
|
try:
|