loadcast 0.1.1__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.
- loadcast/__init__.py +94 -0
- loadcast/classes.py +187 -0
- loadcast/data/__init__.py +5 -0
- loadcast/data/process_classes.json +242 -0
- loadcast/data/reference.json +158 -0
- loadcast/fitting.py +303 -0
- loadcast/generator.py +406 -0
- loadcast/reference.py +175 -0
- loadcast/spec.py +243 -0
- loadcast/stats.py +126 -0
- loadcast-0.1.1.dist-info/METADATA +204 -0
- loadcast-0.1.1.dist-info/RECORD +15 -0
- loadcast-0.1.1.dist-info/WHEEL +5 -0
- loadcast-0.1.1.dist-info/licenses/LICENSE +21 -0
- loadcast-0.1.1.dist-info/top_level.txt +1 -0
loadcast/__init__.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""loadcast -- synthetic multi-carrier industrial energy demand.
|
|
2
|
+
|
|
3
|
+
Hourly heat, cold and electricity demand for an industrial site, from the
|
|
4
|
+
handful of numbers someone actually knows about a plant: how much energy it uses
|
|
5
|
+
in a year, roughly how much more heat than cold it wants, how much of the
|
|
6
|
+
total is electricity, and whether the process runs continuously, in shifts or in
|
|
7
|
+
batches.
|
|
8
|
+
|
|
9
|
+
import loadcast
|
|
10
|
+
|
|
11
|
+
profiles = loadcast.generate(
|
|
12
|
+
annual_mwh=10_000,
|
|
13
|
+
heat_to_cold=7.4,
|
|
14
|
+
elec_share=0.20,
|
|
15
|
+
process_class="semi_continuous",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
What makes it different
|
|
19
|
+
-----------------------
|
|
20
|
+
Existing tools either produce deterministic profiles or leave the relationship
|
|
21
|
+
between carriers to fall out of whatever drives them. Both give carriers that
|
|
22
|
+
move in lockstep, which is an artefact of the construction rather than a
|
|
23
|
+
property of industry -- and for anything with storage, *whether the carriers
|
|
24
|
+
peak together* is precisely what decides how much buffering is needed.
|
|
25
|
+
|
|
26
|
+
loadcast makes that relationship an explicit input. `carrier_correlation` sets
|
|
27
|
+
how tightly the carriers move together and `carrier_lag_h` sets how far apart
|
|
28
|
+
they are offset, so a study can sweep coincidence rather than inherit it.
|
|
29
|
+
|
|
30
|
+
What it does not do
|
|
31
|
+
-------------------
|
|
32
|
+
It carries energy by carrier, not temperature: whether a machine can *reach* a
|
|
33
|
+
given process is a separate question, answered from the machine's own limits.
|
|
34
|
+
And the parameters are yours to choose. Defaults are indicative values with a
|
|
35
|
+
stated provenance, not predictions about any real site -- results are meant to
|
|
36
|
+
be reported as they hold across a swept space, not at a single assumed point.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
from .classes import (
|
|
42
|
+
ProcessClass,
|
|
43
|
+
available_classes,
|
|
44
|
+
get_class,
|
|
45
|
+
load_classes,
|
|
46
|
+
register_class,
|
|
47
|
+
)
|
|
48
|
+
from .fitting import (
|
|
49
|
+
bias_report,
|
|
50
|
+
class_from_observations,
|
|
51
|
+
correlation_floor,
|
|
52
|
+
correlation_transfer,
|
|
53
|
+
phi_from_lag1,
|
|
54
|
+
sigma_from_cv,
|
|
55
|
+
)
|
|
56
|
+
from . import reference
|
|
57
|
+
from .generator import (
|
|
58
|
+
build_profiles,
|
|
59
|
+
generate,
|
|
60
|
+
rescale_persistence,
|
|
61
|
+
solve_correlation_knob,
|
|
62
|
+
step_hours,
|
|
63
|
+
)
|
|
64
|
+
from .spec import CARRIERS, DemandSpec
|
|
65
|
+
from .stats import (
|
|
66
|
+
cross_correlogram,
|
|
67
|
+
duration_curve,
|
|
68
|
+
integrated_drift,
|
|
69
|
+
profile_statistics,
|
|
70
|
+
summary_table,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
__version__ = "0.1.1"
|
|
74
|
+
|
|
75
|
+
__all__ = [
|
|
76
|
+
"__version__",
|
|
77
|
+
# what to generate
|
|
78
|
+
"DemandSpec", "CARRIERS",
|
|
79
|
+
# generating
|
|
80
|
+
"generate", "build_profiles", "solve_correlation_knob",
|
|
81
|
+
"step_hours", "rescale_persistence",
|
|
82
|
+
# process classes
|
|
83
|
+
"ProcessClass", "available_classes", "get_class", "load_classes",
|
|
84
|
+
"register_class",
|
|
85
|
+
# fitting to your own measurements
|
|
86
|
+
"class_from_observations", "bias_report",
|
|
87
|
+
"sigma_from_cv", "phi_from_lag1",
|
|
88
|
+
"correlation_floor", "correlation_transfer",
|
|
89
|
+
# diagnostics
|
|
90
|
+
# indicative published values
|
|
91
|
+
"reference",
|
|
92
|
+
"profile_statistics", "summary_table", "duration_curve",
|
|
93
|
+
"cross_correlogram", "integrated_drift",
|
|
94
|
+
]
|
loadcast/classes.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""Process classes: how peaky an industrial process is, and on what rhythm.
|
|
2
|
+
|
|
3
|
+
A process class bundles everything the generator needs that is *not* specific to
|
|
4
|
+
one site: the calendar rhythm (month, day-of-week, hour-of-day factors) and the
|
|
5
|
+
three statistics that describe how the load fluctuates around it (load factor,
|
|
6
|
+
coefficient of variation, lag-1 persistence), plus an on/off description for
|
|
7
|
+
processes that are genuinely intermittent rather than merely variable.
|
|
8
|
+
|
|
9
|
+
Each class is calibrated on one metered industrial site. The sites themselves
|
|
10
|
+
are not distributed -- what ships is the derived statistics and the calendar
|
|
11
|
+
factors, which are group means and carry no identifying information.
|
|
12
|
+
|
|
13
|
+
The four classes span the range the archive covers:
|
|
14
|
+
|
|
15
|
+
continuous runs around the clock, small excursions
|
|
16
|
+
semi_continuous weekday shift pattern with a weekend trough
|
|
17
|
+
campaign long runs separated by genuine shutdowns
|
|
18
|
+
batch strongly bimodal, most hours near zero
|
|
19
|
+
|
|
20
|
+
Choose one from the *process description* of the site you are modelling, not by
|
|
21
|
+
fitting to an answer you already have. If none fits, `loadcast.fitting` builds a
|
|
22
|
+
class from your own observed statistics.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import json
|
|
28
|
+
from dataclasses import dataclass
|
|
29
|
+
from functools import lru_cache
|
|
30
|
+
from importlib import resources
|
|
31
|
+
|
|
32
|
+
import numpy as np
|
|
33
|
+
|
|
34
|
+
__all__ = ["ProcessClass", "available_classes", "get_class", "load_classes",
|
|
35
|
+
"register_class"]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class ProcessClass:
|
|
40
|
+
"""One calibrated process rhythm.
|
|
41
|
+
|
|
42
|
+
Attributes
|
|
43
|
+
----------
|
|
44
|
+
name
|
|
45
|
+
Identifier used in `DemandSpec.process_class`.
|
|
46
|
+
month, dayofweek, hour
|
|
47
|
+
Calendar factors, each normalised to mean one over a balanced calendar.
|
|
48
|
+
`month` is indexed January..December, `dayofweek` Monday..Sunday, `hour`
|
|
49
|
+
00..23. They carry *shape only*: the level comes from the spec.
|
|
50
|
+
target_load_factor
|
|
51
|
+
Mean over peak of the finished profile. Imposed exactly by the capacity
|
|
52
|
+
cap, not fitted.
|
|
53
|
+
target_cv
|
|
54
|
+
Coefficient of variation of the finished profile, calendar included.
|
|
55
|
+
target_lag1
|
|
56
|
+
Lag-1 autocorrelation of the finished profile.
|
|
57
|
+
mode
|
|
58
|
+
``"lognormal"`` for a process that varies continuously, ``"onoff"`` for
|
|
59
|
+
one that genuinely stops. The choice is forced by the data: a lognormal
|
|
60
|
+
with the right spread still puts a batch process's load factor at 0.02
|
|
61
|
+
against 0.22 measured, because a lognormal tail is far longer than an
|
|
62
|
+
on/off process at equal spread.
|
|
63
|
+
off_fraction
|
|
64
|
+
Share of hours in the off state. Zero for ``"lognormal"`` classes.
|
|
65
|
+
off_level
|
|
66
|
+
Load in the off state, as a fraction of the running level. Non-zero
|
|
67
|
+
because real plants idle rather than stop.
|
|
68
|
+
description
|
|
69
|
+
The kind of process this rhythm was measured on.
|
|
70
|
+
anchor_site
|
|
71
|
+
Anonymised label of the metered site it was calibrated against.
|
|
72
|
+
n_hours
|
|
73
|
+
Length of the series it was calibrated on.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
name: str
|
|
77
|
+
month: tuple[float, ...]
|
|
78
|
+
dayofweek: tuple[float, ...]
|
|
79
|
+
hour: tuple[float, ...]
|
|
80
|
+
target_load_factor: float
|
|
81
|
+
target_cv: float
|
|
82
|
+
target_lag1: float
|
|
83
|
+
mode: str
|
|
84
|
+
off_fraction: float
|
|
85
|
+
off_level: float
|
|
86
|
+
description: str
|
|
87
|
+
anchor_site: str
|
|
88
|
+
n_hours: int
|
|
89
|
+
|
|
90
|
+
def __post_init__(self) -> None:
|
|
91
|
+
if self.mode not in ("lognormal", "onoff"):
|
|
92
|
+
raise ValueError(f"unknown mode {self.mode!r}")
|
|
93
|
+
for field, expected in (("month", 12), ("dayofweek", 7), ("hour", 24)):
|
|
94
|
+
got = len(getattr(self, field))
|
|
95
|
+
if got != expected:
|
|
96
|
+
raise ValueError(
|
|
97
|
+
f"{self.name}: {field} needs {expected} factors, got {got}"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def calendar(self, index) -> np.ndarray:
|
|
101
|
+
"""Calendar factor `S(t)` for every timestamp in `index`.
|
|
102
|
+
|
|
103
|
+
The three periodic factors multiply. Each has mean one over a balanced
|
|
104
|
+
calendar, so their product carries shape and no level -- which is what
|
|
105
|
+
lets the same rhythm be reused at any plant size.
|
|
106
|
+
"""
|
|
107
|
+
month = np.asarray(self.month, dtype=float)
|
|
108
|
+
dayofweek = np.asarray(self.dayofweek, dtype=float)
|
|
109
|
+
hour = np.asarray(self.hour, dtype=float)
|
|
110
|
+
return (month[index.month - 1]
|
|
111
|
+
* dayofweek[index.dayofweek]
|
|
112
|
+
* hour[index.hour])
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@lru_cache(maxsize=1)
|
|
116
|
+
def load_classes() -> dict[str, ProcessClass]:
|
|
117
|
+
"""Every calibrated process class shipped with the package."""
|
|
118
|
+
source = resources.files("loadcast.data").joinpath("process_classes.json")
|
|
119
|
+
raw = json.loads(source.read_text(encoding="utf-8"))
|
|
120
|
+
|
|
121
|
+
classes: dict[str, ProcessClass] = {}
|
|
122
|
+
for name, entry in raw.items():
|
|
123
|
+
classes[name] = ProcessClass(
|
|
124
|
+
name=name,
|
|
125
|
+
month=tuple(entry["month"]),
|
|
126
|
+
dayofweek=tuple(entry["dayofweek"]),
|
|
127
|
+
hour=tuple(entry["hour"]),
|
|
128
|
+
target_load_factor=float(entry["target_load_factor"]),
|
|
129
|
+
target_cv=float(entry["target_cv"]),
|
|
130
|
+
target_lag1=float(entry["target_lag1"]),
|
|
131
|
+
mode=str(entry["mode"]),
|
|
132
|
+
off_fraction=float(entry["off_fraction"]),
|
|
133
|
+
off_level=float(entry["off_level"]),
|
|
134
|
+
description=str(entry["description"]),
|
|
135
|
+
anchor_site=str(entry["anchor_site"]),
|
|
136
|
+
n_hours=int(entry["n_hours"]),
|
|
137
|
+
)
|
|
138
|
+
return classes
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# Classes built at runtime from a user's own measurements. Kept apart from the
|
|
142
|
+
# shipped ones so `available_classes` can say which is which, and so nothing
|
|
143
|
+
# a user registers can silently shadow a calibrated class.
|
|
144
|
+
_REGISTERED: dict[str, ProcessClass] = {}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def register_class(process_class: ProcessClass, overwrite: bool = False) -> None:
|
|
148
|
+
"""Make a process class usable by name in a `DemandSpec`.
|
|
149
|
+
|
|
150
|
+
Pair this with `loadcast.fitting.class_from_observations` to generate from
|
|
151
|
+
statistics you measured yourself:
|
|
152
|
+
|
|
153
|
+
own = class_from_observations("my_plant", 0.45, 0.6, 0.9)
|
|
154
|
+
register_class(own)
|
|
155
|
+
generate(process_class="my_plant", annual_mwh=5_000)
|
|
156
|
+
"""
|
|
157
|
+
name = process_class.name
|
|
158
|
+
if name in load_classes():
|
|
159
|
+
raise ValueError(
|
|
160
|
+
f"{name!r} is a calibrated class shipped with loadcast; "
|
|
161
|
+
f"choose another name rather than shadowing it"
|
|
162
|
+
)
|
|
163
|
+
if name in _REGISTERED and not overwrite:
|
|
164
|
+
raise ValueError(f"{name!r} is already registered; pass overwrite=True")
|
|
165
|
+
_REGISTERED[name] = process_class
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def available_classes(include_registered: bool = True) -> list[str]:
|
|
169
|
+
"""Names of the process classes that can be generated from."""
|
|
170
|
+
names = set(load_classes())
|
|
171
|
+
if include_registered:
|
|
172
|
+
names |= set(_REGISTERED)
|
|
173
|
+
return sorted(names)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def get_class(name: str) -> ProcessClass:
|
|
177
|
+
"""One process class by name, with a helpful error if it does not exist."""
|
|
178
|
+
classes = load_classes()
|
|
179
|
+
if name in classes:
|
|
180
|
+
return classes[name]
|
|
181
|
+
if name in _REGISTERED:
|
|
182
|
+
return _REGISTERED[name]
|
|
183
|
+
raise KeyError(
|
|
184
|
+
f"unknown process class {name!r}; available: {available_classes()}. "
|
|
185
|
+
f"To build one from your own measurements, see "
|
|
186
|
+
f"loadcast.fitting.class_from_observations and register_class."
|
|
187
|
+
)
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
{
|
|
2
|
+
"batch": {
|
|
3
|
+
"anchor_site": "site_c",
|
|
4
|
+
"dayofweek": [
|
|
5
|
+
1.55929,
|
|
6
|
+
1.50644,
|
|
7
|
+
1.234482,
|
|
8
|
+
1.053401,
|
|
9
|
+
1.217668,
|
|
10
|
+
0.25477,
|
|
11
|
+
0.169895
|
|
12
|
+
],
|
|
13
|
+
"description": "long on/off runs; coffee roasting",
|
|
14
|
+
"hour": [
|
|
15
|
+
0.154848,
|
|
16
|
+
0.154795,
|
|
17
|
+
0.158523,
|
|
18
|
+
0.157162,
|
|
19
|
+
0.281185,
|
|
20
|
+
1.229629,
|
|
21
|
+
1.857477,
|
|
22
|
+
1.894545,
|
|
23
|
+
1.853708,
|
|
24
|
+
1.773206,
|
|
25
|
+
1.719132,
|
|
26
|
+
1.632496,
|
|
27
|
+
1.446623,
|
|
28
|
+
1.373535,
|
|
29
|
+
1.369311,
|
|
30
|
+
1.322143,
|
|
31
|
+
1.253803,
|
|
32
|
+
1.170113,
|
|
33
|
+
1.100534,
|
|
34
|
+
1.024897,
|
|
35
|
+
0.553287,
|
|
36
|
+
0.210613,
|
|
37
|
+
0.154494,
|
|
38
|
+
0.15394
|
|
39
|
+
],
|
|
40
|
+
"mode": "onoff",
|
|
41
|
+
"month": [
|
|
42
|
+
1.135009,
|
|
43
|
+
1.011868,
|
|
44
|
+
0.962326,
|
|
45
|
+
0.930036,
|
|
46
|
+
0.965991,
|
|
47
|
+
1.067641,
|
|
48
|
+
0.445614,
|
|
49
|
+
0.913278,
|
|
50
|
+
0.995257,
|
|
51
|
+
1.142671,
|
|
52
|
+
1.422581,
|
|
53
|
+
1.022282
|
|
54
|
+
],
|
|
55
|
+
"n_hours": 8760,
|
|
56
|
+
"off_fraction": 0.4,
|
|
57
|
+
"off_level": 0.02,
|
|
58
|
+
"target_cv": 1.177,
|
|
59
|
+
"target_lag1": 0.848,
|
|
60
|
+
"target_load_factor": 0.221
|
|
61
|
+
},
|
|
62
|
+
"campaign": {
|
|
63
|
+
"anchor_site": "site_d",
|
|
64
|
+
"dayofweek": [
|
|
65
|
+
0.954028,
|
|
66
|
+
0.960068,
|
|
67
|
+
0.975323,
|
|
68
|
+
1.033885,
|
|
69
|
+
1.014871,
|
|
70
|
+
1.045056,
|
|
71
|
+
1.017728
|
|
72
|
+
],
|
|
73
|
+
"description": "long campaigns on and off; phosphate burner",
|
|
74
|
+
"hour": [
|
|
75
|
+
1.032559,
|
|
76
|
+
1.035685,
|
|
77
|
+
1.036415,
|
|
78
|
+
1.037355,
|
|
79
|
+
1.039047,
|
|
80
|
+
1.029501,
|
|
81
|
+
1.026594,
|
|
82
|
+
1.001638,
|
|
83
|
+
0.886791,
|
|
84
|
+
0.898781,
|
|
85
|
+
0.966302,
|
|
86
|
+
0.990871,
|
|
87
|
+
0.988767,
|
|
88
|
+
0.98998,
|
|
89
|
+
0.992691,
|
|
90
|
+
0.998371,
|
|
91
|
+
0.996536,
|
|
92
|
+
0.997509,
|
|
93
|
+
0.99553,
|
|
94
|
+
0.998669,
|
|
95
|
+
1.004324,
|
|
96
|
+
1.011664,
|
|
97
|
+
1.016607,
|
|
98
|
+
1.027876
|
|
99
|
+
],
|
|
100
|
+
"mode": "onoff",
|
|
101
|
+
"month": [
|
|
102
|
+
1.101382,
|
|
103
|
+
1.153565,
|
|
104
|
+
1.260548,
|
|
105
|
+
1.025009,
|
|
106
|
+
1.080069,
|
|
107
|
+
0.909929,
|
|
108
|
+
0.907351,
|
|
109
|
+
0.907123,
|
|
110
|
+
1.051813,
|
|
111
|
+
0.838124,
|
|
112
|
+
0.765126,
|
|
113
|
+
0.928618
|
|
114
|
+
],
|
|
115
|
+
"n_hours": 16535,
|
|
116
|
+
"off_fraction": 0.196,
|
|
117
|
+
"off_level": 0.02,
|
|
118
|
+
"target_cv": 0.527,
|
|
119
|
+
"target_lag1": 0.957,
|
|
120
|
+
"target_load_factor": 0.503
|
|
121
|
+
},
|
|
122
|
+
"continuous": {
|
|
123
|
+
"anchor_site": "site_a",
|
|
124
|
+
"dayofweek": [
|
|
125
|
+
0.883038,
|
|
126
|
+
1.201896,
|
|
127
|
+
1.195369,
|
|
128
|
+
1.191803,
|
|
129
|
+
1.105632,
|
|
130
|
+
0.722996,
|
|
131
|
+
0.697166
|
|
132
|
+
],
|
|
133
|
+
"description": "runs around the clock; pasta drying, mineral drying",
|
|
134
|
+
"hour": [
|
|
135
|
+
0.990317,
|
|
136
|
+
0.993397,
|
|
137
|
+
0.993791,
|
|
138
|
+
0.962328,
|
|
139
|
+
0.95992,
|
|
140
|
+
0.962603,
|
|
141
|
+
0.969056,
|
|
142
|
+
0.956698,
|
|
143
|
+
0.932786,
|
|
144
|
+
0.949027,
|
|
145
|
+
0.992933,
|
|
146
|
+
1.005735,
|
|
147
|
+
1.008749,
|
|
148
|
+
1.024708,
|
|
149
|
+
1.023066,
|
|
150
|
+
1.016832,
|
|
151
|
+
0.992313,
|
|
152
|
+
1.014779,
|
|
153
|
+
1.047381,
|
|
154
|
+
1.04819,
|
|
155
|
+
1.037668,
|
|
156
|
+
1.052113,
|
|
157
|
+
1.046296,
|
|
158
|
+
1.019314
|
|
159
|
+
],
|
|
160
|
+
"mode": "lognormal",
|
|
161
|
+
"month": [
|
|
162
|
+
1.165743,
|
|
163
|
+
1.129872,
|
|
164
|
+
1.088858,
|
|
165
|
+
1.09592,
|
|
166
|
+
0.950285,
|
|
167
|
+
1.109809,
|
|
168
|
+
0.733752,
|
|
169
|
+
0.930212,
|
|
170
|
+
0.989356,
|
|
171
|
+
1.045419,
|
|
172
|
+
0.880077,
|
|
173
|
+
0.895691
|
|
174
|
+
],
|
|
175
|
+
"n_hours": 8760,
|
|
176
|
+
"off_fraction": 0.0,
|
|
177
|
+
"off_level": 0.0,
|
|
178
|
+
"target_cv": 0.409,
|
|
179
|
+
"target_lag1": 0.973,
|
|
180
|
+
"target_load_factor": 0.576
|
|
181
|
+
},
|
|
182
|
+
"semi_continuous": {
|
|
183
|
+
"anchor_site": "site_b",
|
|
184
|
+
"dayofweek": [
|
|
185
|
+
1.236234,
|
|
186
|
+
1.256554,
|
|
187
|
+
1.231145,
|
|
188
|
+
1.190143,
|
|
189
|
+
0.962295,
|
|
190
|
+
0.442463,
|
|
191
|
+
0.681708
|
|
192
|
+
],
|
|
193
|
+
"description": "shifts with weekend shutdown; brewing, phosphate burners",
|
|
194
|
+
"hour": [
|
|
195
|
+
1.004733,
|
|
196
|
+
0.954934,
|
|
197
|
+
0.943026,
|
|
198
|
+
0.953191,
|
|
199
|
+
1.027009,
|
|
200
|
+
1.047198,
|
|
201
|
+
1.039582,
|
|
202
|
+
1.032001,
|
|
203
|
+
1.000225,
|
|
204
|
+
1.042484,
|
|
205
|
+
1.040865,
|
|
206
|
+
1.050062,
|
|
207
|
+
1.035649,
|
|
208
|
+
1.015254,
|
|
209
|
+
1.021087,
|
|
210
|
+
1.038695,
|
|
211
|
+
1.022148,
|
|
212
|
+
0.977781,
|
|
213
|
+
0.973244,
|
|
214
|
+
0.96839,
|
|
215
|
+
0.93851,
|
|
216
|
+
0.917547,
|
|
217
|
+
0.950893,
|
|
218
|
+
1.005491
|
|
219
|
+
],
|
|
220
|
+
"mode": "lognormal",
|
|
221
|
+
"month": [
|
|
222
|
+
0.983435,
|
|
223
|
+
1.034075,
|
|
224
|
+
1.068202,
|
|
225
|
+
1.160898,
|
|
226
|
+
0.98848,
|
|
227
|
+
0.973074,
|
|
228
|
+
0.929518,
|
|
229
|
+
1.093648,
|
|
230
|
+
0.843058,
|
|
231
|
+
0.864036,
|
|
232
|
+
0.983501,
|
|
233
|
+
1.080097
|
|
234
|
+
],
|
|
235
|
+
"n_hours": 8760,
|
|
236
|
+
"off_fraction": 0.0,
|
|
237
|
+
"off_level": 0.0,
|
|
238
|
+
"target_cv": 0.478,
|
|
239
|
+
"target_lag1": 0.793,
|
|
240
|
+
"target_load_factor": 0.401
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compositions": {
|
|
3
|
+
"_source": "jericho",
|
|
4
|
+
"industry_aggregate": {
|
|
5
|
+
"cold": 0.049,
|
|
6
|
+
"elec": 0.213,
|
|
7
|
+
"heat": 0.738,
|
|
8
|
+
"heat_to_cold": 15.0
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"sector_temperatures": {
|
|
12
|
+
"Chemical": {
|
|
13
|
+
"process_median_c": 109.6,
|
|
14
|
+
"process_p25_c": 71.0,
|
|
15
|
+
"process_p75_c": 129.0,
|
|
16
|
+
"process_pj_15_200": 354.6,
|
|
17
|
+
"waste_median_c": 52.1,
|
|
18
|
+
"waste_pj_15_200": 337.1,
|
|
19
|
+
"waste_to_process": 0.95
|
|
20
|
+
},
|
|
21
|
+
"Food and Beverage": {
|
|
22
|
+
"process_median_c": 120.9,
|
|
23
|
+
"process_p25_c": 105.5,
|
|
24
|
+
"process_p75_c": 180.0,
|
|
25
|
+
"process_pj_15_200": 193.1,
|
|
26
|
+
"waste_median_c": 57.0,
|
|
27
|
+
"waste_pj_15_200": 97.2,
|
|
28
|
+
"waste_to_process": 0.5
|
|
29
|
+
},
|
|
30
|
+
"Paper": {
|
|
31
|
+
"process_median_c": 140.0,
|
|
32
|
+
"process_p25_c": 125.0,
|
|
33
|
+
"process_p75_c": 157.2,
|
|
34
|
+
"process_pj_15_200": 356.5,
|
|
35
|
+
"waste_median_c": 45.0,
|
|
36
|
+
"waste_pj_15_200": 230.9,
|
|
37
|
+
"waste_to_process": 0.65
|
|
38
|
+
},
|
|
39
|
+
"Refinery": {
|
|
40
|
+
"process_median_c": 275.3,
|
|
41
|
+
"process_p25_c": 173.4,
|
|
42
|
+
"process_p75_c": 309.0,
|
|
43
|
+
"process_pj_15_200": 219.1,
|
|
44
|
+
"waste_median_c": 89.5,
|
|
45
|
+
"waste_pj_15_200": 464.9,
|
|
46
|
+
"waste_to_process": 2.12
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"sources": {
|
|
50
|
+
"jericho": {
|
|
51
|
+
"caveat": "Annual shares only. Its hourly series cannot be used: carriers are a fixed scalar times one shared profile, so their correlation is 1.000000 and the composition is identical across all 38 regions to 4e-8 while regional scale varies 17.9x.",
|
|
52
|
+
"citation": "Priesmann J, Nolting L, Kockel C, Praktiknjo A. Time series of useful energy consumption patterns for energy system modeling (JERICHO-E-usage). Scientific Data 8:148, 2021. doi:10.1038/s41597-021-00907-w",
|
|
53
|
+
"kind": "useful energy by carrier, annual totals",
|
|
54
|
+
"peer_reviewed": true,
|
|
55
|
+
"scope": "German industry, 2019, 38 NUTS2 regions"
|
|
56
|
+
},
|
|
57
|
+
"rehfeldt": {
|
|
58
|
+
"caveat": "Digitised from Fig. 16 to about +-5 percentage points. Its own two datasets disagree by more than the subsectors differ from one another, so this is reported as a range and never as a point.",
|
|
59
|
+
"citation": "Rehfeldt M, Fleiter T, Toro F. A bottom-up estimation of the heating and cooling demand in European industry. Energy Efficiency 11:1057-1082, 2018. doi:10.1007/s12053-017-9571-y",
|
|
60
|
+
"kind": "heat by temperature band and cooling, shares of subsector demand",
|
|
61
|
+
"peer_reviewed": true,
|
|
62
|
+
"scope": "UK industry, 7 subsectors, two datasets (ECUK and FORECAST)"
|
|
63
|
+
},
|
|
64
|
+
"tno": {
|
|
65
|
+
"caveat": "No electricity and essentially no refrigeration. Its waste heat is rejected above ambient and is a heat pump source, not a cooling demand.",
|
|
66
|
+
"citation": "Marina A, Spoelstra S, Zondag HA, Wemmers AK. An estimation of the European industrial heat pump market potential. Renewable and Sustainable Energy Reviews 139:110545, 2021. doi:10.1016/j.rser.2020.110545",
|
|
67
|
+
"kind": "process and waste heat quantities with stream temperatures",
|
|
68
|
+
"peer_reviewed": true,
|
|
69
|
+
"scope": "EU-28, 2016 production, 4 sectors, ~66 products"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"subsector_heat_to_cold": {
|
|
73
|
+
"Chemicals": {
|
|
74
|
+
"ECUK": {
|
|
75
|
+
"cold_pct": 13.0,
|
|
76
|
+
"heat_pct": 87.0,
|
|
77
|
+
"heat_to_cold": 6.7
|
|
78
|
+
},
|
|
79
|
+
"FORECAST": {
|
|
80
|
+
"cold_pct": 2.0,
|
|
81
|
+
"heat_pct": 98.0,
|
|
82
|
+
"heat_to_cold": 49.0
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"Food, beverages and tobacco": {
|
|
86
|
+
"ECUK": {
|
|
87
|
+
"cold_pct": 8.0,
|
|
88
|
+
"heat_pct": 92.0,
|
|
89
|
+
"heat_to_cold": 11.5
|
|
90
|
+
},
|
|
91
|
+
"FORECAST": {
|
|
92
|
+
"cold_pct": 3.0,
|
|
93
|
+
"heat_pct": 97.0,
|
|
94
|
+
"heat_to_cold": 32.3
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"Iron and steel, non-ferrous metals": {
|
|
98
|
+
"ECUK": {
|
|
99
|
+
"cold_pct": 0.0,
|
|
100
|
+
"heat_pct": 100.0,
|
|
101
|
+
"heat_to_cold": null
|
|
102
|
+
},
|
|
103
|
+
"FORECAST": {
|
|
104
|
+
"cold_pct": 0.0,
|
|
105
|
+
"heat_pct": 100.0,
|
|
106
|
+
"heat_to_cold": null
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
"Machinery and Transport": {
|
|
110
|
+
"ECUK": {
|
|
111
|
+
"cold_pct": 2.0,
|
|
112
|
+
"heat_pct": 98.0,
|
|
113
|
+
"heat_to_cold": 49.0
|
|
114
|
+
},
|
|
115
|
+
"FORECAST": {
|
|
116
|
+
"cold_pct": 2.0,
|
|
117
|
+
"heat_pct": 98.0,
|
|
118
|
+
"heat_to_cold": 49.0
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
"Non-metallic minerals": {
|
|
122
|
+
"ECUK": {
|
|
123
|
+
"cold_pct": 0.0,
|
|
124
|
+
"heat_pct": 100.0,
|
|
125
|
+
"heat_to_cold": null
|
|
126
|
+
},
|
|
127
|
+
"FORECAST": {
|
|
128
|
+
"cold_pct": 0.0,
|
|
129
|
+
"heat_pct": 100.0,
|
|
130
|
+
"heat_to_cold": null
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
"Other industry": {
|
|
134
|
+
"ECUK": {
|
|
135
|
+
"cold_pct": 0.0,
|
|
136
|
+
"heat_pct": 100.0,
|
|
137
|
+
"heat_to_cold": null
|
|
138
|
+
},
|
|
139
|
+
"FORECAST": {
|
|
140
|
+
"cold_pct": 0.0,
|
|
141
|
+
"heat_pct": 100.0,
|
|
142
|
+
"heat_to_cold": null
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"Paper": {
|
|
146
|
+
"ECUK": {
|
|
147
|
+
"cold_pct": 0.0,
|
|
148
|
+
"heat_pct": 100.0,
|
|
149
|
+
"heat_to_cold": null
|
|
150
|
+
},
|
|
151
|
+
"FORECAST": {
|
|
152
|
+
"cold_pct": 0.0,
|
|
153
|
+
"heat_pct": 100.0,
|
|
154
|
+
"heat_to_cold": null
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|