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/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 path.
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 downloaded from
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 not os.path.isfile(download_path):
440
- response = requests.get(url, stream=verbose, allow_redirects=True, timeout=1000)
441
-
442
- if response.status_code != 200:
443
- for backup_url in backup_urls:
444
- response = requests.get(backup_url, stream=verbose, allow_redirects=True,
445
- timeout=1000)
446
- if response.status_code == 200:
447
- break
448
- if response.status_code != 200:
449
- raise SystemError(f"Failed to download -- {response.status_code}")
450
-
451
- if verbose is True:
452
- content_length = int(response.headers.get('content-length', 0))
453
- with open(download_path, "wb") as file, tqdm(desc=download_path,
454
- total=content_length,
455
- ascii=True,
456
- unit='B',
457
- unit_scale=True,
458
- unit_divisor=1024) as progress_bar:
459
- for data in response.iter_content(chunk_size=1024):
460
- size = file.write(data)
461
- progress_bar.update(size)
462
- else:
463
- with open(download_path, "wb") as f_out:
464
- f_out.write(response.content)
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
- self.fig = plt.figure(figsize=(6.4, 4.8), dpi=200)
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=900)
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', 'velocity', or
724
- 'status'. Default is 'flow_rate'.
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
- pos: dict
53
- node_shape: mpl.path.Path = None
54
- node_size: int = 10
55
- node_color: Union[str, list] = 'k'
56
- interpolated: bool = False
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
- pos: dict
258
- edge_color: Union[str, list] = 'k'
259
- interpolated = {}
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', 'velocity', or
314
- 'status'. Default is 'flow_rate'.
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.15.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: >=3.9
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.1.2
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.9 - 3.14
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=-EBaczDChwKNRww-41Bvh48a2F7XnB0a3BnfL_c5iRU,7
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=uGGN1iZ21ek1u6Xzs4z2xum5Qt8554Wem-wEMGEaa7I,14574
4
- epyt_flow/topology.py,sha256=GKWugRj386E-uYMwLDPxZtg5KD27-nkQArSAZEYhde4,26571
5
- epyt_flow/utils.py,sha256=dCEx7FwgTmU8T-ajI-abbrLzJu8llhgn0QDewfTuqHk,20074
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=E9kl0gI5HyL6LZ9ZoLZwdQBNHXKblWW1QV4E8ucr99s,11247
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=LIU1NJ6lnrPeIA2daNJ-runiSLiyMreGLCObe1j3bk4,13909
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=uHHwwzCRwooVdODxDNoCOUgfrlol1K-TS8P8_Ja9Coc,30435
36
- epyt_flow/simulation/scenario_simulator.py,sha256=OIkEjPIFXg16kGXRngzMVJrSQpuyfrOcDvpkEYrhzII,165937
37
- epyt_flow/simulation/sensor_config.py,sha256=5_KNgPMP-z176KeX2KBtTxrqhd8hzVYMH3AJxpBIXp4,94363
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=-WP8n8WOWJOJm2oz78HiwCZMQsyJ5dGS9ZvaeCnesi8,8405
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=tBgxKJGLbjEQ7TOSMjzVGF20-Z5qZAY104MP0AEZaWs,18297
42
- epyt_flow/simulation/events/quality_events.py,sha256=LA9KUlO5hJ1OuneNMsZu4Wl26U6ZiGmz7oUpWXVyvI4,7854
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=2E8q6yWnLxeLgYxW0MHw0GSjlUDjIIZ2GybVRLA7FJQ,7967
45
- epyt_flow/simulation/events/sensor_reading_event.py,sha256=OWMRWPsvgMgaynMayJ-hQotP9zV5sDIYrOfY0fOCuvc,7586
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=06c_cPUetOso2SNDWXS-uhfqYeWtb1Or0U5B4e3dZtA,22132
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=ufaxv_1bFg2WCYQ8IQt1duQ7_fuGINiIL53HYzVa9hM,203288
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=BO9u7gUYdNlGqkWdY_DIiGUWpfQZrL2blx0W805GvIo,12600
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=cqK9XGWcVL6uA-xG_r6rbyJgFkKdtrHhS8YyRicQrG8,50858
55
- epyt_flow/uncertainty/sensor_noise.py,sha256=-AnBfuW1VAx7Ya-q_gJ9bAr7Kx6pzP_y0PvNeuRjXIg,6477
56
- epyt_flow/uncertainty/uncertainties.py,sha256=QBRbI3zIzkeFScyYD5Dy0TBxuL9jPV4SnVU8QwOrJq8,20495
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=vkP0J_OfjvePUz_HehXJkH4CGKR3chAHe_UTx4hQ6XI,57860
60
- epyt_flow/visualization/visualization_utils.py,sha256=-_Hs3FKQz--iVJ10ZeEv1Co9OSbayZmHjxyhKsnzalE,27629
61
- epyt_flow-0.15.0.dist-info/licenses/LICENSE,sha256=YRJC2kcAhMlpEeHwUF0lzE-GRXLFyAjXNErI8L5UwYk,1071
62
- epyt_flow-0.15.0.dist-info/METADATA,sha256=aRbELLILtji0kntYVXehD_dYmGGxGkkdKb4v16RJtww,9615
63
- epyt_flow-0.15.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
64
- epyt_flow-0.15.0.dist-info/top_level.txt,sha256=Wh_kd7TRL8ownCw3Y3dxx-9C0iTSk6wNauv_NX9JcrY,10
65
- epyt_flow-0.15.0.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5