epyt-flow 0.1.1__py3-none-any.whl → 0.3.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/EPANET/compile_linux.sh +4 -0
- epyt_flow/EPANET/compile_macos.sh +4 -0
- epyt_flow/VERSION +1 -1
- epyt_flow/__init__.py +29 -18
- epyt_flow/data/benchmarks/leakdb.py +7 -12
- epyt_flow/data/networks.py +404 -40
- epyt_flow/rest_api/base_handler.py +14 -0
- epyt_flow/rest_api/scada_data/__init__.py +0 -0
- epyt_flow/rest_api/{scada_data_handler.py → scada_data/data_handlers.py} +3 -162
- epyt_flow/rest_api/scada_data/export_handlers.py +140 -0
- epyt_flow/rest_api/scada_data/handlers.py +209 -0
- epyt_flow/rest_api/scenario/__init__.py +0 -0
- epyt_flow/rest_api/scenario/event_handlers.py +118 -0
- epyt_flow/rest_api/{scenario_handler.py → scenario/handlers.py} +86 -67
- epyt_flow/rest_api/scenario/simulation_handlers.py +174 -0
- epyt_flow/rest_api/scenario/uncertainty_handlers.py +118 -0
- epyt_flow/rest_api/server.py +61 -24
- epyt_flow/simulation/events/leakages.py +27 -17
- epyt_flow/simulation/scada/scada_data.py +545 -14
- epyt_flow/simulation/scada/scada_data_export.py +39 -12
- epyt_flow/simulation/scenario_config.py +14 -20
- epyt_flow/simulation/scenario_simulator.py +358 -114
- epyt_flow/simulation/sensor_config.py +693 -37
- epyt_flow/topology.py +149 -8
- epyt_flow/utils.py +75 -18
- {epyt_flow-0.1.1.dist-info → epyt_flow-0.3.0.dist-info}/METADATA +33 -5
- {epyt_flow-0.1.1.dist-info → epyt_flow-0.3.0.dist-info}/RECORD +30 -22
- epyt_flow/EPANET/compile.sh +0 -4
- {epyt_flow-0.1.1.dist-info → epyt_flow-0.3.0.dist-info}/LICENSE +0 -0
- {epyt_flow-0.1.1.dist-info → epyt_flow-0.3.0.dist-info}/WHEEL +0 -0
- {epyt_flow-0.1.1.dist-info → epyt_flow-0.3.0.dist-info}/top_level.txt +0 -0
|
@@ -2,14 +2,18 @@
|
|
|
2
2
|
Module provides a class for storing and processing SCADA data.
|
|
3
3
|
"""
|
|
4
4
|
import warnings
|
|
5
|
-
from typing import Callable
|
|
5
|
+
from typing import Callable, Any
|
|
6
6
|
from copy import deepcopy
|
|
7
7
|
import numpy as np
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
from epyt.epanet import ToolkitConstants
|
|
9
|
+
|
|
10
|
+
from ..sensor_config import SensorConfig, is_flowunit_simetric, massunit_to_str, \
|
|
11
|
+
MASS_UNIT_MG, MASS_UNIT_UG, TIME_UNIT_HRS, MASS_UNIT_MOL, MASS_UNIT_MMOL, \
|
|
12
|
+
AREA_UNIT_CM2, AREA_UNIT_FT2, AREA_UNIT_M2, \
|
|
13
|
+
SENSOR_TYPE_LINK_FLOW, SENSOR_TYPE_LINK_QUALITY, SENSOR_TYPE_NODE_DEMAND, \
|
|
14
|
+
SENSOR_TYPE_NODE_PRESSURE, SENSOR_TYPE_NODE_QUALITY, SENSOR_TYPE_PUMP_STATE, \
|
|
15
|
+
SENSOR_TYPE_TANK_VOLUME, SENSOR_TYPE_VALVE_STATE, SENSOR_TYPE_NODE_BULK_SPECIES, \
|
|
16
|
+
SENSOR_TYPE_LINK_BULK_SPECIES, SENSOR_TYPE_SURFACE_SPECIES
|
|
13
17
|
from ..events import SensorFault, SensorReadingAttack, SensorReadingEvent
|
|
14
18
|
from ...uncertainty import SensorNoise
|
|
15
19
|
from ...serialization import serializable, Serializable, SCADA_DATA_ID
|
|
@@ -118,7 +122,6 @@ class ScadaData(Serializable):
|
|
|
118
122
|
|
|
119
123
|
The default is False.
|
|
120
124
|
"""
|
|
121
|
-
|
|
122
125
|
def __init__(self, sensor_config: SensorConfig, sensor_readings_time: np.ndarray,
|
|
123
126
|
pressure_data_raw: np.ndarray = None, flow_data_raw: np.ndarray = None,
|
|
124
127
|
demand_data_raw: np.ndarray = None, node_quality_data_raw: np.ndarray = None,
|
|
@@ -132,7 +135,8 @@ class ScadaData(Serializable):
|
|
|
132
135
|
sensor_faults: list[SensorFault] = [],
|
|
133
136
|
sensor_reading_attacks: list[SensorReadingAttack] = [],
|
|
134
137
|
sensor_reading_events: list[SensorReadingEvent] = [],
|
|
135
|
-
sensor_noise: SensorNoise = None, frozen_sensor_config: bool = False,
|
|
138
|
+
sensor_noise: SensorNoise = None, frozen_sensor_config: bool = False,
|
|
139
|
+
**kwds):
|
|
136
140
|
if not isinstance(sensor_config, SensorConfig):
|
|
137
141
|
raise TypeError("'sensor_config' must be an instance of " +
|
|
138
142
|
"'epyt_flow.simulation.SensorConfig' but not of " +
|
|
@@ -381,6 +385,533 @@ class ScadaData(Serializable):
|
|
|
381
385
|
|
|
382
386
|
super().__init__(**kwds)
|
|
383
387
|
|
|
388
|
+
def convert_units(self, flow_unit: int = None, quality_unit: int = None,
|
|
389
|
+
bulk_species_mass_unit: list[int] = None,
|
|
390
|
+
surface_species_mass_unit: list[int] = None,
|
|
391
|
+
surface_species_area_unit: int = None) -> Any:
|
|
392
|
+
"""
|
|
393
|
+
Changes the units of some measurement units.
|
|
394
|
+
|
|
395
|
+
.. note::
|
|
396
|
+
|
|
397
|
+
Beaware of potential rounding errors.
|
|
398
|
+
|
|
399
|
+
Parameters
|
|
400
|
+
----------
|
|
401
|
+
flow_unit : `int`, optional
|
|
402
|
+
New units of hydraulic measurements -- note that the flow unit specifies all other
|
|
403
|
+
hydraulic measurement units.
|
|
404
|
+
|
|
405
|
+
Must be one of the following EPANET toolkit constants:
|
|
406
|
+
|
|
407
|
+
- EN_CFS = 0 (cubic foot/sec)
|
|
408
|
+
- EN_GPM = 1 (gal/min)
|
|
409
|
+
- EN_MGD = 2 (Million gal/day)
|
|
410
|
+
- EN_IMGD = 3 (Imperial MGD)
|
|
411
|
+
- EN_AFD = 4 (ac-foot/day)
|
|
412
|
+
- EN_LPS = 5 (liter/sec)
|
|
413
|
+
- EN_LPM = 6 (liter/min)
|
|
414
|
+
- EN_MLD = 7 (Megaliter/day)
|
|
415
|
+
- EN_CMH = 8 (cubic meter/hr)
|
|
416
|
+
- EN_CMD = 9 (cubic meter/day)
|
|
417
|
+
|
|
418
|
+
If None, units of hydraulic measurement are not changed.
|
|
419
|
+
|
|
420
|
+
The default is None.
|
|
421
|
+
quality_unit : `int`, optional
|
|
422
|
+
New unit of quality measurements -- i.e. chemical concentration.
|
|
423
|
+
Only relevant if basic quality analysis was performed.
|
|
424
|
+
|
|
425
|
+
Must be one of the following constants:
|
|
426
|
+
|
|
427
|
+
- MASS_UNIT_MG = 4 (mg/L)
|
|
428
|
+
- MASS_UNIT_UG = 5 (ug/L)
|
|
429
|
+
|
|
430
|
+
If None, units of quality measurements are not changed.
|
|
431
|
+
|
|
432
|
+
The default is None.
|
|
433
|
+
bulk_species_mass_unit : `list[int]`, optional
|
|
434
|
+
New units of all bulk species measurements -- i.e. for each
|
|
435
|
+
bulk species the measurement unit is specified.
|
|
436
|
+
Note that the assumed ordering is the same as given in 'bulk_species'
|
|
437
|
+
in the sensor configuration -- only relevant if EPANET-MSX is used.
|
|
438
|
+
|
|
439
|
+
Must be one of the following constants:
|
|
440
|
+
|
|
441
|
+
- MASS_UNIT_MG = 4 (milligram)
|
|
442
|
+
- MASS_UNIT_UG = 5 (microgram)
|
|
443
|
+
- MASS_UNIT_MOL = 6 (mole)
|
|
444
|
+
- MASS_UNIT_MMOL = 7 (millimole)
|
|
445
|
+
|
|
446
|
+
If None, measurement units of bulk species are not changed.
|
|
447
|
+
|
|
448
|
+
The default is None.
|
|
449
|
+
surface_species_mass_unit : `list[int]`, optional
|
|
450
|
+
New units of all surface species measurements -- i.e. for each
|
|
451
|
+
surface species the measurement unit is specified.
|
|
452
|
+
Note that the assumed ordering is the same as given in 'surface_species'
|
|
453
|
+
in the sensor configuration -- only relevant if EPANET-MSX is used.
|
|
454
|
+
|
|
455
|
+
Must be one of the following constants:
|
|
456
|
+
|
|
457
|
+
- MASS_UNIT_MG = 4 (milligram)
|
|
458
|
+
- MASS_UNIT_UG = 5 (microgram)
|
|
459
|
+
- MASS_UNIT_MOL = 6 (mole)
|
|
460
|
+
- MASS_UNIT_MMOL = 7 (millimole)
|
|
461
|
+
|
|
462
|
+
If None, measurement units of surface species are not changed.
|
|
463
|
+
|
|
464
|
+
The default is None.
|
|
465
|
+
surface_species_area_unit : `int`, optional
|
|
466
|
+
New area unit of all surface species -- only relevant if EPANET-MSX is used.
|
|
467
|
+
|
|
468
|
+
Must be one of the following constants:
|
|
469
|
+
|
|
470
|
+
- AREA_UNIT_FT2 = 1 (square feet)
|
|
471
|
+
- AREA_UNIT_M2 = 2 (square meters)
|
|
472
|
+
- AREA_UNIT_CM2 = 3 (square centimeters)
|
|
473
|
+
|
|
474
|
+
If None, are units of surface species are not changed.
|
|
475
|
+
|
|
476
|
+
The default is None.
|
|
477
|
+
|
|
478
|
+
Returns
|
|
479
|
+
-------
|
|
480
|
+
:class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
|
|
481
|
+
SCADA data instance with the new units.
|
|
482
|
+
"""
|
|
483
|
+
if flow_unit is not None:
|
|
484
|
+
if not isinstance(flow_unit, int):
|
|
485
|
+
raise TypeError("'flow_unit' must be a an instance of 'int' " +
|
|
486
|
+
f"but not of '{type(flow_unit)}'")
|
|
487
|
+
if flow_unit not in range(10):
|
|
488
|
+
raise ValueError("Invalid value of 'flow_unit'")
|
|
489
|
+
|
|
490
|
+
if quality_unit is not None:
|
|
491
|
+
if not isinstance(quality_unit, int):
|
|
492
|
+
raise TypeError("'quality_mass_unit' must be an instance of 'int' " +
|
|
493
|
+
f"but not of '{type(quality_unit)}'")
|
|
494
|
+
if quality_unit not in [MASS_UNIT_MG, MASS_UNIT_UG, TIME_UNIT_HRS]:
|
|
495
|
+
raise ValueError("Invalid value of 'quality_unit'")
|
|
496
|
+
|
|
497
|
+
if bulk_species_mass_unit is not None:
|
|
498
|
+
if not isinstance(bulk_species_mass_unit, list):
|
|
499
|
+
raise TypeError("'bulk_species_mass_unit' must be an instance of 'list[int]' " +
|
|
500
|
+
f"but not of '{type(bulk_species_mass_unit)}'")
|
|
501
|
+
if len(bulk_species_mass_unit) != len(self.__sensor_config.bulk_species):
|
|
502
|
+
raise ValueError("Inconsistency between 'bulk_species_mass_unit' and " +
|
|
503
|
+
"'bulk_species'")
|
|
504
|
+
if any(not isinstance(mass_unit, int) for mass_unit in bulk_species_mass_unit):
|
|
505
|
+
raise TypeError("All items in 'bulk_species_mass_unit' must be an instance " +
|
|
506
|
+
"of 'int'")
|
|
507
|
+
if any(mass_unit not in [MASS_UNIT_MG, MASS_UNIT_UG, MASS_UNIT_MOL, MASS_UNIT_MMOL]
|
|
508
|
+
for mass_unit in bulk_species_mass_unit):
|
|
509
|
+
raise ValueError("Invalid mass unit in 'bulk_species_mass_unit'")
|
|
510
|
+
|
|
511
|
+
if surface_species_mass_unit is not None:
|
|
512
|
+
if not isinstance(surface_species_mass_unit, list):
|
|
513
|
+
raise TypeError("'surface_species_mass_unit' must be an instance of 'list[int]' " +
|
|
514
|
+
f"but not of '{type(surface_species_mass_unit)}'")
|
|
515
|
+
if len(surface_species_mass_unit) != len(self.__sensor_config.surface_species):
|
|
516
|
+
raise ValueError("Inconsistency between 'surface_species_mass_unit' and " +
|
|
517
|
+
"'surface_species'")
|
|
518
|
+
if any(not isinstance(mass_unit, int) for mass_unit in surface_species_mass_unit):
|
|
519
|
+
raise TypeError("All items in 'surface_species_mass_unit' must be an instance " +
|
|
520
|
+
"of 'int'")
|
|
521
|
+
if any(mass_unit not in [MASS_UNIT_MG, MASS_UNIT_UG, MASS_UNIT_MOL, MASS_UNIT_MMOL]
|
|
522
|
+
for mass_unit in surface_species_mass_unit):
|
|
523
|
+
raise ValueError("Invalid mass unit in 'surface_species_mass_unit'")
|
|
524
|
+
|
|
525
|
+
if surface_species_area_unit is not None:
|
|
526
|
+
if surface_species_area_unit is not None:
|
|
527
|
+
if not isinstance(surface_species_area_unit, int):
|
|
528
|
+
raise TypeError("'surface_species_area_unit' must be a an instance of 'int' " +
|
|
529
|
+
f"but not of '{type(surface_species_area_unit)}'")
|
|
530
|
+
if surface_species_area_unit not in [AREA_UNIT_FT2, AREA_UNIT_M2, AREA_UNIT_CM2]:
|
|
531
|
+
raise ValueError("Invalid area unit 'surface_species_area_unit'")
|
|
532
|
+
|
|
533
|
+
def __get_mass_convert_factor(new_unit_id: int, old_unit_id: int) -> float:
|
|
534
|
+
if new_unit_id == MASS_UNIT_MG and old_unit_id == MASS_UNIT_UG:
|
|
535
|
+
return .001
|
|
536
|
+
elif new_unit_id == MASS_UNIT_UG and old_unit_id == MASS_UNIT_MG:
|
|
537
|
+
return 1000.
|
|
538
|
+
elif new_unit_id == MASS_UNIT_MOL and old_unit_id == MASS_UNIT_MMOL:
|
|
539
|
+
return .001
|
|
540
|
+
elif new_unit_id == MASS_UNIT_MMOL and old_unit_id == MASS_UNIT_MOL:
|
|
541
|
+
return 1000.
|
|
542
|
+
else:
|
|
543
|
+
raise NotImplementedError(f"Can not convert '{massunit_to_str(old_unit_id)}' to " +
|
|
544
|
+
f"'{massunit_to_str(new_unit_id)}'")
|
|
545
|
+
|
|
546
|
+
def __get_flow_convert_factor(new_unit_id: int, old_unit: int) -> float:
|
|
547
|
+
if new_unit_id == ToolkitConstants.EN_CFS:
|
|
548
|
+
if old_unit == ToolkitConstants.EN_GPM:
|
|
549
|
+
return .0022280093
|
|
550
|
+
elif old_unit == ToolkitConstants.EN_MGD:
|
|
551
|
+
return 1.5472286523
|
|
552
|
+
elif old_unit == ToolkitConstants.EN_IMGD:
|
|
553
|
+
return 1.8581441347
|
|
554
|
+
elif old_unit == ToolkitConstants.EN_AFD:
|
|
555
|
+
return .5041666667
|
|
556
|
+
elif old_unit == ToolkitConstants.EN_LPS:
|
|
557
|
+
return .0353146667
|
|
558
|
+
elif old_unit == ToolkitConstants.EN_LPM:
|
|
559
|
+
return .0005885778
|
|
560
|
+
elif old_unit == ToolkitConstants.EN_MLD:
|
|
561
|
+
return .40873456853575
|
|
562
|
+
elif old_unit == ToolkitConstants.EN_CMH:
|
|
563
|
+
return .0098096296
|
|
564
|
+
elif old_unit == ToolkitConstants.EN_CMD:
|
|
565
|
+
return .0004087346
|
|
566
|
+
elif new_unit_id == ToolkitConstants.EN_GPM:
|
|
567
|
+
if old_unit == ToolkitConstants.EN_CFS:
|
|
568
|
+
return 448.8325660485
|
|
569
|
+
elif old_unit == ToolkitConstants.EN_MGD:
|
|
570
|
+
return 694.44444444
|
|
571
|
+
elif old_unit == ToolkitConstants.EN_IMGD:
|
|
572
|
+
return 833.99300382
|
|
573
|
+
elif old_unit == ToolkitConstants.EN_AFD:
|
|
574
|
+
return 226.28571429
|
|
575
|
+
elif old_unit == ToolkitConstants.EN_LPS:
|
|
576
|
+
return 15.850323141
|
|
577
|
+
elif old_unit == ToolkitConstants.EN_LPM:
|
|
578
|
+
return .2641720524
|
|
579
|
+
elif old_unit == ToolkitConstants.EN_MLD:
|
|
580
|
+
return 183.4528141376
|
|
581
|
+
elif old_unit == ToolkitConstants.EN_CMH:
|
|
582
|
+
return 4.4028675393
|
|
583
|
+
elif old_unit == ToolkitConstants.EN_CMD:
|
|
584
|
+
return .1834528141
|
|
585
|
+
elif new_unit_id == ToolkitConstants.EN_MGD:
|
|
586
|
+
if old_unit == ToolkitConstants.EN_CFS:
|
|
587
|
+
return .6463168831
|
|
588
|
+
elif old_unit == ToolkitConstants.EN_GPM:
|
|
589
|
+
return .00144
|
|
590
|
+
elif old_unit == ToolkitConstants.EN_IMGD:
|
|
591
|
+
return 1.2009499255
|
|
592
|
+
elif old_unit == ToolkitConstants.EN_AFD:
|
|
593
|
+
return 0.3258514286
|
|
594
|
+
elif old_unit == ToolkitConstants.EN_LPS:
|
|
595
|
+
return .0228244653
|
|
596
|
+
elif old_unit == ToolkitConstants.EN_LPM:
|
|
597
|
+
return .0003804078
|
|
598
|
+
elif old_unit == ToolkitConstants.EN_MLD:
|
|
599
|
+
return .26417205124156
|
|
600
|
+
elif old_unit == ToolkitConstants.EN_CMH:
|
|
601
|
+
return .0063401293
|
|
602
|
+
elif old_unit == ToolkitConstants.EN_CMD:
|
|
603
|
+
return .0002641721
|
|
604
|
+
elif new_unit_id == ToolkitConstants.EN_IMGD:
|
|
605
|
+
if old_unit == ToolkitConstants.EN_CFS:
|
|
606
|
+
return .5381713837
|
|
607
|
+
elif old_unit == ToolkitConstants.EN_MGD:
|
|
608
|
+
return .8326741846
|
|
609
|
+
elif old_unit == ToolkitConstants.EN_GPM:
|
|
610
|
+
return .0011990508
|
|
611
|
+
elif old_unit == ToolkitConstants.EN_AFD:
|
|
612
|
+
return .2713280726
|
|
613
|
+
elif old_unit == ToolkitConstants.EN_LPS:
|
|
614
|
+
return .0190053431
|
|
615
|
+
elif old_unit == ToolkitConstants.EN_LPM:
|
|
616
|
+
return .0003167557
|
|
617
|
+
elif old_unit == ToolkitConstants.EN_MLD:
|
|
618
|
+
return .21996924829908776
|
|
619
|
+
elif old_unit == ToolkitConstants.EN_CMH:
|
|
620
|
+
return .005279262
|
|
621
|
+
elif old_unit == ToolkitConstants.EN_CMD:
|
|
622
|
+
return .0002199692
|
|
623
|
+
elif new_unit_id == ToolkitConstants.EN_AFD:
|
|
624
|
+
if old_unit == ToolkitConstants.EN_CFS:
|
|
625
|
+
return 1.9834710744
|
|
626
|
+
elif old_unit == ToolkitConstants.EN_MGD:
|
|
627
|
+
return 3.0688832772
|
|
628
|
+
elif old_unit == ToolkitConstants.EN_GPM:
|
|
629
|
+
return .0044191919
|
|
630
|
+
elif old_unit == ToolkitConstants.EN_IMGD:
|
|
631
|
+
return 3.6855751432
|
|
632
|
+
elif old_unit == ToolkitConstants.EN_LPS:
|
|
633
|
+
return .0700456199
|
|
634
|
+
elif old_unit == ToolkitConstants.EN_LPM:
|
|
635
|
+
return .001167427
|
|
636
|
+
elif old_unit == ToolkitConstants.EN_MLD:
|
|
637
|
+
return .81070995093708
|
|
638
|
+
elif old_unit == ToolkitConstants.EN_CMH:
|
|
639
|
+
return .0194571167
|
|
640
|
+
elif old_unit == ToolkitConstants.EN_CMD:
|
|
641
|
+
return .0008107132
|
|
642
|
+
elif new_unit_id == ToolkitConstants.EN_LPS:
|
|
643
|
+
if old_unit == ToolkitConstants.EN_CFS:
|
|
644
|
+
return 28.316846592
|
|
645
|
+
elif old_unit == ToolkitConstants.EN_MGD:
|
|
646
|
+
return 43.812636389
|
|
647
|
+
elif old_unit == ToolkitConstants.EN_IMGD:
|
|
648
|
+
return 52.616782407
|
|
649
|
+
elif old_unit == ToolkitConstants.EN_GPM:
|
|
650
|
+
return .0630901964
|
|
651
|
+
elif old_unit == ToolkitConstants.EN_AFD:
|
|
652
|
+
return 14.276410157
|
|
653
|
+
elif old_unit == ToolkitConstants.EN_LPM:
|
|
654
|
+
return .0166666667
|
|
655
|
+
elif old_unit == ToolkitConstants.EN_MLD:
|
|
656
|
+
return 11.574074074074
|
|
657
|
+
elif old_unit == ToolkitConstants.EN_CMH:
|
|
658
|
+
return .2777777778
|
|
659
|
+
elif old_unit == ToolkitConstants.EN_CMD:
|
|
660
|
+
return .0115740741
|
|
661
|
+
elif new_unit_id == ToolkitConstants.EN_LPM:
|
|
662
|
+
if old_unit == ToolkitConstants.EN_CFS:
|
|
663
|
+
return 1699.0107955
|
|
664
|
+
elif old_unit == ToolkitConstants.EN_MGD:
|
|
665
|
+
return 2628.7581833
|
|
666
|
+
elif old_unit == ToolkitConstants.EN_IMGD:
|
|
667
|
+
return 3157.0069444
|
|
668
|
+
elif old_unit == ToolkitConstants.EN_AFD:
|
|
669
|
+
return 856.58460941
|
|
670
|
+
elif old_unit == ToolkitConstants.EN_LPS:
|
|
671
|
+
return 60
|
|
672
|
+
elif old_unit == ToolkitConstants.EN_GPM:
|
|
673
|
+
return 3.785411784
|
|
674
|
+
elif old_unit == ToolkitConstants.EN_MLD:
|
|
675
|
+
return 694.44444444443
|
|
676
|
+
elif old_unit == ToolkitConstants.EN_CMH:
|
|
677
|
+
return 16.666666667
|
|
678
|
+
elif old_unit == ToolkitConstants.EN_CMD:
|
|
679
|
+
return 0.6944444444
|
|
680
|
+
elif new_unit_id == ToolkitConstants.EN_MLD:
|
|
681
|
+
if old_unit == ToolkitConstants.EN_CFS:
|
|
682
|
+
return 2.4465755456688
|
|
683
|
+
elif old_unit == ToolkitConstants.EN_MGD:
|
|
684
|
+
return 3.7854117999999777
|
|
685
|
+
elif old_unit == ToolkitConstants.EN_IMGD:
|
|
686
|
+
return 4.54609
|
|
687
|
+
elif old_unit == ToolkitConstants.EN_AFD:
|
|
688
|
+
return 1.2334867714947
|
|
689
|
+
elif old_unit == ToolkitConstants.EN_LPS:
|
|
690
|
+
return .0864
|
|
691
|
+
elif old_unit == ToolkitConstants.EN_LPM:
|
|
692
|
+
return .00144
|
|
693
|
+
elif old_unit == ToolkitConstants.EN_GPM:
|
|
694
|
+
return .00545099296896
|
|
695
|
+
elif old_unit == ToolkitConstants.EN_CMH:
|
|
696
|
+
return .024
|
|
697
|
+
elif old_unit == ToolkitConstants.EN_CMD:
|
|
698
|
+
return .00099999999999999
|
|
699
|
+
elif new_unit_id == ToolkitConstants.EN_CMH:
|
|
700
|
+
if old_unit == ToolkitConstants.EN_CFS:
|
|
701
|
+
return 101.94064773
|
|
702
|
+
elif old_unit == ToolkitConstants.EN_MGD:
|
|
703
|
+
return 157.725491
|
|
704
|
+
elif old_unit == ToolkitConstants.EN_IMGD:
|
|
705
|
+
return 189.42041667
|
|
706
|
+
elif old_unit == ToolkitConstants.EN_AFD:
|
|
707
|
+
return 51.395076564
|
|
708
|
+
elif old_unit == ToolkitConstants.EN_LPS:
|
|
709
|
+
return 3.6
|
|
710
|
+
elif old_unit == ToolkitConstants.EN_LPM:
|
|
711
|
+
return .06
|
|
712
|
+
elif old_unit == ToolkitConstants.EN_MLD:
|
|
713
|
+
return 41.666666666666
|
|
714
|
+
elif old_unit == ToolkitConstants.EN_GPM:
|
|
715
|
+
return .227124707
|
|
716
|
+
elif old_unit == ToolkitConstants.EN_CMD:
|
|
717
|
+
return 0.0416666667
|
|
718
|
+
elif new_unit_id == ToolkitConstants.EN_CMD:
|
|
719
|
+
if old_unit == ToolkitConstants.EN_CFS:
|
|
720
|
+
return 2446.5755455
|
|
721
|
+
elif old_unit == ToolkitConstants.EN_MGD:
|
|
722
|
+
return 3785.411784
|
|
723
|
+
elif old_unit == ToolkitConstants.EN_IMGD:
|
|
724
|
+
return 4546.09
|
|
725
|
+
elif old_unit == ToolkitConstants.EN_AFD:
|
|
726
|
+
return 1233.4818375
|
|
727
|
+
elif old_unit == ToolkitConstants.EN_LPS:
|
|
728
|
+
return 86.4
|
|
729
|
+
elif old_unit == ToolkitConstants.EN_LPM:
|
|
730
|
+
return 1.44
|
|
731
|
+
elif old_unit == ToolkitConstants.EN_MLD:
|
|
732
|
+
return 1000.
|
|
733
|
+
elif old_unit == ToolkitConstants.EN_CMH:
|
|
734
|
+
return 24
|
|
735
|
+
elif old_unit == ToolkitConstants.EN_GPM:
|
|
736
|
+
return 5.450992969
|
|
737
|
+
|
|
738
|
+
# Convert units
|
|
739
|
+
pressure_data = self.pressure_data_raw
|
|
740
|
+
flow_data = self.flow_data_raw
|
|
741
|
+
demand_data = self.demand_data_raw
|
|
742
|
+
quality_node_data = self.node_quality_data_raw
|
|
743
|
+
quality_link_data = self.link_quality_data_raw
|
|
744
|
+
tanks_volume_data = self.tanks_volume_data_raw
|
|
745
|
+
surface_species_concentrations = self.surface_species_concentration_raw
|
|
746
|
+
bulk_species_node_concentrations = self.bulk_species_node_concentration_raw
|
|
747
|
+
bulk_species_link_concentrations = self.bulk_species_link_concentration_raw
|
|
748
|
+
|
|
749
|
+
if flow_unit is not None:
|
|
750
|
+
old_flow_unit = self.__sensor_config.flow_unit
|
|
751
|
+
if flow_unit == old_flow_unit:
|
|
752
|
+
warnings.warn("'flow_unit' is identical to the current flow units " +
|
|
753
|
+
"-- nothing to do!", UserWarning)
|
|
754
|
+
else:
|
|
755
|
+
# Convert flows and demands
|
|
756
|
+
convert_factor = __get_flow_convert_factor(flow_unit, old_flow_unit)
|
|
757
|
+
|
|
758
|
+
flow_data *= convert_factor
|
|
759
|
+
demand_data *= convert_factor
|
|
760
|
+
|
|
761
|
+
if is_flowunit_simetric(flow_unit) != is_flowunit_simetric(old_flow_unit):
|
|
762
|
+
# Convert tank volume and pressure
|
|
763
|
+
convert_factor = None
|
|
764
|
+
convert_factor_pressure = None
|
|
765
|
+
if is_flowunit_simetric(flow_unit) is True and \
|
|
766
|
+
is_flowunit_simetric(old_flow_unit) is False:
|
|
767
|
+
convert_factor_volume = .0283168
|
|
768
|
+
convert_factor_pressure = .70325
|
|
769
|
+
else:
|
|
770
|
+
convert_factor_volume = 35.3147
|
|
771
|
+
convert_factor_pressure = 1.4219702084872
|
|
772
|
+
|
|
773
|
+
pressure_data *= convert_factor_pressure
|
|
774
|
+
tanks_volume_data *= convert_factor_volume
|
|
775
|
+
|
|
776
|
+
if quality_unit is not None:
|
|
777
|
+
old_quality_unit = self.__sensor_config.quality_unit()
|
|
778
|
+
if quality_unit == old_quality_unit:
|
|
779
|
+
warnings.warn("'quality_unit' are identical to the current quality units " +
|
|
780
|
+
"-- nothing to do!", UserWarning)
|
|
781
|
+
else:
|
|
782
|
+
# Convert chemical concentration and time (basic quality analysis)
|
|
783
|
+
if quality_unit != TIME_UNIT_HRS:
|
|
784
|
+
convert_factor = __get_mass_convert_factor(quality_unit, old_quality_unit)
|
|
785
|
+
|
|
786
|
+
quality_node_data *= convert_factor
|
|
787
|
+
quality_link_data *= convert_factor
|
|
788
|
+
|
|
789
|
+
if bulk_species_mass_unit is not None:
|
|
790
|
+
# Convert bulk species concentrations
|
|
791
|
+
if self.__frozen_sensor_config is True:
|
|
792
|
+
for i, species_id, _ in enumerate(self.__sensor_config.bulk_species_node_sensors):
|
|
793
|
+
species_idx = self.__sensor_config.bulk_species.index(species_id)
|
|
794
|
+
new_mass_unit = bulk_species_mass_unit[species_idx]
|
|
795
|
+
old_mass_unit = self.__sensor_config.bulk_species_mass_unit[species_idx]
|
|
796
|
+
|
|
797
|
+
if new_mass_unit != old_mass_unit:
|
|
798
|
+
convert_factor = __get_mass_convert_factor(new_mass_unit, old_mass_unit)
|
|
799
|
+
bulk_species_node_concentrations[:, i, :] *= convert_factor
|
|
800
|
+
|
|
801
|
+
for i, species_id, _ in enumerate(self.__sensor_config.bulk_species_link_sensors):
|
|
802
|
+
species_idx = self.__sensor_config.bulk_species.index(species_id)
|
|
803
|
+
new_mass_unit = bulk_species_mass_unit[species_idx]
|
|
804
|
+
old_mass_unit = self.__sensor_config.bulk_species_mass_unit[species_idx]
|
|
805
|
+
|
|
806
|
+
if new_mass_unit != old_mass_unit:
|
|
807
|
+
convert_factor = __get_mass_convert_factor(new_mass_unit, old_mass_unit)
|
|
808
|
+
bulk_species_link_concentrations[:, i, :] *= convert_factor
|
|
809
|
+
else:
|
|
810
|
+
for i in range(bulk_species_node_concentrations.shape[1]):
|
|
811
|
+
if bulk_species_mass_unit[i] != self.__sensor_config.bulk_species_mass_unit[i]:
|
|
812
|
+
old_mass_unit = self.__sensor_config.bulk_species_mass_unit[i]
|
|
813
|
+
convert_factor = __get_mass_convert_factor(bulk_species_mass_unit[i],
|
|
814
|
+
old_mass_unit)
|
|
815
|
+
|
|
816
|
+
bulk_species_node_concentrations[:, i, :] *= convert_factor
|
|
817
|
+
bulk_species_link_concentrations[:, i, :] *= convert_factor
|
|
818
|
+
|
|
819
|
+
if surface_species_mass_unit is not None:
|
|
820
|
+
# Convert surface species concentrations
|
|
821
|
+
if self.__frozen_sensor_config is True:
|
|
822
|
+
for i, species_id, _ in enumerate(self.__sensor_config.surface_species_sensors):
|
|
823
|
+
species_idx = self.__sensor_config.surface_species.index(species_id)
|
|
824
|
+
new_mass_unit = surface_species_mass_unit[species_idx]
|
|
825
|
+
old_mass_unit = self.__sensor_config.surface_species_mass_unit[species_idx]
|
|
826
|
+
|
|
827
|
+
if new_mass_unit != old_mass_unit:
|
|
828
|
+
convert_factor = __get_mass_convert_factor(new_mass_unit, old_mass_unit)
|
|
829
|
+
surface_species_concentrations[:, i, :] *= convert_factor
|
|
830
|
+
else:
|
|
831
|
+
for i in range(surface_species_concentrations.shape[1]):
|
|
832
|
+
old_mass_unit = self.__sensor_config.surface_species_mass_unit[i]
|
|
833
|
+
if surface_species_mass_unit[i] != old_mass_unit:
|
|
834
|
+
convert_factor = __get_mass_convert_factor(surface_species_mass_unit[i],
|
|
835
|
+
old_mass_unit)
|
|
836
|
+
|
|
837
|
+
surface_species_concentrations[:, i, :] *= convert_factor
|
|
838
|
+
|
|
839
|
+
# Create new SCADA data instance
|
|
840
|
+
new_flow_unit = self.__sensor_config.flow_unit
|
|
841
|
+
if flow_unit is not None:
|
|
842
|
+
new_flow_unit = flow_unit
|
|
843
|
+
|
|
844
|
+
new_quality_unit = self.__sensor_config.quality_unit
|
|
845
|
+
if quality_unit is not None:
|
|
846
|
+
new_quality_unit = quality_unit
|
|
847
|
+
|
|
848
|
+
new_bulk_species_mass_unit = self.__sensor_config.bulk_species_mass_unit
|
|
849
|
+
if bulk_species_mass_unit is not None:
|
|
850
|
+
new_bulk_species_mass_unit = bulk_species_mass_unit
|
|
851
|
+
|
|
852
|
+
new_surface_species_mass_unit = self.__sensor_config.surface_species_mass_unit
|
|
853
|
+
if surface_species_mass_unit is not None:
|
|
854
|
+
new_surface_species_mass_unit = surface_species_mass_unit
|
|
855
|
+
|
|
856
|
+
new_surface_species_area_unit = self.__sensor_config.surface_species_area_unit
|
|
857
|
+
if surface_species_area_unit is not None:
|
|
858
|
+
new_surface_species_mass_unit = surface_species_area_unit
|
|
859
|
+
|
|
860
|
+
sensor_config = SensorConfig(nodes=self.__sensor_config.nodes,
|
|
861
|
+
links=self.__sensor_config.links,
|
|
862
|
+
valves=self.__sensor_config.valves,
|
|
863
|
+
pumps=self.__sensor_config.pumps,
|
|
864
|
+
tanks=self.__sensor_config.tanks,
|
|
865
|
+
bulk_species=self.__sensor_config.bulk_species,
|
|
866
|
+
surface_species=self.__sensor_config.surface_species,
|
|
867
|
+
node_id_to_idx=self.__sensor_config.node_id_to_idx,
|
|
868
|
+
link_id_to_idx=self.__sensor_config.link_id_to_idx,
|
|
869
|
+
valve_id_to_idx=self.__sensor_config.valve_id_to_idx,
|
|
870
|
+
pump_id_to_idx=self.__sensor_config.pump_id_to_idx,
|
|
871
|
+
tank_id_to_idx=self.__sensor_config.tank_id_to_idx,
|
|
872
|
+
bulkspecies_id_to_idx=self.__sensor_config.
|
|
873
|
+
bulkspecies_id_to_idx,
|
|
874
|
+
surfacespecies_id_to_idx=self.__sensor_config.
|
|
875
|
+
surfacespecies_id_to_idx,
|
|
876
|
+
flow_unit=new_flow_unit,
|
|
877
|
+
pressure_sensors=self.__sensor_config.pressure_sensors,
|
|
878
|
+
flow_sensors=self.__sensor_config.flow_sensors,
|
|
879
|
+
demand_sensors=self.__sensor_config.demand_sensors,
|
|
880
|
+
quality_node_sensors=self.__sensor_config.quality_node_sensors,
|
|
881
|
+
quality_link_sensors=self.__sensor_config.quality_link_sensors,
|
|
882
|
+
valve_state_sensors=self.__sensor_config.valve_state_sensors,
|
|
883
|
+
pump_state_sensors=self.__sensor_config.pump_state_sensors,
|
|
884
|
+
tank_volume_sensors=self.__sensor_config.tank_volume_sensors,
|
|
885
|
+
bulk_species_node_sensors=
|
|
886
|
+
self.__sensor_config.bulk_species_node_sensors,
|
|
887
|
+
bulk_species_link_sensors=
|
|
888
|
+
self.__sensor_config.bulk_species_link_sensors,
|
|
889
|
+
surface_species_sensors=
|
|
890
|
+
self.__sensor_config.surface_species_sensors,
|
|
891
|
+
quality_unit=new_quality_unit,
|
|
892
|
+
bulk_species_mass_unit=new_bulk_species_mass_unit,
|
|
893
|
+
surface_species_mass_unit=new_surface_species_mass_unit,
|
|
894
|
+
surface_species_area_unit=new_surface_species_area_unit)
|
|
895
|
+
|
|
896
|
+
return ScadaData(sensor_config=sensor_config,
|
|
897
|
+
sensor_readings_time=self.sensor_readings_time,
|
|
898
|
+
sensor_reading_events=self.sensor_reading_events,
|
|
899
|
+
sensor_noise=self.sensor_noise,
|
|
900
|
+
frozen_sensor_config=self.frozen_sensor_config,
|
|
901
|
+
pressure_data_raw=pressure_data,
|
|
902
|
+
flow_data_raw=flow_data,
|
|
903
|
+
demand_data_raw=demand_data,
|
|
904
|
+
node_quality_data_raw=quality_node_data,
|
|
905
|
+
link_quality_data_raw=quality_link_data,
|
|
906
|
+
pumps_state_data_raw=self.pumps_state_data_raw,
|
|
907
|
+
valves_state_data_raw=self.valves_state_data_raw,
|
|
908
|
+
tanks_volume_data_raw=tanks_volume_data,
|
|
909
|
+
pump_energy_usage_data=self.pump_energy_usage_data,
|
|
910
|
+
pump_efficiency_data=self.pump_efficiency_data,
|
|
911
|
+
bulk_species_node_concentration_raw=bulk_species_node_concentrations,
|
|
912
|
+
bulk_species_link_concentration_raw=bulk_species_link_concentrations,
|
|
913
|
+
surface_species_concentration_raw=surface_species_concentrations)
|
|
914
|
+
|
|
384
915
|
@property
|
|
385
916
|
def frozen_sensor_config(self) -> bool:
|
|
386
917
|
"""
|
|
@@ -863,7 +1394,7 @@ class ScadaData(Serializable):
|
|
|
863
1394
|
|
|
864
1395
|
def join(self, other) -> None:
|
|
865
1396
|
"""
|
|
866
|
-
Joins two :class:`~epyt_flow.simulation.
|
|
1397
|
+
Joins two :class:`~epyt_flow.simulation.scada.scada_data.ScadaData` instances based
|
|
867
1398
|
on the sensor reading times. Consequently, **both instances must be equal in their
|
|
868
1399
|
sensor reading times**.
|
|
869
1400
|
Attributes (i.e. types of sensor readings) that are NOT present in THIS instance
|
|
@@ -872,7 +1403,7 @@ class ScadaData(Serializable):
|
|
|
872
1403
|
|
|
873
1404
|
Parameters
|
|
874
1405
|
----------
|
|
875
|
-
other : :class:`~epyt_flow.simulation.
|
|
1406
|
+
other : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
|
|
876
1407
|
Other scada data to be concatenated to this data.
|
|
877
1408
|
"""
|
|
878
1409
|
if not isinstance(other, ScadaData):
|
|
@@ -964,16 +1495,16 @@ class ScadaData(Serializable):
|
|
|
964
1495
|
|
|
965
1496
|
def concatenate(self, other) -> None:
|
|
966
1497
|
"""
|
|
967
|
-
Concatenates two :class:`~epyt_flow.simulation.
|
|
1498
|
+
Concatenates two :class:`~epyt_flow.simulation.scada.scada_data.ScadaData` instances
|
|
968
1499
|
-- i.e. add SCADA data from another given
|
|
969
|
-
:class:`~epyt_flow.simulation.
|
|
1500
|
+
:class:`~epyt_flow.simulation.scada.scada_data.ScadaData` instance to this one.
|
|
970
1501
|
|
|
971
|
-
Note that the two :class:`~epyt_flow.simulation.
|
|
1502
|
+
Note that the two :class:`~epyt_flow.simulation.scada.scada_data.ScadaData` instances
|
|
972
1503
|
must be the same in all other attributs (e.g. sensor configuration, etc.).
|
|
973
1504
|
|
|
974
1505
|
Parameters
|
|
975
1506
|
----------
|
|
976
|
-
other : :class:`~epyt_flow.simulation.
|
|
1507
|
+
other : :class:`~epyt_flow.simulation.scada.scada_data.ScadaData`
|
|
977
1508
|
Other scada data to be concatenated to this data.
|
|
978
1509
|
"""
|
|
979
1510
|
if not isinstance(other, ScadaData):
|