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,547 @@
|
|
|
1
|
+
"""Per-(d, t) fallback lookups used by ``entity_period_calc_params``.
|
|
2
|
+
|
|
3
|
+
Three lookup classes implementing the 4-/6-/7-branch fallback chains:
|
|
4
|
+
|
|
5
|
+
* :class:`PdLookup` — 4-branch period-only resolution (used by
|
|
6
|
+
``pdNode`` / ``pdProcess``).
|
|
7
|
+
* :class:`PdtLookup` — 7- or 9-branch period+time fallback over
|
|
8
|
+
pbt/pd/pt/p + class-level defaults.
|
|
9
|
+
* :class:`PdtLookupPerSide` — 6-branch period+time fallback for the
|
|
10
|
+
(process, side) per-arc tables.
|
|
11
|
+
|
|
12
|
+
Why a class (not a polars-lazy frame)?
|
|
13
|
+
--------------------------------------
|
|
14
|
+
|
|
15
|
+
Consumers call ``.get(entity, param, period, time)`` per output row in
|
|
16
|
+
the inner emission loop. The lookup is a layered
|
|
17
|
+
``dict[tuple, float]`` cascade — optimal for the access pattern.
|
|
18
|
+
A polars-lazy join chain would be slower at the small fixture sizes
|
|
19
|
+
used by these writers and would obscure the 4-/7-branch semantics.
|
|
20
|
+
|
|
21
|
+
The CSV-read helpers below return plain dicts so the ``.get()`` hot
|
|
22
|
+
path stays pure Python (no polars overhead per call).
|
|
23
|
+
|
|
24
|
+
Branching semantics:
|
|
25
|
+
* 4-branch (``PdLookup``): pd row → branch-sum → p scalar → 0.
|
|
26
|
+
* 7-branch (``PdtLookup``): stochastic-fold → parent-period-fold →
|
|
27
|
+
pd or pt (order toggled by ``time_first_priority``) → p scalar →
|
|
28
|
+
``param ∈ param_def1`` → class default → 0.
|
|
29
|
+
* 6-branch (``PdtLookupPerSide``): stochastic-fold → parent-period-fold
|
|
30
|
+
→ pd → pt → p → 0.
|
|
31
|
+
"""
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
from flextool.engine_polars._emit_provider_io import _provider_key
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
# Native-frame row helpers.
|
|
41
|
+
#
|
|
42
|
+
# These read the in-memory polars frame directly from the Provider via
|
|
43
|
+
# ``provider.get(_provider_key(path))`` instead of round-tripping through
|
|
44
|
+
# CSV text (the legacy ``_provider_open`` + ``csv.reader`` path).
|
|
45
|
+
#
|
|
46
|
+
# Type-fidelity contract — reproduce *exactly* what ``csv.reader`` over a
|
|
47
|
+
# ``DataFrame.write_csv`` serialisation would have yielded:
|
|
48
|
+
#
|
|
49
|
+
# * Key columns (the dict-key positions) were ``csv.reader`` strings.
|
|
50
|
+
# ``write_csv`` serialises ``null`` → ``""`` and any scalar (Enum /
|
|
51
|
+
# Int / Float / Utf8) → its string form. We therefore coerce each
|
|
52
|
+
# key cell with :func:`_cell_str` (``None`` → ``""``, else ``str``)
|
|
53
|
+
# and apply the original truthiness guard to the *string* form so a
|
|
54
|
+
# null cell is skipped (matching the legacy ``if row[i]`` test) while
|
|
55
|
+
# a literal ``"0"`` is kept.
|
|
56
|
+
# * Value columns were re-coerced with ``float(...)``. We apply
|
|
57
|
+
# ``float(...)`` to the native cell — harmless on an already-float
|
|
58
|
+
# frame, necessary on an int frame, and identical to the legacy
|
|
59
|
+
# ``float(str_cell)`` on a stringified-value frame. A value that
|
|
60
|
+
# cannot be parsed as a float is skipped (matching the legacy
|
|
61
|
+
# ``except ValueError: continue``).
|
|
62
|
+
#
|
|
63
|
+
# ``provider.get`` returns data rows only (no header), so there is no
|
|
64
|
+
# header row to skip; an empty / missing frame yields the same empty
|
|
65
|
+
# dict / list the legacy loop produced.
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
def _cell_str(value: "object | None") -> str:
|
|
69
|
+
"""Reproduce a ``csv.reader`` cell string for a native frame value.
|
|
70
|
+
|
|
71
|
+
``DataFrame.write_csv`` renders ``null`` as the empty string and every
|
|
72
|
+
other scalar as its textual form; ``csv.reader`` then reads those
|
|
73
|
+
strings back. Mirror that here so dict keys stay byte-identical to
|
|
74
|
+
the legacy CSV round-trip.
|
|
75
|
+
"""
|
|
76
|
+
return "" if value is None else str(value)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _read_pd(path: Path,
|
|
80
|
+
*, provider: "object | None" = None) -> dict[tuple[str, str, str], float]:
|
|
81
|
+
"""4-col frame: (entity, param, period, value)."""
|
|
82
|
+
out: dict[tuple[str, str, str], float] = {}
|
|
83
|
+
df = provider.get(_provider_key(path))
|
|
84
|
+
if df is None:
|
|
85
|
+
return out
|
|
86
|
+
for row in df.iter_rows():
|
|
87
|
+
if len(row) < 4:
|
|
88
|
+
continue
|
|
89
|
+
c0, c1, c2 = _cell_str(row[0]), _cell_str(row[1]), _cell_str(row[2])
|
|
90
|
+
if c0 and c1 and c2:
|
|
91
|
+
try:
|
|
92
|
+
out[(c0, c1, c2)] = float(row[3])
|
|
93
|
+
except (ValueError, TypeError):
|
|
94
|
+
continue
|
|
95
|
+
return out
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _read_p(path: Path,
|
|
99
|
+
*, provider: "object | None" = None) -> dict[tuple[str, str], float]:
|
|
100
|
+
"""3-col frame: (entity, param, value)."""
|
|
101
|
+
out: dict[tuple[str, str], float] = {}
|
|
102
|
+
df = provider.get(_provider_key(path))
|
|
103
|
+
if df is None:
|
|
104
|
+
return out
|
|
105
|
+
for row in df.iter_rows():
|
|
106
|
+
if len(row) < 3:
|
|
107
|
+
continue
|
|
108
|
+
c0, c1 = _cell_str(row[0]), _cell_str(row[1])
|
|
109
|
+
if c0 and c1:
|
|
110
|
+
try:
|
|
111
|
+
out[(c0, c1)] = float(row[2])
|
|
112
|
+
except (ValueError, TypeError):
|
|
113
|
+
continue
|
|
114
|
+
return out
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _read_pt(path: Path,
|
|
118
|
+
*, provider: "object | None" = None) -> dict[tuple[str, str, str], float]:
|
|
119
|
+
"""4-col frame: (entity, param, time, value)."""
|
|
120
|
+
out: dict[tuple[str, str, str], float] = {}
|
|
121
|
+
df = provider.get(_provider_key(path))
|
|
122
|
+
if df is None:
|
|
123
|
+
return out
|
|
124
|
+
for row in df.iter_rows():
|
|
125
|
+
if len(row) < 4:
|
|
126
|
+
continue
|
|
127
|
+
c0, c1, c2 = _cell_str(row[0]), _cell_str(row[1]), _cell_str(row[2])
|
|
128
|
+
if c0 and c1 and c2:
|
|
129
|
+
try:
|
|
130
|
+
out[(c0, c1, c2)] = float(row[3])
|
|
131
|
+
except (ValueError, TypeError):
|
|
132
|
+
continue
|
|
133
|
+
return out
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _read_pbt(path: Path,
|
|
137
|
+
*, provider: "object | None" = None) -> dict[tuple[str, str, str, str, str], float]:
|
|
138
|
+
"""6-col frame: (entity, param, branch, time_start, time, value)."""
|
|
139
|
+
out: dict[tuple[str, str, str, str, str], float] = {}
|
|
140
|
+
df = provider.get(_provider_key(path))
|
|
141
|
+
if df is None:
|
|
142
|
+
return out
|
|
143
|
+
for row in df.iter_rows():
|
|
144
|
+
if len(row) < 6:
|
|
145
|
+
continue
|
|
146
|
+
c = [_cell_str(row[i]) for i in range(5)]
|
|
147
|
+
if all(c):
|
|
148
|
+
try:
|
|
149
|
+
out[(c[0], c[1], c[2], c[3], c[4])] = float(row[5])
|
|
150
|
+
except (ValueError, TypeError):
|
|
151
|
+
continue
|
|
152
|
+
return out
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _read_pd_3(path: Path,
|
|
156
|
+
*, provider: "object | None" = None) -> dict[tuple[str, str, str, str], float]:
|
|
157
|
+
"""5-col frame: (e1, e2, param, period, value)."""
|
|
158
|
+
out: dict[tuple[str, str, str, str], float] = {}
|
|
159
|
+
df = provider.get(_provider_key(path))
|
|
160
|
+
if df is None:
|
|
161
|
+
return out
|
|
162
|
+
for row in df.iter_rows():
|
|
163
|
+
if len(row) < 5:
|
|
164
|
+
continue
|
|
165
|
+
c = [_cell_str(row[i]) for i in range(4)]
|
|
166
|
+
if all(c):
|
|
167
|
+
try:
|
|
168
|
+
out[(c[0], c[1], c[2], c[3])] = float(row[4])
|
|
169
|
+
except (ValueError, TypeError):
|
|
170
|
+
continue
|
|
171
|
+
return out
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _read_pt_3(path: Path,
|
|
175
|
+
*, provider: "object | None" = None) -> dict[tuple[str, str, str, str], float]:
|
|
176
|
+
"""5-col frame: (e1, e2, param, time, value)."""
|
|
177
|
+
out: dict[tuple[str, str, str, str], float] = {}
|
|
178
|
+
df = provider.get(_provider_key(path))
|
|
179
|
+
if df is None:
|
|
180
|
+
return out
|
|
181
|
+
for row in df.iter_rows():
|
|
182
|
+
if len(row) < 5:
|
|
183
|
+
continue
|
|
184
|
+
c = [_cell_str(row[i]) for i in range(4)]
|
|
185
|
+
if all(c):
|
|
186
|
+
try:
|
|
187
|
+
out[(c[0], c[1], c[2], c[3])] = float(row[4])
|
|
188
|
+
except (ValueError, TypeError):
|
|
189
|
+
continue
|
|
190
|
+
return out
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def _read_p_3(path: Path,
|
|
194
|
+
*, provider: "object | None" = None) -> dict[tuple[str, str, str], float]:
|
|
195
|
+
"""4-col frame: (e1, e2, param, value)."""
|
|
196
|
+
out: dict[tuple[str, str, str], float] = {}
|
|
197
|
+
df = provider.get(_provider_key(path))
|
|
198
|
+
if df is None:
|
|
199
|
+
return out
|
|
200
|
+
for row in df.iter_rows():
|
|
201
|
+
if len(row) < 4:
|
|
202
|
+
continue
|
|
203
|
+
c = [_cell_str(row[i]) for i in range(3)]
|
|
204
|
+
if all(c):
|
|
205
|
+
try:
|
|
206
|
+
out[(c[0], c[1], c[2])] = float(row[3])
|
|
207
|
+
except (ValueError, TypeError):
|
|
208
|
+
continue
|
|
209
|
+
return out
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _read_pbt_3(path: Path,
|
|
213
|
+
*, provider: "object | None" = None) -> dict[tuple[str, str, str, str, str, str], float]:
|
|
214
|
+
"""7-col frame: (e1, e2, param, branch, time_start, time, value)."""
|
|
215
|
+
out: dict[tuple[str, str, str, str, str, str], float] = {}
|
|
216
|
+
df = provider.get(_provider_key(path))
|
|
217
|
+
if df is None:
|
|
218
|
+
return out
|
|
219
|
+
for row in df.iter_rows():
|
|
220
|
+
if len(row) < 7:
|
|
221
|
+
continue
|
|
222
|
+
c = [_cell_str(row[i]) for i in range(6)]
|
|
223
|
+
if all(c):
|
|
224
|
+
try:
|
|
225
|
+
out[(c[0], c[1], c[2], c[3], c[4], c[5])] = float(row[6])
|
|
226
|
+
except (ValueError, TypeError):
|
|
227
|
+
continue
|
|
228
|
+
return out
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _read_pairs_to_dict(path: Path, key_col: int,
|
|
232
|
+
*, provider: "object | None" = None) -> dict[str, list[str]]:
|
|
233
|
+
"""Generic two-col frame → ``key_col → list[other_col]``."""
|
|
234
|
+
out: dict[str, list[str]] = {}
|
|
235
|
+
df = provider.get(_provider_key(path))
|
|
236
|
+
if df is None:
|
|
237
|
+
return out
|
|
238
|
+
other_col = 1 - key_col
|
|
239
|
+
for row in df.iter_rows():
|
|
240
|
+
if len(row) < 2:
|
|
241
|
+
continue
|
|
242
|
+
c0, c1 = _cell_str(row[0]), _cell_str(row[1])
|
|
243
|
+
if c0 and c1:
|
|
244
|
+
out.setdefault((c0, c1)[key_col], []).append((c0, c1)[other_col])
|
|
245
|
+
return out
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _read_period_branch(path: Path,
|
|
249
|
+
*, provider: "object | None" = None) -> dict[str, list[str]]:
|
|
250
|
+
"""Build ``d → [db]`` from ``period__branch`` (col0=db, col1=d)."""
|
|
251
|
+
out: dict[str, list[str]] = {}
|
|
252
|
+
df = provider.get(_provider_key(path))
|
|
253
|
+
if df is None:
|
|
254
|
+
return out
|
|
255
|
+
for row in df.iter_rows():
|
|
256
|
+
if len(row) < 2:
|
|
257
|
+
continue
|
|
258
|
+
c0, c1 = _cell_str(row[0]), _cell_str(row[1])
|
|
259
|
+
if c0 and c1:
|
|
260
|
+
out.setdefault(c1, []).append(c0)
|
|
261
|
+
return out
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _read_singles(path: Path,
|
|
265
|
+
*, provider: "object | None" = None) -> list[str]:
|
|
266
|
+
df = provider.get(_provider_key(path))
|
|
267
|
+
if df is None:
|
|
268
|
+
return []
|
|
269
|
+
out: list[str] = []
|
|
270
|
+
for row in df.iter_rows():
|
|
271
|
+
if not row:
|
|
272
|
+
continue
|
|
273
|
+
c0 = _cell_str(row[0])
|
|
274
|
+
if c0:
|
|
275
|
+
out.append(c0)
|
|
276
|
+
return out
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
# ---------------------------------------------------------------------------
|
|
280
|
+
# Param-default enums (mirror flextool_base.dat — pinned here to avoid
|
|
281
|
+
# importing the legacy preprocessing tree).
|
|
282
|
+
# ---------------------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
PROCESS_PARAM_DEF1: frozenset[str] = frozenset({"efficiency", "availability"})
|
|
285
|
+
NODE_PARAM_DEF1: frozenset[str] = frozenset({"availability"})
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def read_class_defaults(path: Path, class_name: str,
|
|
289
|
+
*, provider: "object | None" = None) -> dict[str, float]:
|
|
290
|
+
"""Read ``input/default_values.csv`` filtered to ``class_name``."""
|
|
291
|
+
out: dict[str, float] = {}
|
|
292
|
+
df = provider.get(_provider_key(path))
|
|
293
|
+
if df is None:
|
|
294
|
+
return out
|
|
295
|
+
for row in df.iter_rows():
|
|
296
|
+
if len(row) < 3:
|
|
297
|
+
continue
|
|
298
|
+
c0, c1 = _cell_str(row[0]), _cell_str(row[1])
|
|
299
|
+
if c0 == class_name and c1:
|
|
300
|
+
try:
|
|
301
|
+
out[c1] = float(row[2])
|
|
302
|
+
except (ValueError, TypeError):
|
|
303
|
+
continue
|
|
304
|
+
return out
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# ---------------------------------------------------------------------------
|
|
308
|
+
# PdLookup — 4-branch period-only resolution.
|
|
309
|
+
# ---------------------------------------------------------------------------
|
|
310
|
+
|
|
311
|
+
class PdLookup:
|
|
312
|
+
"""Mod's 4-branch period-param resolution (pdProcess / pdNode @ L1252/L1216).
|
|
313
|
+
|
|
314
|
+
Branch 1: explicit (e, param, d) row in ``pd_<class>``.
|
|
315
|
+
Branch 2: any ``(e, param, db)`` where ``(db, d)`` ∈ period__branch — sum.
|
|
316
|
+
Branch 3: scalar fallback in ``p_<class>``.
|
|
317
|
+
Branch 4: 0.
|
|
318
|
+
"""
|
|
319
|
+
|
|
320
|
+
def __init__(
|
|
321
|
+
self,
|
|
322
|
+
pd_csv: Path, # input/pd_<class>.csv
|
|
323
|
+
p_csv: Path, # input/p_<class>.csv
|
|
324
|
+
period_branch_csv: Path, # solve_data/period__branch.csv
|
|
325
|
+
*,
|
|
326
|
+
provider: "object | None" = None,
|
|
327
|
+
) -> None:
|
|
328
|
+
self._pd = _read_pd(pd_csv, provider=provider)
|
|
329
|
+
self._p = _read_p(p_csv, provider=provider)
|
|
330
|
+
self._branches_for_d = _read_period_branch(period_branch_csv, provider=provider)
|
|
331
|
+
|
|
332
|
+
def get(self, e: str, param: str, d: str) -> float:
|
|
333
|
+
if (e, param, d) in self._pd:
|
|
334
|
+
return self._pd[(e, param, d)]
|
|
335
|
+
branches = self._branches_for_d.get(d, ())
|
|
336
|
+
branched = [
|
|
337
|
+
self._pd[(e, param, db)]
|
|
338
|
+
for db in branches
|
|
339
|
+
if (e, param, db) in self._pd
|
|
340
|
+
]
|
|
341
|
+
if branched:
|
|
342
|
+
return sum(branched)
|
|
343
|
+
if (e, param) in self._p:
|
|
344
|
+
return self._p[(e, param)]
|
|
345
|
+
return 0.0
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
# ---------------------------------------------------------------------------
|
|
349
|
+
# PdtLookup — 7/9-branch period+time fallback.
|
|
350
|
+
# ---------------------------------------------------------------------------
|
|
351
|
+
|
|
352
|
+
class PdtLookup:
|
|
353
|
+
"""Mod's 7/9-branch ``pdtProcess`` / ``pdtNode`` resolution.
|
|
354
|
+
|
|
355
|
+
Outer index ``(e, param, d, t)``. Branches in order:
|
|
356
|
+
|
|
357
|
+
1. stochastic-fold: e ∈ groupStochastic-flagged group — sum pbt rows
|
|
358
|
+
over ts ∈ first_timesteps[d], tb ∈ solve_branch[d].
|
|
359
|
+
2. parent-period-fold: for each pe with (pe, d) ∈ period__branch,
|
|
360
|
+
sum pbt rows over ts ∈ first_timesteps[d], tb ∈ solve_branch[pe].
|
|
361
|
+
3-4. pt or pd row (order toggled by ``time_first_priority``;
|
|
362
|
+
pdtNode prefers time, pdtProcess prefers period).
|
|
363
|
+
5. p scalar.
|
|
364
|
+
6. ``param ∈ param_def1`` → 1.
|
|
365
|
+
7. class-default (pdtNode only).
|
|
366
|
+
8. 0.
|
|
367
|
+
"""
|
|
368
|
+
|
|
369
|
+
def __init__(
|
|
370
|
+
self,
|
|
371
|
+
pbt_csv: Path,
|
|
372
|
+
pd_csv: Path,
|
|
373
|
+
pt_csv: Path,
|
|
374
|
+
p_csv: Path,
|
|
375
|
+
period_time_first_csv: Path,
|
|
376
|
+
solve_branch_csv: Path,
|
|
377
|
+
period_branch_csv: Path,
|
|
378
|
+
group_entity_csv: Path,
|
|
379
|
+
group_stochastic_csv: Path,
|
|
380
|
+
param_def1: frozenset[str],
|
|
381
|
+
time_first_priority: bool = False,
|
|
382
|
+
class_default_values: dict[str, float] | None = None,
|
|
383
|
+
*,
|
|
384
|
+
provider: "object | None" = None,
|
|
385
|
+
) -> None:
|
|
386
|
+
self._pbt = _read_pbt(pbt_csv, provider=provider)
|
|
387
|
+
self._pd = _read_pd(pd_csv, provider=provider)
|
|
388
|
+
self._pt = _read_pt(pt_csv, provider=provider)
|
|
389
|
+
self._p = _read_p(p_csv, provider=provider)
|
|
390
|
+
self._ts_for_d = _read_pairs_to_dict(period_time_first_csv, key_col=0, provider=provider)
|
|
391
|
+
self._tb_for_d = _read_pairs_to_dict(solve_branch_csv, key_col=0, provider=provider)
|
|
392
|
+
self._pe_for_d = _read_pairs_to_dict(period_branch_csv, key_col=1, provider=provider)
|
|
393
|
+
groups_stoch = frozenset(_read_singles(group_stochastic_csv, provider=provider))
|
|
394
|
+
self._stoch_entity: set[str] = set()
|
|
395
|
+
ge_df = provider.get(_provider_key(group_entity_csv))
|
|
396
|
+
if ge_df is not None:
|
|
397
|
+
for row in ge_df.iter_rows():
|
|
398
|
+
if len(row) < 2:
|
|
399
|
+
continue
|
|
400
|
+
c0, c1 = _cell_str(row[0]), _cell_str(row[1])
|
|
401
|
+
if c0 in groups_stoch and c1:
|
|
402
|
+
self._stoch_entity.add(c1)
|
|
403
|
+
self._param_def1 = param_def1
|
|
404
|
+
self._time_first = time_first_priority
|
|
405
|
+
self._class_defaults = class_default_values or {}
|
|
406
|
+
|
|
407
|
+
def get(self, e: str, param: str, d: str, t: str) -> float:
|
|
408
|
+
# Branch 1: stochastic fold-in.
|
|
409
|
+
if e in self._stoch_entity:
|
|
410
|
+
ts_list = self._ts_for_d.get(d, ())
|
|
411
|
+
tb_list = self._tb_for_d.get(d, ())
|
|
412
|
+
total = 0.0
|
|
413
|
+
hit = False
|
|
414
|
+
for tb in tb_list:
|
|
415
|
+
for ts in ts_list:
|
|
416
|
+
v = self._pbt.get((e, param, tb, ts, t))
|
|
417
|
+
if v is not None:
|
|
418
|
+
total += v
|
|
419
|
+
hit = True
|
|
420
|
+
if hit:
|
|
421
|
+
return total
|
|
422
|
+
# Branch 2: parent-period fold-in.
|
|
423
|
+
ts_list = self._ts_for_d.get(d, ())
|
|
424
|
+
pe_list = self._pe_for_d.get(d, ())
|
|
425
|
+
if pe_list and ts_list:
|
|
426
|
+
total = 0.0
|
|
427
|
+
hit = False
|
|
428
|
+
for pe in pe_list:
|
|
429
|
+
for tb in self._tb_for_d.get(pe, ()):
|
|
430
|
+
for ts in ts_list:
|
|
431
|
+
v = self._pbt.get((e, param, tb, ts, t))
|
|
432
|
+
if v is not None:
|
|
433
|
+
total += v
|
|
434
|
+
hit = True
|
|
435
|
+
if hit:
|
|
436
|
+
return total
|
|
437
|
+
# Branches 3-4: pt vs pd order toggle.
|
|
438
|
+
if self._time_first:
|
|
439
|
+
v = self._pt.get((e, param, t))
|
|
440
|
+
if v is not None:
|
|
441
|
+
return v
|
|
442
|
+
v = self._pd.get((e, param, d))
|
|
443
|
+
if v is not None:
|
|
444
|
+
return v
|
|
445
|
+
else:
|
|
446
|
+
v = self._pd.get((e, param, d))
|
|
447
|
+
if v is not None:
|
|
448
|
+
return v
|
|
449
|
+
v = self._pt.get((e, param, t))
|
|
450
|
+
if v is not None:
|
|
451
|
+
return v
|
|
452
|
+
# Branch 5: scalar.
|
|
453
|
+
v = self._p.get((e, param))
|
|
454
|
+
if v is not None:
|
|
455
|
+
return v
|
|
456
|
+
# Branch 6: param_def1 default-1.
|
|
457
|
+
if param in self._param_def1:
|
|
458
|
+
return 1.0
|
|
459
|
+
# Branch 7: class default.
|
|
460
|
+
if param in self._class_defaults:
|
|
461
|
+
return self._class_defaults[param]
|
|
462
|
+
# Branch 8: 0.
|
|
463
|
+
return 0.0
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
# ---------------------------------------------------------------------------
|
|
467
|
+
# PdtLookupPerSide — 6-branch for (process, side) entities.
|
|
468
|
+
# ---------------------------------------------------------------------------
|
|
469
|
+
|
|
470
|
+
class PdtLookupPerSide:
|
|
471
|
+
"""6-branch pdt lookup for ``pdtProcess_source`` / ``pdtProcess_sink``.
|
|
472
|
+
|
|
473
|
+
Entity key is ``(process, side)``. No def1 branch; otherwise same
|
|
474
|
+
cascade as :class:`PdtLookup` minus the param_def1 / class-default
|
|
475
|
+
layers.
|
|
476
|
+
"""
|
|
477
|
+
|
|
478
|
+
def __init__(
|
|
479
|
+
self,
|
|
480
|
+
pbt_csv: Path,
|
|
481
|
+
pd_csv: Path,
|
|
482
|
+
pt_csv: Path,
|
|
483
|
+
p_csv: Path,
|
|
484
|
+
period_time_first_csv: Path,
|
|
485
|
+
solve_branch_csv: Path,
|
|
486
|
+
period_branch_csv: Path,
|
|
487
|
+
group_process_csv: Path,
|
|
488
|
+
group_stochastic_csv: Path,
|
|
489
|
+
*,
|
|
490
|
+
provider: "object | None" = None,
|
|
491
|
+
) -> None:
|
|
492
|
+
self._pbt = _read_pbt_3(pbt_csv, provider=provider)
|
|
493
|
+
self._pd = _read_pd_3(pd_csv, provider=provider)
|
|
494
|
+
self._pt = _read_pt_3(pt_csv, provider=provider)
|
|
495
|
+
self._p = _read_p_3(p_csv, provider=provider)
|
|
496
|
+
self._ts_for_d = _read_pairs_to_dict(period_time_first_csv, key_col=0, provider=provider)
|
|
497
|
+
self._tb_for_d = _read_pairs_to_dict(solve_branch_csv, key_col=0, provider=provider)
|
|
498
|
+
self._pe_for_d = _read_pairs_to_dict(period_branch_csv, key_col=1, provider=provider)
|
|
499
|
+
groups_stoch = frozenset(_read_singles(group_stochastic_csv, provider=provider))
|
|
500
|
+
self._stoch_process: set[str] = set()
|
|
501
|
+
gp_df = provider.get(_provider_key(group_process_csv))
|
|
502
|
+
if gp_df is not None:
|
|
503
|
+
for row in gp_df.iter_rows():
|
|
504
|
+
if len(row) < 2:
|
|
505
|
+
continue
|
|
506
|
+
c0, c1 = _cell_str(row[0]), _cell_str(row[1])
|
|
507
|
+
if c0 in groups_stoch and c1:
|
|
508
|
+
self._stoch_process.add(c1)
|
|
509
|
+
|
|
510
|
+
def get(self, p: str, side: str, param: str, d: str, t: str) -> float:
|
|
511
|
+
if p in self._stoch_process:
|
|
512
|
+
ts_list = self._ts_for_d.get(d, ())
|
|
513
|
+
tb_list = self._tb_for_d.get(d, ())
|
|
514
|
+
total = 0.0
|
|
515
|
+
hit = False
|
|
516
|
+
for tb in tb_list:
|
|
517
|
+
for ts in ts_list:
|
|
518
|
+
v = self._pbt.get((p, side, param, tb, ts, t))
|
|
519
|
+
if v is not None:
|
|
520
|
+
total += v
|
|
521
|
+
hit = True
|
|
522
|
+
if hit:
|
|
523
|
+
return total
|
|
524
|
+
ts_list = self._ts_for_d.get(d, ())
|
|
525
|
+
pe_list = self._pe_for_d.get(d, ())
|
|
526
|
+
if pe_list and ts_list:
|
|
527
|
+
total = 0.0
|
|
528
|
+
hit = False
|
|
529
|
+
for pe in pe_list:
|
|
530
|
+
for tb in self._tb_for_d.get(pe, ()):
|
|
531
|
+
for ts in ts_list:
|
|
532
|
+
v = self._pbt.get((p, side, param, tb, ts, t))
|
|
533
|
+
if v is not None:
|
|
534
|
+
total += v
|
|
535
|
+
hit = True
|
|
536
|
+
if hit:
|
|
537
|
+
return total
|
|
538
|
+
v = self._pd.get((p, side, param, d))
|
|
539
|
+
if v is not None:
|
|
540
|
+
return v
|
|
541
|
+
v = self._pt.get((p, side, param, t))
|
|
542
|
+
if v is not None:
|
|
543
|
+
return v
|
|
544
|
+
v = self._p.get((p, side, param))
|
|
545
|
+
if v is not None:
|
|
546
|
+
return v
|
|
547
|
+
return 0.0
|