fluxopt.model_data
¶
Classes:
| Name | Description |
|---|---|
SizingData |
Sizing (capacity optimization) parameters for one entity family. |
InvestmentData |
Investment (build-timing optimization) parameters for one entity family. |
StatusData |
Binary on/off behaviour for one entity family — a flow's or a component's. |
FlowsData |
Flow parameters, as the tables a flow declares. |
CarriersData |
Which carrier each flow balances on, and what a carrier is called. |
ConvertersData |
Linear conversion equations, as the coefficients they actually have. |
PiecewiseData |
Piecewise-linear conversion curves, as the breakpoints they name. |
EffectsData |
What each effect is bounded by, and how effects charge one another. |
StoragesData |
Storage parameters, as the four tables a storage declares. |
Dims |
The model's temporal axes: what a timestep is, and what a period is. |
ModelData |
|
Functions:
| Name | Description |
|---|---|
compute_previous_duration |
Compute consecutive duration of target_state at end of previous_status. |
SizingData
dataclass
¶
Sizing (capacity optimization) parameters for one entity family.
Two frames rather than five arrays. bounds is one row per entity — the
scalars a Sizing declares — and effects is one row per effect an
entity actually charges, which is the shape #306 and #311 established for
coefficients: the pairs that exist, not the product they sit in.
Methods:
| Name | Description |
|---|---|
__post_init__ |
Re-check the bounds |
build |
Collect Sizing objects into frames, or None if there are none. |
Attributes:
| Name | Type | Description |
|---|---|---|
ids |
list[str]
|
The entities this table sizes, in declaration order. |
__post_init__
¶
Re-check the bounds Sizing already refuses, for a reloaded file.
The element layer is where this rule is enforced — there it fires on
the value the user wrote, naming the field. A hand-edited file never
passed through Sizing at all, which is the only reason to say it
twice. See docs/design/validation-layers.md.
Source code in src/fluxopt/model_data.py
build
classmethod
¶
Collect Sizing objects into frames, or None if there are none.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[tuple[str, Sizing]]
|
Pairs of (element_id, Sizing). |
required |
|
Index | None
|
Period index for period-varying effects. |
None
|
Source code in src/fluxopt/model_data.py
InvestmentData
dataclass
¶
Investment (build-timing optimization) parameters for one entity family.
Absence is a missing row, not a sentinel. An entity with no lifetime
has no row in lifetime and is alive forever; a coefficient nobody
declared has no row in effects and charges nothing. That is the
convention frames make available and the one lpspec's language already
uses, so the two now agree.
Methods:
| Name | Description |
|---|---|
__post_init__ |
Re-check the bounds |
build |
Collect Investment objects into frames, or None if there are none. |
Attributes:
| Name | Type | Description |
|---|---|---|
ids |
list[str]
|
The entities this table invests in, in declaration order. |
__post_init__
¶
Re-check the bounds Investment already refuses, for a reloaded file.
A reload guard — see docs/design/validation-layers.md.
Source code in src/fluxopt/model_data.py
build
classmethod
¶
Collect Investment objects into frames, or None if there are none.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[tuple[str, Investment]]
|
Pairs of (element_id, Investment). |
required |
|
Index | None
|
Period index for period-varying effects. |
None
|
Source code in src/fluxopt/model_data.py
StatusData
dataclass
¶
Binary on/off behaviour for one entity family — a flow's or a component's.
Durations are optional on the element, so an entity that constrains none
has no row in durations rather than a row of NaN. The same for what
happened before the horizon: prior holds only the entities that ran.
Methods:
| Name | Description |
|---|---|
__post_init__ |
Re-check the durations |
build |
Collect Status objects into frames, or None if there are none. |
Attributes:
| Name | Type | Description |
|---|---|---|
ids |
list[str]
|
The entities carrying a Status, in declaration order. |
__post_init__
¶
Re-check the durations Status already refuses, for a reloaded file.
A reload guard — see docs/design/validation-layers.md.
Source code in src/fluxopt/model_data.py
build
classmethod
¶
build(
items: list[tuple[str, Status]],
time: TimeIndex,
prior_rates_map: dict[str, list[float]] | None = None,
dt: float = 1.0,
period: Index | None = None,
) -> Self | None
Collect Status objects into frames, or None if there are none.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[tuple[str, Status]]
|
Pairs of (id, Status). |
required |
|
TimeIndex
|
Time index for effect series. |
required |
|
dict[str, list[float]] | None
|
Item id to prior flow rates (MW) before horizon. |
None
|
|
float
|
Scalar timestep duration in hours for prior duration computation. |
1.0
|
|
Index | None
|
Period index for period-varying effects. |
None
|
Source code in src/fluxopt/model_data.py
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 | |
FlowsData
dataclass
¶
FlowsData(
flows: DataFrame,
sizes: DataFrame,
envelope: DataFrame,
fixed_profile: DataFrame,
effect_pairs: DataFrame,
ramps: DataFrame,
aggregates: DataFrame,
governed_by: DataFrame,
sizing: SizingData | None = None,
invest: InvestmentData | None = None,
)
Flow parameters, as the tables a flow declares.
Absence is a missing row. A flow whose size is being optimized has no row
in sizes; one that limits no ramp has none in ramps; a flow charges
only the effects it has rows for in effect_pairs. envelope is the
exception and is dense on purpose: the relative bounds sit at the rate
variable's own grid, where a missing row reads as a binding zero.
Methods:
| Name | Description |
|---|---|
__post_init__ |
Validate relative bounds, status non-degeneracy, and sized-feature requirements. |
build |
Build FlowsData from element objects. |
Attributes:
| Name | Type | Description |
|---|---|---|
ids |
list[str]
|
Every flow, in declaration order. |
sized_ids |
set[str]
|
Flows whose size is known here — fixed, not optimized. |
has_size |
set[str]
|
Flows with a size at all — fixed here, or a variable to be solved for. |
profiled_ids |
list[str]
|
Flows whose rate is pinned to a profile, in declaration order. |
bounded_ids |
list[str]
|
Flows whose rate envelope is |
has_size
property
¶
Flows with a size at all — fixed here, or a variable to be solved for.
profiled_ids
property
¶
Flows whose rate is pinned to a profile, in declaration order.
bounded_ids
property
¶
Flows whose rate envelope is [rel_lb, rel_ub] * size.
Which is: it has a size, and no profile pinning it. A flow that is neither bounded nor profiled is unsized, and the program asks about that by asking about neither — so the third case needs no name.
__post_init__
¶
Validate relative bounds, status non-degeneracy, and sized-feature requirements.
Source code in src/fluxopt/model_data.py
build
classmethod
¶
build(
flows: list[_BoundFlow],
time: TimeIndex,
period: Index | None = None,
component_status_items: list[tuple[str, Status, list[str]]] | None = None,
) -> Self
Build FlowsData from element objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[_BoundFlow]
|
All collected flows with qualified ids. |
required |
|
TimeIndex
|
Time index. |
required |
|
Index | None
|
Period index for multi-period models. |
None
|
|
list[tuple[str, Status, list[str]]] | None
|
Component-level status entries as
|
None
|
Source code in src/fluxopt/model_data.py
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 | |
CarriersData
dataclass
¶
Which carrier each flow balances on, and what a carrier is called.
Two frames because they are two things: membership is per flow,
carriers is per carrier. A flow is on exactly one carrier, so
membership is a column rather than the (carrier, flow) matrix it used
to be.
Methods:
| Name | Description |
|---|---|
__post_init__ |
Check the signs, and that every carrier named is declared. |
build |
Build CarriersData from explicit carrier declarations. |
Attributes:
| Name | Type | Description |
|---|---|---|
ids |
list[str]
|
The declared carriers, in declaration order. |
__post_init__
¶
Check the signs, and that every carrier named is declared.
Layer 3 — see docs/design/validation-layers.md.
Source code in src/fluxopt/model_data.py
build
classmethod
¶
Build CarriersData from explicit carrier declarations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[Carrier]
|
Declared carriers. |
required |
|
list[_BoundFlow]
|
All collected flows. |
required |
|
dict[str, float]
|
Mapping of qualified flow id to +1 (produces) or -1 (consumes). |
required |
Source code in src/fluxopt/model_data.py
ConvertersData
dataclass
¶
Linear conversion equations, as the coefficients they actually have.
A converter states some equations; each names some of its flows with a coefficient. That is a table, and only the non-zero entries are in it — a flow a given equation does not mention has no row rather than a zero.
Methods:
| Name | Description |
|---|---|
__post_init__ |
Check the counts are positive and the rows name converters we carry. |
build |
Build ConvertersData from the equations each converter states. |
Attributes:
| Name | Type | Description |
|---|---|---|
ids |
list[str]
|
The converters stating linear equations, in declaration order. |
width |
int
|
The most equations any one converter states. |
__post_init__
¶
Check the counts are positive and the rows name converters we carry.
Layer 3 — see docs/design/validation-layers.md.
Source code in src/fluxopt/model_data.py
build
classmethod
¶
build(converters: list[Converter], time: TimeIndex) -> Self | None
Build ConvertersData from the equations each converter states.
Only linear converters are included; piecewise converters
(conversion is not None) live in :class:PiecewiseData.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[Converter]
|
Converter definitions. |
required |
|
TimeIndex
|
Time index. |
required |
Source code in src/fluxopt/model_data.py
PiecewiseData
dataclass
¶
Piecewise-linear conversion curves, as the breakpoints they name.
Two frames: curves is per converter — the method, whether a Status
gates it, and the availability series — and links is per (flow,
breakpoint), which is what a curve is.
Methods:
| Name | Description |
|---|---|
converter_ids |
Piecewise converter ids, in declaration order. |
__post_init__ |
Re-check what |
build |
Build PiecewiseData from converters with |
converter_ids
¶
__post_init__
¶
Re-check what PiecewiseConversion already refuses, for a reloaded file.
A reload guard, not the enforcement: method is a Literal on the
element and bound is checked when the curve is constructed. See
docs/design/validation-layers.md.
Source code in src/fluxopt/model_data.py
build
classmethod
¶
build(converters: list[Converter], time: TimeIndex) -> Self | None
Build PiecewiseData from converters with PiecewiseConversion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[Converter]
|
Converter definitions; only those with
|
required |
|
TimeIndex
|
Time index for breakpoint and availability series. |
required |
Source code in src/fluxopt/model_data.py
983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | |
EffectsData
dataclass
¶
EffectsData(
effects: DataFrame,
totals: DataFrame,
periodic: DataFrame,
contributions: DataFrame,
period_weights: DataFrame,
)
What each effect is bounded by, and how effects charge one another.
Bounds are optional, so an effect that names none has no row rather than a row of NaN. Cross-effect factors are the ones declared: the square matrix they imply is 67 MB at 2% live on the stress reference system, and most of that is not sparsity but a scalar repeated across every timestep.
Methods:
| Name | Description |
|---|---|
cf_matrix |
The cross-effect factors as a square matrix, or None if there are none. |
__post_init__ |
Reject self-references and cycles in the cross-effect graph. |
build |
Build EffectsData from element objects. |
Attributes:
| Name | Type | Description |
|---|---|---|
ids |
list[str]
|
The declared effects, in declaration order. |
cf_matrix
¶
The cross-effect factors as a square matrix, or None if there are none.
Built here rather than stored, because inverting it is the only thing
that wants it square, and a stored one costs a full
(effect, source_effect, time, period) grid to hold a handful of
declared factors. Transient by design: nothing keeps the result.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[Any] | None
|
Labels for the period axis. The frame indexes periods by position, which is what the binder wants and what every table it is contracted against carries. A caller comparing against a solution wants the labels the element layer used instead, and xarray aligns on coordinate values rather than reporting a mismatch — so passing the wrong one matches nothing, silently. |
None
|
Source code in src/fluxopt/model_data.py
__post_init__
¶
Reject self-references and cycles in the cross-effect graph.
Layer 3 — see docs/design/validation-layers.md.
Source code in src/fluxopt/model_data.py
build
classmethod
¶
Build EffectsData from element objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[Effect]
|
Effect definitions. |
required |
|
TimeIndex
|
Time index. |
required |
|
Index | None
|
Period index (multi-period only). |
None
|
Source code in src/fluxopt/model_data.py
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 | |
StoragesData
dataclass
¶
StoragesData(
storages: DataFrame,
capacity: DataFrame,
profiles: DataFrame,
levels: DataFrame,
sizing: SizingData | None = None,
invest: InvestmentData | None = None,
)
Storage parameters, as the four tables a storage declares.
Absence is a missing row. A storage whose capacity is being optimized has
no row in capacity; one that constrains neither its starting nor its
final level has no row in levels. profiles is the exception and is
dense on purpose: a missing coefficient row reads as a zero, and a
retention of zero empties the store rather than leaving it alone.
Methods:
| Name | Description |
|---|---|
__post_init__ |
Re-check the ranges |
build |
Build StoragesData from element objects. |
Attributes:
| Name | Type | Description |
|---|---|---|
ids |
list[str]
|
The declared storages, in declaration order. |
__post_init__
¶
Re-check the ranges Storage already refuses, on the resolved values.
Two things reach here that the element could not see: a reloaded
file, which never passed through Storage at all, and a
ProfileRef, whose numbers arrive when profiles are resolved and so
are not there to check when the storage is written. Everything else
was refused at construction — see docs/design/validation-layers.md.
Source code in src/fluxopt/model_data.py
build
classmethod
¶
Build StoragesData from element objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
list[Storage]
|
Storage definitions. |
required |
|
TimeIndex
|
Time index. |
required |
|
Index | None
|
Period index for period-varying effects. |
None
|
Source code in src/fluxopt/model_data.py
1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 | |
Dims
dataclass
¶
The model's temporal axes: what a timestep is, and what a period is.
Two tables, indexed by position like every other container. The labels the user gave ride along as a column, because they are what a result is reported against and nothing else keys on them.
Absence is a missing table: a system with no investment periods has an
empty periods, which is what has_periods reads.
Methods:
| Name | Description |
|---|---|
coords |
Return shared coordinates for variable/DataArray creation. |
build |
Build Dims from a time index and optional periods. |
Attributes:
| Name | Type | Description |
|---|---|---|
has_periods |
bool
|
Whether the system declares an investment period axis. |
n_time |
int
|
How many timesteps the horizon has. |
n_periods |
int
|
How many periods the program indexes — one, when none are declared. |
period_labels |
list[Any]
|
The period labels, or |
time |
DataArray
|
Timestep labels as a coordinate. |
dt |
DataArray
|
Timestep durations [h] as a time-indexed array. |
weights |
DataArray
|
Timestep weights as a time-indexed array. |
period |
DataArray | None
|
Period labels as a coordinate, or None when there is no period axis. |
period_weights |
DataArray | None
|
Period duration weights, or None when there is no period axis. |
n_periods
property
¶
n_periods: int
How many periods the program indexes — one, when none are declared.
period_labels
property
¶
The period labels, or [0] when the system declares no periods.
period
property
¶
period: DataArray | None
Period labels as a coordinate, or None when there is no period axis.
period_weights
property
¶
period_weights: DataArray | None
Period duration weights, or None when there is no period axis.
coords
¶
Return shared coordinates for variable/DataArray creation.
Also the single point of truth for the model's variate dims used by
:func:fluxopt.types.as_dataarray: pick the reach a field supports
(e.g. coords(time=True, period=True) for operational profiles,
coords(period=True) for investment-time fields). When a new
variate dim (e.g. scenario) is added, extend this method once
and every call site picks it up.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
bool
|
Include the time coordinate. |
False
|
|
bool
|
Include the period coordinate (no-op in single-period mode). |
False
|
Source code in src/fluxopt/model_data.py
build
classmethod
¶
build(
time: TimeIndex,
dt: DataArray,
periods: list[int] | Index | None = None,
period_weights: list[float] | None = None,
) -> Self
Build Dims from a time index and optional periods.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
TimeIndex
|
Normalized time index. |
required |
|
DataArray
|
Timestep durations. |
required |
|
list[int] | Index | None
|
Integer period labels for multi-period optimization. |
None
|
|
list[float] | None
|
Explicit weights per period. Inferred from gaps if None. |
None
|
Source code in src/fluxopt/model_data.py
ModelData
dataclass
¶
ModelData(
flows: FlowsData,
carriers: CarriersData,
converters: ConvertersData | None,
effects: EffectsData,
storages: StoragesData | None,
dims: Dims,
piecewise: PiecewiseData | None = None,
status: StatusData | None = None,
)
Methods:
| Name | Description |
|---|---|
__post_init__ |
Check ids referenced between tables resolve. |
save |
Write the model data as a directory of tables. |
load |
Read model data written by :meth: |
build |
Build ModelData from element objects. |
__post_init__
¶
Check ids referenced between tables resolve.
Layer 3, and the clearest case for it: no single table can answer this, and a reloaded file never passed the system layer. See docs/design/validation-layers.md.
Each table validates itself in its own __post_init__; this checks
that ids referenced between tables resolve, so a tampered or
hand-edited file fails here instead of as a KeyError deep in
model building.
Source code in src/fluxopt/model_data.py
save
¶
Write the model data as a directory of tables.
One parquet file per frame, in a directory per container. Parquet because these are tables: it carries the schema, so a column with no rows still knows it holds strings, and an empty frame reloads as the same empty frame rather than as something that lost its dtypes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str | Path
|
Directory to write into. Created if absent. |
required |
Source code in src/fluxopt/model_data.py
load
classmethod
¶
Read model data written by :meth:save.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str | Path
|
The directory :meth: |
required |
Raises:
| Type | Description |
|---|---|
OSError
|
If the directory holds no fluxopt model data. |
Source code in src/fluxopt/model_data.py
build
classmethod
¶
build(
timesteps: Timesteps,
carriers: list[Carrier],
effects: list[Effect],
ports: list[Port],
converters: list[Converter] | None = None,
storages: list[Storage] | None = None,
dt: float | list[float] | None = None,
periods: list[int] | Index | None = None,
period_weights: list[float] | None = None,
) -> Self
Build ModelData from element objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Timesteps
|
Time index for the optimization horizon. |
required |
|
list[Carrier]
|
Carrier declarations. |
required |
|
list[Effect]
|
Effects to track. |
required |
|
list[Port]
|
System boundary ports. |
required |
|
list[Converter] | None
|
Linear converters. |
None
|
|
list[Storage] | None
|
Energy storages. |
None
|
|
float | list[float] | None
|
Timestep duration in hours. Auto-derived if None. |
None
|
|
list[int] | Index | None
|
Integer period labels for multi-period optimization. |
None
|
|
list[float] | None
|
Explicit weights per period. Inferred from gaps if None. |
None
|
Source code in src/fluxopt/model_data.py
1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 | |
compute_previous_duration
¶
compute_previous_duration(
previous_status: DataArray, target_state: int, dt: DataArray | float
) -> float
Compute consecutive duration of target_state at end of previous_status.
Walks backward through previous_status counting timesteps that match the target state, then multiplies by timestep duration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
DataArray
|
Previous status values (time dimension). |
required |
|
int
|
1 for active (uptime), 0 for inactive (downtime). |
required |
|
DataArray | float
|
Duration per timestep (scalar or DataArray). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Total duration in target state at end of previous period. |