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,153 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
import spinedb_api as api
|
|
4
|
+
from spinedb_api import DatabaseMapping
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from flextool.engine_polars._db_reader import check_version
|
|
8
|
+
from flextool.input_derivation import run as _input_derivation_run
|
|
9
|
+
from flextool.engine_polars._solve_config import SolveConfig
|
|
10
|
+
from flextool.engine_polars._timeline import TimelineConfig
|
|
11
|
+
from flextool.engine_polars._solve_state import PathConfig, RunnerState, FlexToolError, FlexToolConfigError
|
|
12
|
+
from flextool.cli._timing import TimingRecorder
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FlexToolRunner:
|
|
16
|
+
"""Thin coordinator that builds RunnerState for the native cascade.
|
|
17
|
+
|
|
18
|
+
Public API:
|
|
19
|
+
write_input() — reads DB, writes input/ CSV files (delegates to input_writer)
|
|
20
|
+
|
|
21
|
+
See the engine_polars cascade modules for the full input-writing flow.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, input_db_url=None, output_path=None, scenario_name=None, flextool_dir=None, solver_config_dir=None, root_dir=None, work_folder=None, highs_threads=None, auto_scale=False, timing_recorder: "TimingRecorder | None" = None):
|
|
25
|
+
try:
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
# Resolve work_folder: default to cwd for backward compatibility
|
|
28
|
+
resolved_work_folder = Path(work_folder) if work_folder is not None else Path.cwd()
|
|
29
|
+
# delete highs.log from previous run
|
|
30
|
+
highs_log = resolved_work_folder / "HiGHS.log"
|
|
31
|
+
if highs_log.exists():
|
|
32
|
+
highs_log.unlink()
|
|
33
|
+
# make a directory for solve data
|
|
34
|
+
(resolved_work_folder / "solve_data").mkdir(exist_ok=True)
|
|
35
|
+
# Build PathConfig — defaults are PyPI-friendly: the
|
|
36
|
+
# ``flextool`` package directory for static data, CWD for
|
|
37
|
+
# outputs and user-editable ``solver_config/highs.opt``.
|
|
38
|
+
from flextool._resources import package_data_path
|
|
39
|
+
_pkg_dir = package_data_path("")
|
|
40
|
+
paths = PathConfig(
|
|
41
|
+
flextool_dir=Path(flextool_dir) if flextool_dir is not None else _pkg_dir,
|
|
42
|
+
solver_config_dir=Path(solver_config_dir) if solver_config_dir is not None else Path.cwd() / "solver_config",
|
|
43
|
+
root_dir=Path(root_dir) if root_dir is not None else Path.cwd(),
|
|
44
|
+
output_path=Path(output_path) if output_path is not None else Path.cwd(),
|
|
45
|
+
work_folder=resolved_work_folder,
|
|
46
|
+
)
|
|
47
|
+
# read the data in
|
|
48
|
+
# open connection to input db
|
|
49
|
+
if scenario_name:
|
|
50
|
+
scen_config = api.filters.scenario_filter.scenario_filter_config(scenario_name)
|
|
51
|
+
with (DatabaseMapping(input_db_url) as db):
|
|
52
|
+
if scenario_name:
|
|
53
|
+
api.filters.scenario_filter.scenario_filter_from_dict(db, scen_config)
|
|
54
|
+
else:
|
|
55
|
+
scen_names = db.get_scenario_items()
|
|
56
|
+
if len(scen_names) == 0:
|
|
57
|
+
message = "No scenario found"
|
|
58
|
+
logger.error(message)
|
|
59
|
+
raise FlexToolConfigError(message)
|
|
60
|
+
scenario_name=scen_names[0]['name']
|
|
61
|
+
# The Work dir / DB URL / Scenario / Output header is
|
|
62
|
+
# now printed once by ``cmd_run_flextool.main`` before
|
|
63
|
+
# the cascade starts. The previous multi-line INFO
|
|
64
|
+
# here duplicated the Scenario line and split related
|
|
65
|
+
# metadata across the startup log; drop it.
|
|
66
|
+
if len(db.get_scenario_alternative_items(scenario_name=scenario_name)) == 0:
|
|
67
|
+
message = "No alternatives in the scenario, i.e. empty scenario."
|
|
68
|
+
logger.error(message)
|
|
69
|
+
raise FlexToolConfigError(message)
|
|
70
|
+
|
|
71
|
+
# Pre-warm both entity and parameter_value caches so
|
|
72
|
+
# every find_* call downstream (SolveConfig, TimelineConfig,
|
|
73
|
+
# input_writer) hits memory. Adding fetch_all("entity")
|
|
74
|
+
# matches the spinedb-api docs' performance guidance and
|
|
75
|
+
# mirrors the engine_polars _solve_config / _timeline
|
|
76
|
+
# load_from_db_url paths.
|
|
77
|
+
db.fetch_all("entity")
|
|
78
|
+
db.fetch_all("parameter_value")
|
|
79
|
+
check_version(db=db, logger=logger)
|
|
80
|
+
# Solve-level fields — delegated to SolveConfig
|
|
81
|
+
solve = SolveConfig.load_from_db(db=db, logger=logger)
|
|
82
|
+
# Timeline-level fields — delegated to TimelineConfig
|
|
83
|
+
timeline = TimelineConfig.load_from_db(db=db, logger=logger)
|
|
84
|
+
|
|
85
|
+
# Post-DB initialization of timeline
|
|
86
|
+
timeline.create_assumptive_parts(solve)
|
|
87
|
+
timeline.create_timeline_from_timestep_duration(solve)
|
|
88
|
+
|
|
89
|
+
# Assemble RunnerState — the single cross-cutting state container
|
|
90
|
+
self.state = RunnerState(
|
|
91
|
+
paths=paths, solve=solve, timeline=timeline, logger=logger,
|
|
92
|
+
)
|
|
93
|
+
# HiGHS thread count (CLI override; solver_runner defaults to 4 when None).
|
|
94
|
+
self.state.highs_threads = highs_threads
|
|
95
|
+
# Agent 8 (LP-scaling) — opt-in flag that lets the Python
|
|
96
|
+
# ScaleAnalyzer enable row scaling. Batch C.10 removed
|
|
97
|
+
# the DB-stored ``use_row_scaling`` setting; the per-solve
|
|
98
|
+
# row-scaling toggle is now driven by ``--scaling`` CLI +
|
|
99
|
+
# this auto-scale flag (or the FLEXTOOL_FORCE_ROW_SCALING
|
|
100
|
+
# env-var test hook). Analysis itself runs unconditionally
|
|
101
|
+
# (writes JSON); this flag only gates auto-application.
|
|
102
|
+
self.state.auto_scale = auto_scale
|
|
103
|
+
# Phase-timing recorder. The CLI constructs one earlier and
|
|
104
|
+
# passes it in; direct callers (tests) get a fresh recorder
|
|
105
|
+
# bootstrapped here so timings.csv coverage is consistent
|
|
106
|
+
# across both entry points. The recorder always lives on
|
|
107
|
+
# ``state.timing_recorder``.
|
|
108
|
+
if timing_recorder is not None:
|
|
109
|
+
self.state.timing_recorder = timing_recorder
|
|
110
|
+
# Late-bind scenario when the CLI couldn't pass it in.
|
|
111
|
+
if scenario_name and not timing_recorder.scenario:
|
|
112
|
+
timing_recorder.set_scenario(scenario_name)
|
|
113
|
+
elif self.state.timing_recorder is None:
|
|
114
|
+
self.state.timing_recorder = TimingRecorder(
|
|
115
|
+
work_folder=resolved_work_folder,
|
|
116
|
+
scenario=scenario_name,
|
|
117
|
+
)
|
|
118
|
+
except FlexToolError:
|
|
119
|
+
sys.exit(-1)
|
|
120
|
+
|
|
121
|
+
def write_input(
|
|
122
|
+
self,
|
|
123
|
+
input_db_url,
|
|
124
|
+
scenario_name=None,
|
|
125
|
+
precision_digits: int = 0,
|
|
126
|
+
*,
|
|
127
|
+
provider=None,
|
|
128
|
+
) -> None:
|
|
129
|
+
"""Write input/ CSVs to the runner's workdir.
|
|
130
|
+
|
|
131
|
+
Used by the regional-decomposition wrapper and a handful of
|
|
132
|
+
debug callers that want a freshly-staged ``input/`` directory.
|
|
133
|
+
When *provider* is None an ephemeral
|
|
134
|
+
:class:`flextool.engine_polars._flex_data_provider.FlexDataProvider`
|
|
135
|
+
is constructed; the cascade itself constructs its own Provider in
|
|
136
|
+
``_drive_cascade``.
|
|
137
|
+
"""
|
|
138
|
+
if provider is None:
|
|
139
|
+
from flextool.engine_polars._flex_data_provider import FlexDataProvider
|
|
140
|
+
provider = FlexDataProvider()
|
|
141
|
+
_input_derivation_run(
|
|
142
|
+
input_db_url,
|
|
143
|
+
provider,
|
|
144
|
+
self.state.logger,
|
|
145
|
+
scenario_name=scenario_name,
|
|
146
|
+
work_folder=self.state.paths.work_folder,
|
|
147
|
+
precision_digits=precision_digits,
|
|
148
|
+
)
|
|
149
|
+
# Persist the cascade-input Provider on ``self.state`` so the
|
|
150
|
+
# native cascade picks up the seeded ``input/<class>`` frames.
|
|
151
|
+
# Mirrors the same handoff that
|
|
152
|
+
# ``engine_polars._orchestration.run_orchestration`` performs.
|
|
153
|
+
self.state.cascade_input_provider = provider
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
db_reader — Pure functions for reading from spinedb_api.
|
|
3
|
+
No state, no side effects. All functions take explicit parameters.
|
|
4
|
+
"""
|
|
5
|
+
import logging
|
|
6
|
+
from collections import defaultdict
|
|
7
|
+
from enum import Enum
|
|
8
|
+
|
|
9
|
+
import spinedb_api as api
|
|
10
|
+
|
|
11
|
+
from flextool.engine_polars._solve_state import FlexToolConfigError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DictMode(Enum):
|
|
15
|
+
DICT = "dict"
|
|
16
|
+
DEFAULTDICT = "defaultdict"
|
|
17
|
+
LIST = "list"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def check_version(db: api.DatabaseMapping, logger: logging.Logger) -> None:
|
|
21
|
+
"""Verify that the input database version is compatible with this tool."""
|
|
22
|
+
db_version_item = db.get_parameter_definition_item(entity_class_name="model", name="version")
|
|
23
|
+
if not db_version_item:
|
|
24
|
+
message = "No version information found in the FlexTool input database, check you have a correct database."
|
|
25
|
+
logger.error(message)
|
|
26
|
+
raise FlexToolConfigError(message)
|
|
27
|
+
database_version = api.from_database(db_version_item["default_value"], db_version_item["default_type"])
|
|
28
|
+
tool_version = 25.0
|
|
29
|
+
if float(database_version) < tool_version:
|
|
30
|
+
message = (
|
|
31
|
+
"The input database is in an older version than the tool.\n"
|
|
32
|
+
"Please migrate the database to the new version:\n"
|
|
33
|
+
"- Make sure FlexTool python environment is activated\n"
|
|
34
|
+
"- cd to flextool directory\n"
|
|
35
|
+
"- run command: python migrate_database.py path_to_database\n"
|
|
36
|
+
"where path_to_database is replaced by the filepath of your current input database"
|
|
37
|
+
)
|
|
38
|
+
logger.error(message)
|
|
39
|
+
raise FlexToolConfigError(message)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_single_entities(db: api.DatabaseMapping, entity_class_name: str) -> list[str]:
|
|
43
|
+
"""Return a list of entity names for a single-dimension entity class."""
|
|
44
|
+
return [entity["entity_byname"][0] for entity in db.find_entities(entity_class_name=entity_class_name)]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def entities_to_dict(
|
|
48
|
+
db: api.DatabaseMapping, cl: str, mode: DictMode
|
|
49
|
+
) -> dict | defaultdict:
|
|
50
|
+
"""
|
|
51
|
+
Read multi-dimension entities of class *cl* and return as a dict.
|
|
52
|
+
|
|
53
|
+
mode="defaultdict" → defaultdict(list)
|
|
54
|
+
mode="dict" → plain dict
|
|
55
|
+
Each entity maps entity_byname[0] → list of remaining byname elements.
|
|
56
|
+
"""
|
|
57
|
+
entities = db.find_entities(entity_class_name=cl)
|
|
58
|
+
if mode == DictMode.DEFAULTDICT:
|
|
59
|
+
result: dict | defaultdict = defaultdict(list)
|
|
60
|
+
else:
|
|
61
|
+
result = dict()
|
|
62
|
+
for entity in entities:
|
|
63
|
+
if len(entity["entity_byname"]) > 1:
|
|
64
|
+
result[entity["entity_byname"][0]] = list(entity["entity_byname"][1:])
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError(
|
|
67
|
+
"Only one dimension in the entity, cannot make into a dict in entities_to_dict"
|
|
68
|
+
)
|
|
69
|
+
return result
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def params_to_dict(
|
|
73
|
+
db: api.DatabaseMapping,
|
|
74
|
+
cl: str,
|
|
75
|
+
par: str,
|
|
76
|
+
mode: DictMode,
|
|
77
|
+
str_to_list: bool = False,
|
|
78
|
+
) -> dict | defaultdict | list:
|
|
79
|
+
"""
|
|
80
|
+
Read parameter values of *par* on entity class *cl* and return as a
|
|
81
|
+
dict, defaultdict, or list depending on *mode*.
|
|
82
|
+
|
|
83
|
+
mode="defaultdict" → defaultdict(list)
|
|
84
|
+
mode="dict" → plain dict
|
|
85
|
+
mode="list" → list of [entity_name, value] pairs
|
|
86
|
+
str_to_list → wrap string values in a list (only for dict/defaultdict modes)
|
|
87
|
+
"""
|
|
88
|
+
all_params = db.find_parameter_values(
|
|
89
|
+
entity_class_name=cl, parameter_definition_name=par
|
|
90
|
+
)
|
|
91
|
+
if mode == DictMode.DEFAULTDICT:
|
|
92
|
+
result: dict | defaultdict | list = defaultdict(list)
|
|
93
|
+
elif mode == DictMode.DICT:
|
|
94
|
+
result = dict()
|
|
95
|
+
elif mode == DictMode.LIST:
|
|
96
|
+
result = []
|
|
97
|
+
for param in all_params:
|
|
98
|
+
param_value = api.from_database(param["value"], param["type"])
|
|
99
|
+
if mode in (DictMode.DEFAULTDICT, DictMode.DICT):
|
|
100
|
+
if isinstance(param_value, api.Map):
|
|
101
|
+
if isinstance(param_value.values[0], float):
|
|
102
|
+
result[param["entity_name"]] = list(
|
|
103
|
+
zip(list(param_value.indexes), list(map(float, param_value.values)))
|
|
104
|
+
)
|
|
105
|
+
elif isinstance(param_value.values[0], str):
|
|
106
|
+
result[param["entity_name"]] = list(
|
|
107
|
+
zip(list(param_value.indexes), param_value.values)
|
|
108
|
+
)
|
|
109
|
+
elif isinstance(param_value.values[0], api.Map):
|
|
110
|
+
result[param["entity_name"]] = api.convert_map_to_table(param_value)
|
|
111
|
+
else:
|
|
112
|
+
raise TypeError(
|
|
113
|
+
"params_to_dict function does not handle other values than floats and strings"
|
|
114
|
+
)
|
|
115
|
+
elif isinstance(param_value, api.Array):
|
|
116
|
+
result[param["entity_name"]] = param_value.values
|
|
117
|
+
elif isinstance(param_value, float):
|
|
118
|
+
result[param["entity_name"]] = str(param_value)
|
|
119
|
+
elif isinstance(param_value, str):
|
|
120
|
+
if str_to_list:
|
|
121
|
+
result[param["entity_name"]] = [param_value]
|
|
122
|
+
else:
|
|
123
|
+
result[param["entity_name"]] = param_value
|
|
124
|
+
elif mode == DictMode.LIST:
|
|
125
|
+
if isinstance(param_value, (float, str)):
|
|
126
|
+
result.append([param["entity_name"], param_value]) # type: ignore[union-attr]
|
|
127
|
+
return result
|