epyt-flow 0.15.0__py3-none-any.whl → 0.16.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.
- epyt_flow/VERSION +1 -1
- epyt_flow/data/benchmarks/batadal.py +1 -1
- epyt_flow/gym/scenario_control_env.py +6 -12
- epyt_flow/serialization.py +19 -3
- epyt_flow/simulation/events/actuator_events.py +24 -24
- epyt_flow/simulation/events/leakages.py +45 -45
- epyt_flow/simulation/events/quality_events.py +23 -23
- epyt_flow/simulation/events/sensor_reading_attack.py +27 -27
- epyt_flow/simulation/events/sensor_reading_event.py +33 -33
- epyt_flow/simulation/scada/complex_control.py +103 -103
- epyt_flow/simulation/scada/scada_data.py +58 -30
- epyt_flow/simulation/scada/simple_control.py +33 -33
- epyt_flow/simulation/scenario_config.py +31 -0
- epyt_flow/simulation/scenario_simulator.py +217 -82
- epyt_flow/simulation/sensor_config.py +220 -108
- epyt_flow/topology.py +197 -6
- epyt_flow/uncertainty/model_uncertainty.py +23 -20
- epyt_flow/uncertainty/sensor_noise.py +16 -16
- epyt_flow/uncertainty/uncertainties.py +6 -4
- epyt_flow/utils.py +189 -30
- epyt_flow/visualization/scenario_visualizer.py +14 -5
- epyt_flow/visualization/visualization_utils.py +18 -16
- {epyt_flow-0.15.0.dist-info → epyt_flow-0.16.0.dist-info}/METADATA +7 -5
- {epyt_flow-0.15.0.dist-info → epyt_flow-0.16.0.dist-info}/RECORD +27 -27
- {epyt_flow-0.15.0.dist-info → epyt_flow-0.16.0.dist-info}/WHEEL +1 -1
- {epyt_flow-0.15.0.dist-info → epyt_flow-0.16.0.dist-info}/licenses/LICENSE +0 -0
- {epyt_flow-0.15.0.dist-info → epyt_flow-0.16.0.dist-info}/top_level.txt +0 -0
epyt_flow/utils.py
CHANGED
|
@@ -8,6 +8,10 @@ import zipfile
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
import re
|
|
10
10
|
import requests
|
|
11
|
+
import time
|
|
12
|
+
import threading
|
|
13
|
+
import multiprocessing as mp
|
|
14
|
+
from deprecated import deprecated
|
|
11
15
|
from tqdm import tqdm
|
|
12
16
|
import numpy as np
|
|
13
17
|
import matplotlib
|
|
@@ -345,6 +349,149 @@ def plot_timeseries_prediction(y: np.ndarray, y_pred: np.ndarray,
|
|
|
345
349
|
return ax
|
|
346
350
|
|
|
347
351
|
|
|
352
|
+
def robust_download(download_path: str, urls: str or list,
|
|
353
|
+
verbose: bool = True, timeout: int = 30) -> None:
|
|
354
|
+
"""
|
|
355
|
+
Downloads a file from the given urls if it does not already exist in the
|
|
356
|
+
given path. The urls are tried in order. If a download stops or stalls,
|
|
357
|
+
the next url is tried until one succeeds or all urls have failed.
|
|
358
|
+
|
|
359
|
+
Parameters
|
|
360
|
+
----------
|
|
361
|
+
download_path : `str`
|
|
362
|
+
Local path to the file -- if this path does not exist, the file will be
|
|
363
|
+
downloaded from the provided 'urls' and stored there.
|
|
364
|
+
urls : `list` or `str`
|
|
365
|
+
One url or a list of urls (where additional urls function as backup) to
|
|
366
|
+
download the file from.
|
|
367
|
+
verbose : `bool`, optional
|
|
368
|
+
If True, a progress bar is shown while downloading the file.
|
|
369
|
+
|
|
370
|
+
The default is True.
|
|
371
|
+
timeout : `int`
|
|
372
|
+
If this time passed without progress while downloading, the download is
|
|
373
|
+
considered failed.
|
|
374
|
+
|
|
375
|
+
The default is 30 seconds.
|
|
376
|
+
"""
|
|
377
|
+
if isinstance(urls, str):
|
|
378
|
+
urls = [urls]
|
|
379
|
+
|
|
380
|
+
for url in urls:
|
|
381
|
+
try:
|
|
382
|
+
download_if_necessary(download_path, url, verbose, timeout)
|
|
383
|
+
return
|
|
384
|
+
except Exception as e:
|
|
385
|
+
print(f"Failed url: {url} with {e}")
|
|
386
|
+
continue
|
|
387
|
+
|
|
388
|
+
raise SystemError("All download attempts failed")
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def _download_process(download_path: str, url: str, backup_urls: list[str],
|
|
392
|
+
last_update: mp.Value, stop_flag: mp.Value,
|
|
393
|
+
finish_flag: mp.Value, verbose: bool) -> None:
|
|
394
|
+
"""
|
|
395
|
+
Process that handles the actual download. It updates the last download
|
|
396
|
+
update variable and cleans up the corrupted file if the download fails from
|
|
397
|
+
within.
|
|
398
|
+
|
|
399
|
+
This function is only to be called from `download_if_necessary`.
|
|
400
|
+
|
|
401
|
+
Parameters
|
|
402
|
+
----------
|
|
403
|
+
download_path : `str`
|
|
404
|
+
Local path to the file -- if this path does not exist, the file will be
|
|
405
|
+
downloaded from the provided 'urls' and stored there.
|
|
406
|
+
url : `str`
|
|
407
|
+
Web-URL pointing to the source the file should be downloaded from. Can
|
|
408
|
+
also point to a google drive file.
|
|
409
|
+
backup_urls : `list[str]`
|
|
410
|
+
List of alternative URLs that are being tried in the case that
|
|
411
|
+
downloading from 'url' fails. This is deprecated, but left in for
|
|
412
|
+
downward compatibility with `download_if_necessary` calls with
|
|
413
|
+
backup_urls. Not necessary when using `robust_download`.
|
|
414
|
+
last_update : `mp.Value`
|
|
415
|
+
Shared variable to keep track of the last successful download update.
|
|
416
|
+
stop_flag : `mp.Value`
|
|
417
|
+
Shared variable. Set to 1 when this process stopped by finishing or
|
|
418
|
+
error.
|
|
419
|
+
finish_flag : `mp.Value`
|
|
420
|
+
Shared variable. Set to 1 when download finished successfully.
|
|
421
|
+
verbose : `bool`
|
|
422
|
+
If True, a progress bar is shown while downloading the file.
|
|
423
|
+
"""
|
|
424
|
+
try:
|
|
425
|
+
progress_bar = None
|
|
426
|
+
response = None
|
|
427
|
+
|
|
428
|
+
if "drive.google.com" in url:
|
|
429
|
+
session = requests.Session()
|
|
430
|
+
response = session.get(url)
|
|
431
|
+
html = response.text
|
|
432
|
+
|
|
433
|
+
def extract(pattern):
|
|
434
|
+
match = re.search(pattern, html)
|
|
435
|
+
return match.group(1) if match else None
|
|
436
|
+
|
|
437
|
+
file_id = extract(r'name="id" value="([^"]+)"')
|
|
438
|
+
file_confirm = extract(r'name="confirm" value="([^"]+)"')
|
|
439
|
+
file_uuid = extract(r'name="uuid" value="([^"]+)"')
|
|
440
|
+
|
|
441
|
+
if not all([file_id, file_confirm, file_uuid]):
|
|
442
|
+
raise SystemError("Failed to extract download parameters")
|
|
443
|
+
|
|
444
|
+
download_url = (
|
|
445
|
+
f"https://drive.usercontent.google.com/download"
|
|
446
|
+
f"?id={file_id}&export=download&confirm={file_confirm}&uuid={file_uuid}"
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
response = session.get(download_url, stream=True)
|
|
450
|
+
|
|
451
|
+
else:
|
|
452
|
+
response = requests.get(url, stream=True, allow_redirects=True,
|
|
453
|
+
timeout=1000)
|
|
454
|
+
|
|
455
|
+
# Deprecated, left in for backward compatibility
|
|
456
|
+
if response.status_code != 200:
|
|
457
|
+
for backup_url in backup_urls:
|
|
458
|
+
response = requests.get(backup_url, stream=verbose,
|
|
459
|
+
allow_redirects=True, timeout=1000)
|
|
460
|
+
if response.status_code == 200:
|
|
461
|
+
break
|
|
462
|
+
if response.status_code != 200:
|
|
463
|
+
raise SystemError(f"Failed to download -- {response.status_code}")
|
|
464
|
+
|
|
465
|
+
content_length = int(response.headers.get("content-length", 0))
|
|
466
|
+
with open(download_path, "wb") as file:
|
|
467
|
+
progress_bar = False
|
|
468
|
+
if verbose:
|
|
469
|
+
progress_bar = tqdm(desc=download_path, total=content_length,
|
|
470
|
+
ascii=True, unit='B', unit_scale=True,
|
|
471
|
+
unit_divisor=1024)
|
|
472
|
+
for data in response.iter_content(chunk_size=1024):
|
|
473
|
+
size = file.write(data)
|
|
474
|
+
if progress_bar:
|
|
475
|
+
progress_bar.update(size)
|
|
476
|
+
with last_update.get_lock():
|
|
477
|
+
last_update.value = time.time()
|
|
478
|
+
with finish_flag.get_lock():
|
|
479
|
+
finish_flag.value = 1
|
|
480
|
+
with stop_flag.get_lock():
|
|
481
|
+
stop_flag.value = 1
|
|
482
|
+
finally:
|
|
483
|
+
if progress_bar:
|
|
484
|
+
progress_bar.close()
|
|
485
|
+
if response:
|
|
486
|
+
response.close()
|
|
487
|
+
with finish_flag.get_lock():
|
|
488
|
+
if os.path.exists(download_path) and finish_flag.value == 0:
|
|
489
|
+
os.remove(download_path)
|
|
490
|
+
with stop_flag.get_lock():
|
|
491
|
+
stop_flag.value = 1
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
@deprecated(reason="Please use new function `robust_download` instead.")
|
|
348
495
|
def download_from_gdrive_if_necessary(download_path: str, url: str, verbose: bool = True) -> None:
|
|
349
496
|
"""
|
|
350
497
|
Downloads a file from a google drive repository if it does not already exist
|
|
@@ -410,18 +557,20 @@ def download_from_gdrive_if_necessary(download_path: str, url: str, verbose: boo
|
|
|
410
557
|
f_out.write(response.content)
|
|
411
558
|
|
|
412
559
|
|
|
560
|
+
# TODO: documentation
|
|
413
561
|
def download_if_necessary(download_path: str, url: str, verbose: bool = True,
|
|
414
|
-
backup_urls: list[str] = []) -> None:
|
|
562
|
+
backup_urls: list[str] = [], timeout: int = 30) -> None:
|
|
415
563
|
"""
|
|
416
|
-
Downloads a file from a given URL if it does not already exist in a given
|
|
564
|
+
Downloads a file from a given URL if it does not already exist in a given
|
|
565
|
+
path. This function is deprecated, please use `robust_download` instead.
|
|
417
566
|
|
|
418
567
|
Note that if the path (folder) does not already exist, it will be created.
|
|
419
568
|
|
|
420
569
|
Parameters
|
|
421
570
|
----------
|
|
422
571
|
download_path : `str`
|
|
423
|
-
Local path to the file -- if this path does not exist, the file will be
|
|
424
|
-
the provided 'url' and stored in 'download_dir'.
|
|
572
|
+
Local path to the file -- if this path does not exist, the file will be
|
|
573
|
+
downloaded from the provided 'url' and stored in 'download_dir'.
|
|
425
574
|
url : `str`
|
|
426
575
|
Web-URL.
|
|
427
576
|
verbose : `bool`, optional
|
|
@@ -432,36 +581,46 @@ def download_if_necessary(download_path: str, url: str, verbose: bool = True,
|
|
|
432
581
|
List of alternative URLs that are being tried in the case that downloading from 'url' fails.
|
|
433
582
|
|
|
434
583
|
The default is an empty list.
|
|
584
|
+
timeout : `int`, optional
|
|
585
|
+
Allowed download inactivity in seconds. After this time passed without
|
|
586
|
+
an update, the donwload is considered failed.
|
|
587
|
+
|
|
588
|
+
The default is 30 seconds.
|
|
435
589
|
"""
|
|
436
590
|
folder_path = str(Path(download_path).parent.absolute())
|
|
437
591
|
create_path_if_not_exist(folder_path)
|
|
438
592
|
|
|
439
|
-
if
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
with
|
|
464
|
-
|
|
593
|
+
if os.path.isfile(download_path):
|
|
594
|
+
return
|
|
595
|
+
|
|
596
|
+
last_update = mp.Value('d', time.time())
|
|
597
|
+
stop_flag = mp.Value('i', 0)
|
|
598
|
+
finish_flag = mp.Value('i', 0)
|
|
599
|
+
|
|
600
|
+
t = mp.Process(target=_download_process, args=(download_path, url, backup_urls, last_update, stop_flag, finish_flag, verbose))
|
|
601
|
+
t.start()
|
|
602
|
+
|
|
603
|
+
while True:
|
|
604
|
+
time.sleep(1)
|
|
605
|
+
with last_update.get_lock():
|
|
606
|
+
idle = time.time() - last_update.value
|
|
607
|
+
with stop_flag.get_lock():
|
|
608
|
+
if stop_flag.value == 1:
|
|
609
|
+
with finish_flag.get_lock():
|
|
610
|
+
if finish_flag.value == 1:
|
|
611
|
+
break
|
|
612
|
+
else:
|
|
613
|
+
if os.path.exists(download_path) and finish_flag.value == 0:
|
|
614
|
+
os.remove(download_path)
|
|
615
|
+
raise SystemError(f"failed downloading from {url}")
|
|
616
|
+
if idle > timeout:
|
|
617
|
+
with finish_flag.get_lock():
|
|
618
|
+
t.terminate()
|
|
619
|
+
t.join()
|
|
620
|
+
if os.path.exists(download_path) and finish_flag.value == 0:
|
|
621
|
+
os.remove(download_path)
|
|
622
|
+
raise SystemError(f"no progress in {timeout} seconds, aborting download")
|
|
623
|
+
t.join()
|
|
465
624
|
|
|
466
625
|
|
|
467
626
|
def create_path_if_not_exist(path_in: str) -> None:
|
|
@@ -518,7 +518,7 @@ class ScenarioVisualizer:
|
|
|
518
518
|
return None
|
|
519
519
|
|
|
520
520
|
def show_plot(self, export_to_file: str = None,
|
|
521
|
-
suppress_plot: bool = False) -> None:
|
|
521
|
+
suppress_plot: bool = False, dpi: int = None) -> None:
|
|
522
522
|
"""
|
|
523
523
|
Displays a static plot of the water distribution network.
|
|
524
524
|
|
|
@@ -533,13 +533,21 @@ class ScenarioVisualizer:
|
|
|
533
533
|
Default is `None`.
|
|
534
534
|
suppress_plot : `bool`, default is False
|
|
535
535
|
If true, no plot is displayed after running this method.
|
|
536
|
+
dpi : `int`, optional
|
|
537
|
+
Dpi of the generated plot. If None, standard values (200 when
|
|
538
|
+
displaying and 900 when saving) are used.
|
|
536
539
|
"""
|
|
537
|
-
|
|
540
|
+
if dpi is None:
|
|
541
|
+
plot_dpi = 200
|
|
542
|
+
save_dpi = 900
|
|
543
|
+
else:
|
|
544
|
+
plot_dpi = save_dpi = dpi
|
|
545
|
+
self.fig = plt.figure(figsize=(6.4, 4.8), dpi=plot_dpi)
|
|
538
546
|
self._get_next_frame(0)
|
|
539
547
|
|
|
540
548
|
if export_to_file is not None:
|
|
541
549
|
plt.savefig(export_to_file, transparent=True, bbox_inches='tight',
|
|
542
|
-
dpi=
|
|
550
|
+
dpi=save_dpi)
|
|
543
551
|
if not suppress_plot:
|
|
544
552
|
plt.show()
|
|
545
553
|
else:
|
|
@@ -720,8 +728,9 @@ class ScenarioVisualizer:
|
|
|
720
728
|
shape links*timesteps. If `None`, a simulation is run to generate
|
|
721
729
|
SCADA data. Default is `None`.
|
|
722
730
|
parameter : `str`, optional
|
|
723
|
-
The link data to visualize. Options are 'flow_rate', '
|
|
724
|
-
'
|
|
731
|
+
The link data to visualize. Options are 'flow_rate', 'link_quality',
|
|
732
|
+
'custom_data', 'bulk_species_concentration' or 'diameter'.
|
|
733
|
+
Default is 'flow_rate'.
|
|
725
734
|
statistic : `str`, optional
|
|
726
735
|
The statistic to calculate for the data. Can be 'mean', 'min',
|
|
727
736
|
'max', or 'time_step'. Default is 'mean'.
|
|
@@ -3,11 +3,9 @@ Module provides helper functions and data management classes for visualizing
|
|
|
3
3
|
scenarios.
|
|
4
4
|
"""
|
|
5
5
|
import inspect
|
|
6
|
-
from dataclasses import dataclass
|
|
7
6
|
from typing import Optional, Union, List, Tuple
|
|
8
7
|
|
|
9
8
|
import matplotlib as mpl
|
|
10
|
-
import matplotlib.pyplot as plt
|
|
11
9
|
import networkx.drawing.nx_pylab as nxp
|
|
12
10
|
import numpy as np
|
|
13
11
|
from scipy.interpolate import CubicSpline
|
|
@@ -23,7 +21,6 @@ stat_funcs = {
|
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
|
|
26
|
-
@dataclass
|
|
27
24
|
class JunctionObject:
|
|
28
25
|
"""
|
|
29
26
|
Represents a junction component (e.g. nodes, tanks, reservoirs, ...) in a
|
|
@@ -48,12 +45,15 @@ class JunctionObject:
|
|
|
48
45
|
interpolated : `bool`, default = False
|
|
49
46
|
Set to True, if node_colors are interpolated for smoother animation.
|
|
50
47
|
"""
|
|
51
|
-
nodelist: list
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
def __init__(self, nodelist: list, pos: dict, node_shape: mpl.path.Path = None,
|
|
49
|
+
node_size: int = 10, node_color: Union[str, list] = 'k',
|
|
50
|
+
interpolated: bool = False):
|
|
51
|
+
self.nodelist = nodelist
|
|
52
|
+
self.pos = pos
|
|
53
|
+
self.node_shape = node_shape
|
|
54
|
+
self.node_size = node_size
|
|
55
|
+
self.node_color = node_color
|
|
56
|
+
self.interpolated = interpolated
|
|
57
57
|
|
|
58
58
|
def add_frame(self, statistic: str, values: np.ndarray,
|
|
59
59
|
pit: int, intervals: Union[int, List[Union[int, float]]]):
|
|
@@ -233,7 +233,6 @@ class JunctionObject:
|
|
|
233
233
|
setattr(self, key, value)
|
|
234
234
|
|
|
235
235
|
|
|
236
|
-
@dataclass
|
|
237
236
|
class EdgeObject:
|
|
238
237
|
"""
|
|
239
238
|
Represents an edge component (pipes) in a water distribution network and
|
|
@@ -253,10 +252,12 @@ class EdgeObject:
|
|
|
253
252
|
interpolated : `dict`, default = {}
|
|
254
253
|
Filled with interpolated frames if interpolation method is called.
|
|
255
254
|
"""
|
|
256
|
-
edgelist: list
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
255
|
+
def __init__(self, edgelist: list, pos: dict, edge_color: Union[str, list] = 'k',
|
|
256
|
+
interpolated: dict = {}):
|
|
257
|
+
self.edgelist = edgelist
|
|
258
|
+
self.pos = pos
|
|
259
|
+
self.edge_color = edge_color
|
|
260
|
+
self.interpolated = interpolated
|
|
260
261
|
|
|
261
262
|
def rescale_widths(self, line_widths: Tuple[int, int] = (1, 2)):
|
|
262
263
|
"""
|
|
@@ -310,8 +311,9 @@ class EdgeObject:
|
|
|
310
311
|
SCADA data created by the :class:`~epyt_flow.simulation.scenario_simulator.ScenarioSimulator`
|
|
311
312
|
instance, is used to retrieve data for the next frame.
|
|
312
313
|
parameter : `str`, default = 'flow_rate'
|
|
313
|
-
The link data to visualize. Options are 'flow_rate', '
|
|
314
|
-
'
|
|
314
|
+
The link data to visualize. Options are 'flow_rate', 'link_quality',
|
|
315
|
+
'custom_data', 'bulk_species_concentration' or 'diameter'.
|
|
316
|
+
Default is 'flow_rate'.
|
|
315
317
|
statistic : `str`, default = 'mean'
|
|
316
318
|
The statistic to calculate for the data. Can be 'mean', 'min',
|
|
317
319
|
'max' or 'time_step'.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: epyt-flow
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.16.0
|
|
4
4
|
Summary: EPyT-Flow -- EPANET Python Toolkit - Flow
|
|
5
5
|
Author-email: André Artelt <aartelt@techfak.uni-bielefeld.de>, "Marios S. Kyriakou" <kiriakou.marios@ucy.ac.cy>, "Stelios G. Vrachimis" <vrachimis.stelios@ucy.ac.cy>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -11,17 +11,19 @@ Project-URL: Issues, https://github.com/WaterFutures/EPyT-Flow/issues
|
|
|
11
11
|
Keywords: epanet,water,networks,hydraulics,quality,simulations
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
14
17
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
16
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
21
|
Classifier: Programming Language :: Python :: 3.13
|
|
20
22
|
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
-
Requires-Python:
|
|
23
|
+
Requires-Python: !=3.14.1,>=3.10
|
|
22
24
|
Description-Content-Type: text/markdown
|
|
23
25
|
License-File: LICENSE
|
|
24
|
-
Requires-Dist: epanet-plus>=0.
|
|
26
|
+
Requires-Dist: epanet-plus>=0.2.0
|
|
25
27
|
Requires-Dist: requests>=2.31.0
|
|
26
28
|
Requires-Dist: scipy>=1.11.4
|
|
27
29
|
Requires-Dist: u-msgpack-python>=2.8.0
|
|
@@ -80,7 +82,7 @@ Unique features of EPyT-Flow that make it superior to other (Python) toolboxes a
|
|
|
80
82
|
|
|
81
83
|
## Installation
|
|
82
84
|
|
|
83
|
-
EPyT-Flow supports Python 3.
|
|
85
|
+
EPyT-Flow supports Python 3.10 - 3.14
|
|
84
86
|
|
|
85
87
|
Note that EPyT-Flow builds upon [EPANET-PLUS](https://github.com/WaterFutures/EPANET-PLUS) which
|
|
86
88
|
constitutes a C extension and Python package.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
epyt_flow/VERSION,sha256
|
|
1
|
+
epyt_flow/VERSION,sha256=qgN0VLXoMgUhoysnij9K_wX8eQCatVA_XUobmjOzjEc,7
|
|
2
2
|
epyt_flow/__init__.py,sha256=mXQNlqjdqYKRiKCYnSCflFAJFEVlke7o-_ln6wZPAmY,154
|
|
3
|
-
epyt_flow/serialization.py,sha256=
|
|
4
|
-
epyt_flow/topology.py,sha256=
|
|
5
|
-
epyt_flow/utils.py,sha256=
|
|
3
|
+
epyt_flow/serialization.py,sha256=Tl-m9GQpDt1YcrVazaNGqvkWtSMxHcAFp-j5tvKyiGE,15109
|
|
4
|
+
epyt_flow/topology.py,sha256=GRFXpXHxhj99iNmaCOcKYarF4K7PJbuV6C7_5wyE0kU,36672
|
|
5
|
+
epyt_flow/utils.py,sha256=8jqjuBIrxWWcvu35e38_47saqQHsgxOBFdROftotfYs,25964
|
|
6
6
|
epyt_flow/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
epyt_flow/data/networks.py,sha256=fZLv3yv_iDS23H-7KNMxNhCvfvrr9WdO7VNR5K3e94U,39842
|
|
8
8
|
epyt_flow/data/benchmarks/__init__.py,sha256=nJ6hqPaPNp8YMizniev3fwOWpzvvNUBMoRF16wACUkE,754
|
|
9
|
-
epyt_flow/data/benchmarks/batadal.py,sha256=
|
|
9
|
+
epyt_flow/data/benchmarks/batadal.py,sha256=0c7Y2yX_Wii4j9xXait0Xg9p3PUhRC4g-5MJDjYYcqI,11256
|
|
10
10
|
epyt_flow/data/benchmarks/batadal_data.py,sha256=oIzcysGivMPAgrfzrk5l8i-j6Ii96DPcFa6sL4TSaw8,880
|
|
11
11
|
epyt_flow/data/benchmarks/battledim.py,sha256=sVV8w4ESjybD__jrmKFhQxuBjtmLoBQmSmU2Wp-v76w,20790
|
|
12
12
|
epyt_flow/data/benchmarks/battledim_data.py,sha256=0vHm-2eAiLv6U-n5dqUUWS1o_szFRy9mVJ3eqDRp4PE,3373
|
|
@@ -15,7 +15,7 @@ epyt_flow/data/benchmarks/leakdb.py,sha256=1EtGshPwnAGJFRrd9B8RqRCnABsCtAoG2kvg1
|
|
|
15
15
|
epyt_flow/data/benchmarks/leakdb_data.py,sha256=FNssgMkC1wqWVlaOrrihr4Od9trEZY7KeK5KuBeRMvM,507058
|
|
16
16
|
epyt_flow/data/benchmarks/water_usage.py,sha256=RiLGLof1HT0luwn7_dG11_nP5U5r9B3L8vDLuwsIKTM,4962
|
|
17
17
|
epyt_flow/gym/__init__.py,sha256=gDIP9VGjKraajsJo6MnPN-TaEUTLnnJNj2c_7jrb8Bg,36
|
|
18
|
-
epyt_flow/gym/scenario_control_env.py,sha256=
|
|
18
|
+
epyt_flow/gym/scenario_control_env.py,sha256=nYwMjvoFywbilCXy_rrC_sdxdi8ngeld-8SUuBJqNWQ,13559
|
|
19
19
|
epyt_flow/rest_api/__init__.py,sha256=4HilmXhdh6H56UHJBB2WUSULlEBUDnI1FPTP11ft3HE,126
|
|
20
20
|
epyt_flow/rest_api/base_handler.py,sha256=HDLXrMXqgWvxWAsB-3G4plyTyCv27_eBbq4eRl_FeBo,2609
|
|
21
21
|
epyt_flow/rest_api/res_manager.py,sha256=j6-3FUBZNLKM9bCsIDZzSytfDYJbDLRwjn1mIPstTqI,2342
|
|
@@ -32,34 +32,34 @@ epyt_flow/rest_api/scenario/simulation_handlers.py,sha256=A59Iv-6k7SlGF_RDQgoFlv
|
|
|
32
32
|
epyt_flow/rest_api/scenario/uncertainty_handlers.py,sha256=Pdo2YmiawOqKXWcLs2P-lv5qN-OKHS3sDgkAFQeRzK8,4568
|
|
33
33
|
epyt_flow/simulation/__init__.py,sha256=nihvZ8O2rJjYQkv7JhtVMqNacO5bA38VtS8Y_0BWrVQ,129
|
|
34
34
|
epyt_flow/simulation/parallel_simulation.py,sha256=ph4KXw9jCt-hiJFJbmC6fNvEsrbQoWV-tFKE5-qSfoQ,6523
|
|
35
|
-
epyt_flow/simulation/scenario_config.py,sha256=
|
|
36
|
-
epyt_flow/simulation/scenario_simulator.py,sha256=
|
|
37
|
-
epyt_flow/simulation/sensor_config.py,sha256=
|
|
35
|
+
epyt_flow/simulation/scenario_config.py,sha256=C-AwNcOd-pabxrF-SJ5Nx8hmdYhkBkSghkIJ3zLkoBY,31733
|
|
36
|
+
epyt_flow/simulation/scenario_simulator.py,sha256=498lmir4FloDh6tfHtYHZO5OMXwS7Of9a_ZHhf2mTQA,173211
|
|
37
|
+
epyt_flow/simulation/sensor_config.py,sha256=ZzZjcdiglkyNvU8DsbMvwDNPvxjEJYnEqG-KvheO8uE,99473
|
|
38
38
|
epyt_flow/simulation/events/__init__.py,sha256=gv8ZcvwjJN0Z5MwRXEOVFRNq4X5NPyyqXIQnhBxszQ0,215
|
|
39
|
-
epyt_flow/simulation/events/actuator_events.py,sha256
|
|
39
|
+
epyt_flow/simulation/events/actuator_events.py,sha256=CAd5A8NGACRKco5wLTMJ3RbL4DBv5FEcKj_JURkNsec,8381
|
|
40
40
|
epyt_flow/simulation/events/event.py,sha256=kARPV20XCAl6zxnJwI9U7ICtZUPACO_rgAmtHm1mGCs,2603
|
|
41
|
-
epyt_flow/simulation/events/leakages.py,sha256=
|
|
42
|
-
epyt_flow/simulation/events/quality_events.py,sha256=
|
|
41
|
+
epyt_flow/simulation/events/leakages.py,sha256=3uR_Zp9iKyar2T8BJ0qf4EfadnEdyIoh_bki7gmwY4I,18242
|
|
42
|
+
epyt_flow/simulation/events/quality_events.py,sha256=Ay67zEQ-zupz-LPdJmTAgQ5RmszSG3Gah5HWlSmjMVk,7827
|
|
43
43
|
epyt_flow/simulation/events/sensor_faults.py,sha256=RFR8LjePkDEkCgENHEgCGT7iKYYtr_qq87-IIwN6ezg,8423
|
|
44
|
-
epyt_flow/simulation/events/sensor_reading_attack.py,sha256=
|
|
45
|
-
epyt_flow/simulation/events/sensor_reading_event.py,sha256=
|
|
44
|
+
epyt_flow/simulation/events/sensor_reading_attack.py,sha256=ZcSSlRc4sN_w5-io5af3RtX_YMmTuJp6x2wgllNfBtQ,7932
|
|
45
|
+
epyt_flow/simulation/events/sensor_reading_event.py,sha256=tQZlmJ7RT0Yv-7BDkmIgEZm8FgQgwAO4HkJIvPhtBoQ,7553
|
|
46
46
|
epyt_flow/simulation/events/system_event.py,sha256=kyj6--D457vlbiVyg0M9yAXMs7GFHpcY8YZns3Aeo8c,2616
|
|
47
47
|
epyt_flow/simulation/scada/__init__.py,sha256=pfJhg-tM5DaiZTXs0_1qJsY2R6Py_LwSz6BUFJexfQM,150
|
|
48
|
-
epyt_flow/simulation/scada/complex_control.py,sha256=
|
|
48
|
+
epyt_flow/simulation/scada/complex_control.py,sha256=8NNl2cM1-AEn5AGg1YYUzfKIJdqWVL6C6YR12V4H_aI,22026
|
|
49
49
|
epyt_flow/simulation/scada/custom_control.py,sha256=HNO5WmRhN9BtMEzC82DKreAym1SC6xdLhNQ-cSOfU2c,4758
|
|
50
|
-
epyt_flow/simulation/scada/scada_data.py,sha256=
|
|
50
|
+
epyt_flow/simulation/scada/scada_data.py,sha256=8_ny9TPSEUm_f8DZ_zRlc4GFvoKfOIPie25pCQJKJtk,205066
|
|
51
51
|
epyt_flow/simulation/scada/scada_data_export.py,sha256=WNAFn_WNfzYAEFbl2Al-cOIx-A0ozY4AI60-i_qEHdc,11643
|
|
52
|
-
epyt_flow/simulation/scada/simple_control.py,sha256=
|
|
52
|
+
epyt_flow/simulation/scada/simple_control.py,sha256=H4uqP-1pifXDai2SnxrmS5SYxBc4UCYUn_1tc9ELb7M,12565
|
|
53
53
|
epyt_flow/uncertainty/__init__.py,sha256=ZRjuJL9rDpWVSdPwObPxFpEmMTcgAl3VmPOsS6cIyGg,89
|
|
54
|
-
epyt_flow/uncertainty/model_uncertainty.py,sha256=
|
|
55
|
-
epyt_flow/uncertainty/sensor_noise.py,sha256
|
|
56
|
-
epyt_flow/uncertainty/uncertainties.py,sha256=
|
|
54
|
+
epyt_flow/uncertainty/model_uncertainty.py,sha256=X9iEWVY58cE_kA_K8U0af5w0fN72m1fTHPnfXY6RuV8,51089
|
|
55
|
+
epyt_flow/uncertainty/sensor_noise.py,sha256=_tZvGF-4tjF-u-1sceIz7VwiVeUiqFeFej-KJenb6Ws,6459
|
|
56
|
+
epyt_flow/uncertainty/uncertainties.py,sha256=nTafn1AhtA2XvC6ESuyPxIhBAuju-BLpFRFdvYEW9jI,20602
|
|
57
57
|
epyt_flow/uncertainty/utils.py,sha256=K-ZhyO6Bg7UmNPgpfND0JLa_wRwyrtUUgGTWyWwy-fo,8029
|
|
58
58
|
epyt_flow/visualization/__init__.py,sha256=uQ7lO6AsgLc88X48Te3QhBQY-iDKGvdPtxKCl4b-Mfw,70
|
|
59
|
-
epyt_flow/visualization/scenario_visualizer.py,sha256=
|
|
60
|
-
epyt_flow/visualization/visualization_utils.py,sha256
|
|
61
|
-
epyt_flow-0.
|
|
62
|
-
epyt_flow-0.
|
|
63
|
-
epyt_flow-0.
|
|
64
|
-
epyt_flow-0.
|
|
65
|
-
epyt_flow-0.
|
|
59
|
+
epyt_flow/visualization/scenario_visualizer.py,sha256=IGJBt_OKoZohmHy8LSA_THn2drsBxZSdxef-G09_Q4g,58237
|
|
60
|
+
epyt_flow/visualization/visualization_utils.py,sha256=CiLFX1ebi4lbiBOoI91707SfFAcIwjL31Xo2AgGDGG4,28018
|
|
61
|
+
epyt_flow-0.16.0.dist-info/licenses/LICENSE,sha256=YRJC2kcAhMlpEeHwUF0lzE-GRXLFyAjXNErI8L5UwYk,1071
|
|
62
|
+
epyt_flow-0.16.0.dist-info/METADATA,sha256=HSUoA3V4ElmaQofEwisSxuQjPIKFFqzDsqL4cNr9ZkM,9714
|
|
63
|
+
epyt_flow-0.16.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
64
|
+
epyt_flow-0.16.0.dist-info/top_level.txt,sha256=Wh_kd7TRL8ownCw3Y3dxx-9C0iTSk6wNauv_NX9JcrY,10
|
|
65
|
+
epyt_flow-0.16.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|