epyt-flow 0.14.0__py3-none-any.whl → 0.14.2__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/gym/scenario_control_env.py +26 -3
- epyt_flow/simulation/events/quality_events.py +6 -6
- epyt_flow/simulation/events/sensor_faults.py +24 -24
- epyt_flow/simulation/events/system_event.py +3 -3
- epyt_flow/simulation/scada/scada_data.py +2 -2
- epyt_flow/simulation/scenario_simulator.py +14 -11
- epyt_flow/topology.py +8 -1
- epyt_flow/uncertainty/model_uncertainty.py +292 -150
- epyt_flow/utils.py +66 -0
- epyt_flow/visualization/visualization_utils.py +4 -2
- {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.2.dist-info}/METADATA +2 -2
- {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.2.dist-info}/RECORD +16 -16
- {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.2.dist-info}/WHEEL +0 -0
- {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.2.dist-info}/licenses/LICENSE +0 -0
- {epyt_flow-0.14.0.dist-info → epyt_flow-0.14.2.dist-info}/top_level.txt +0 -0
|
@@ -119,6 +119,15 @@ class ModelUncertainty(JsonSerializable):
|
|
|
119
119
|
Seed for the random number generator.
|
|
120
120
|
|
|
121
121
|
Thed default is None.
|
|
122
|
+
cache_original : `bool`, optional
|
|
123
|
+
If True, all original properties are cached before the uncertainties are applied.
|
|
124
|
+
This is necessary if you have multiple simulation runs and you want to reset/re-apply
|
|
125
|
+
the uncertainties before each run.
|
|
126
|
+
|
|
127
|
+
You can set it to False if you do not and want to re-apply the uncertainties and
|
|
128
|
+
save some working memory.
|
|
129
|
+
|
|
130
|
+
The default is True.
|
|
122
131
|
"""
|
|
123
132
|
def __init__(self, global_pipe_length_uncertainty: Optional[Uncertainty] = None,
|
|
124
133
|
global_pipe_roughness_uncertainty: Optional[Uncertainty] = None,
|
|
@@ -138,7 +147,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
138
147
|
local_parameters_uncertainty: Optional[dict[str, int, Uncertainty]] = None,
|
|
139
148
|
local_patterns_uncertainty: Optional[dict[str, Uncertainty]] = None,
|
|
140
149
|
local_msx_patterns_uncertainty: Optional[dict[str, Uncertainty]] = None,
|
|
141
|
-
seed: Optional[int] = None,
|
|
150
|
+
seed: Optional[int] = None, cache_original: Optional[bool] = True,
|
|
142
151
|
**kwds):
|
|
143
152
|
if global_pipe_length_uncertainty is not None:
|
|
144
153
|
if not isinstance(global_pipe_length_uncertainty, Uncertainty):
|
|
@@ -283,26 +292,42 @@ class ModelUncertainty(JsonSerializable):
|
|
|
283
292
|
raise TypeError("'local_msx_patterns_uncertainty': " +
|
|
284
293
|
"All keys must be instances of 'str' and all values must be " +
|
|
285
294
|
"instances of 'epyt_flow.uncertainty.Uncertainty'")
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
self.
|
|
291
|
-
self.
|
|
292
|
-
self.
|
|
293
|
-
self.
|
|
294
|
-
self.
|
|
295
|
-
self.
|
|
296
|
-
self.
|
|
297
|
-
self.
|
|
298
|
-
self.
|
|
299
|
-
self.
|
|
300
|
-
self.
|
|
301
|
-
self.
|
|
302
|
-
self.
|
|
303
|
-
self.
|
|
304
|
-
self.
|
|
295
|
+
if not isinstance(cache_original, bool):
|
|
296
|
+
raise TypeError("'cache_original' must be an instance of 'bool' " +
|
|
297
|
+
f"but not of '{type(cache_original)}'")
|
|
298
|
+
|
|
299
|
+
self._global_pipe_length = global_pipe_length_uncertainty
|
|
300
|
+
self._global_pipe_roughness = global_pipe_roughness_uncertainty
|
|
301
|
+
self._global_pipe_diameter = global_pipe_diameter_uncertainty
|
|
302
|
+
self._global_base_demand = global_base_demand_uncertainty
|
|
303
|
+
self._global_demand_pattern = global_demand_pattern_uncertainty
|
|
304
|
+
self._global_elevation = global_elevation_uncertainty
|
|
305
|
+
self._global_constants = global_constants_uncertainty
|
|
306
|
+
self._global_parameters = global_parameters_uncertainty
|
|
307
|
+
self._local_pipe_length = local_pipe_length_uncertainty
|
|
308
|
+
self._local_pipe_roughness = local_pipe_roughness_uncertainty
|
|
309
|
+
self._local_pipe_diameter = local_pipe_diameter_uncertainty
|
|
310
|
+
self._local_base_demand = local_base_demand_uncertainty
|
|
311
|
+
self._local_demand_pattern = local_demand_pattern_uncertainty
|
|
312
|
+
self._local_elevation = local_elevation_uncertainty
|
|
313
|
+
self._local_constants = local_constants_uncertainty
|
|
314
|
+
self._local_parameters = local_parameters_uncertainty
|
|
315
|
+
self._local_patterns = local_patterns_uncertainty
|
|
316
|
+
self._local_msx_patterns = local_msx_patterns_uncertainty
|
|
305
317
|
self.__seed = seed
|
|
318
|
+
self.__cache_original = cache_original
|
|
319
|
+
|
|
320
|
+
self._cache_links_length = None
|
|
321
|
+
self._cache_links_diameter = None
|
|
322
|
+
self._cache_links_roughness_coeff = None
|
|
323
|
+
self._cache_nodes_base_demand = None
|
|
324
|
+
self._cache_nodes_demand_pattern = None
|
|
325
|
+
self._cache_nodes_elevation = None
|
|
326
|
+
self._cache_patterns = None
|
|
327
|
+
self._cache_msx_constants = None
|
|
328
|
+
self._cache_msx_links_parameters = None
|
|
329
|
+
self._cache_msx_tanks_parameters = None
|
|
330
|
+
self._cache_msx_patterns = None
|
|
306
331
|
|
|
307
332
|
super().__init__(**kwds)
|
|
308
333
|
|
|
@@ -328,7 +353,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
328
353
|
:class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`
|
|
329
354
|
Global pipe length uncertainty.
|
|
330
355
|
"""
|
|
331
|
-
return deepcopy(self.
|
|
356
|
+
return deepcopy(self._global_pipe_length)
|
|
332
357
|
|
|
333
358
|
@property
|
|
334
359
|
def global_pipe_roughness(self) -> Uncertainty:
|
|
@@ -340,7 +365,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
340
365
|
:class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`
|
|
341
366
|
Global pipe roughness uncertainty.
|
|
342
367
|
"""
|
|
343
|
-
return deepcopy(self.
|
|
368
|
+
return deepcopy(self._global_pipe_roughness)
|
|
344
369
|
|
|
345
370
|
@property
|
|
346
371
|
def global_pipe_diameter(self) -> Uncertainty:
|
|
@@ -352,7 +377,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
352
377
|
:class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`
|
|
353
378
|
Global pipe diameter uncertainty.
|
|
354
379
|
"""
|
|
355
|
-
return deepcopy(self.
|
|
380
|
+
return deepcopy(self._global_pipe_diameter)
|
|
356
381
|
|
|
357
382
|
@property
|
|
358
383
|
def global_base_demand(self) -> Uncertainty:
|
|
@@ -364,7 +389,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
364
389
|
:class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`
|
|
365
390
|
Global base demand uncertainty.
|
|
366
391
|
"""
|
|
367
|
-
return deepcopy(self.
|
|
392
|
+
return deepcopy(self._global_base_demand)
|
|
368
393
|
|
|
369
394
|
@property
|
|
370
395
|
def global_demand_pattern(self) -> Uncertainty:
|
|
@@ -376,7 +401,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
376
401
|
:class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`
|
|
377
402
|
Global demand pattern uncertainty.
|
|
378
403
|
"""
|
|
379
|
-
return deepcopy(self.
|
|
404
|
+
return deepcopy(self._global_demand_pattern)
|
|
380
405
|
|
|
381
406
|
@property
|
|
382
407
|
def global_elevation(self) -> Uncertainty:
|
|
@@ -388,7 +413,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
388
413
|
:class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`
|
|
389
414
|
Global node elevation uncertainty.
|
|
390
415
|
"""
|
|
391
|
-
return deepcopy(self.
|
|
416
|
+
return deepcopy(self._global_elevation)
|
|
392
417
|
|
|
393
418
|
@property
|
|
394
419
|
def global_constants(self) -> Uncertainty:
|
|
@@ -400,7 +425,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
400
425
|
:class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`
|
|
401
426
|
Global MSX constant uncertainty.
|
|
402
427
|
"""
|
|
403
|
-
return deepcopy(self.
|
|
428
|
+
return deepcopy(self._global_constants)
|
|
404
429
|
|
|
405
430
|
@property
|
|
406
431
|
def global_parameters(self) -> Uncertainty:
|
|
@@ -412,7 +437,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
412
437
|
:class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`
|
|
413
438
|
Global MSX parameter uncertainty.
|
|
414
439
|
"""
|
|
415
|
-
return deepcopy(self.
|
|
440
|
+
return deepcopy(self._global_parameters)
|
|
416
441
|
|
|
417
442
|
@property
|
|
418
443
|
def local_pipe_length(self) -> dict[str, Uncertainty]:
|
|
@@ -424,7 +449,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
424
449
|
dict[str, :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
425
450
|
Local pipe length uncertainty.
|
|
426
451
|
"""
|
|
427
|
-
return deepcopy(self.
|
|
452
|
+
return deepcopy(self._local_pipe_length)
|
|
428
453
|
|
|
429
454
|
@property
|
|
430
455
|
def local_pipe_roughness(self) -> dict[str, Uncertainty]:
|
|
@@ -436,7 +461,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
436
461
|
dict[str, :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
437
462
|
Local pipe roughness uncertainty.
|
|
438
463
|
"""
|
|
439
|
-
return deepcopy(self.
|
|
464
|
+
return deepcopy(self._local_pipe_roughness)
|
|
440
465
|
|
|
441
466
|
@property
|
|
442
467
|
def local_pipe_diameter(self) -> dict[str, Uncertainty]:
|
|
@@ -448,7 +473,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
448
473
|
dict[str, :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
449
474
|
Local pipe diameter uncertainty.
|
|
450
475
|
"""
|
|
451
|
-
return deepcopy(self.
|
|
476
|
+
return deepcopy(self._local_pipe_diameter)
|
|
452
477
|
|
|
453
478
|
@property
|
|
454
479
|
def local_base_demand(self) -> dict[str, Uncertainty]:
|
|
@@ -460,7 +485,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
460
485
|
dict[str, :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
461
486
|
Local base demand uncertainty.
|
|
462
487
|
"""
|
|
463
|
-
return deepcopy(self.
|
|
488
|
+
return deepcopy(self._local_base_demand)
|
|
464
489
|
|
|
465
490
|
@property
|
|
466
491
|
def local_demand_pattern(self) -> dict[str, Uncertainty]:
|
|
@@ -472,7 +497,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
472
497
|
dict[str, :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
473
498
|
Local demand pattern uncertainty.
|
|
474
499
|
"""
|
|
475
|
-
return deepcopy(self.
|
|
500
|
+
return deepcopy(self._local_demand_pattern)
|
|
476
501
|
|
|
477
502
|
@property
|
|
478
503
|
def local_elevation(self) -> dict[str, Uncertainty]:
|
|
@@ -484,7 +509,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
484
509
|
dict[str, :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
485
510
|
Local node elevation uncertainty.
|
|
486
511
|
"""
|
|
487
|
-
return deepcopy(self.
|
|
512
|
+
return deepcopy(self._local_elevation)
|
|
488
513
|
|
|
489
514
|
@property
|
|
490
515
|
def local_constants(self) -> dict[str, Uncertainty]:
|
|
@@ -496,7 +521,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
496
521
|
dict[str, :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
497
522
|
Local MSX constant uncertainty.
|
|
498
523
|
"""
|
|
499
|
-
return deepcopy(self.
|
|
524
|
+
return deepcopy(self._local_constants)
|
|
500
525
|
|
|
501
526
|
@property
|
|
502
527
|
def local_parameters(self) -> dict[tuple[str, int, str], Uncertainty]:
|
|
@@ -508,7 +533,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
508
533
|
dict[tuple[str, int, str], :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
509
534
|
Local MSX parameter uncertainty.
|
|
510
535
|
"""
|
|
511
|
-
return deepcopy(self.
|
|
536
|
+
return deepcopy(self._local_parameters)
|
|
512
537
|
|
|
513
538
|
@property
|
|
514
539
|
def local_patterns(self) -> dict[str, Uncertainty]:
|
|
@@ -520,7 +545,7 @@ class ModelUncertainty(JsonSerializable):
|
|
|
520
545
|
dict[str, :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
521
546
|
Local EPANET patterns uncertainty.
|
|
522
547
|
"""
|
|
523
|
-
return deepcopy(self.
|
|
548
|
+
return deepcopy(self._local_patterns)
|
|
524
549
|
|
|
525
550
|
@property
|
|
526
551
|
def local_msx_patterns(self) -> dict[str, Uncertainty]:
|
|
@@ -532,27 +557,27 @@ class ModelUncertainty(JsonSerializable):
|
|
|
532
557
|
dict[str, :class:`~epyt_flow.uncertainty.uncertainties.Uncertainty`]
|
|
533
558
|
Local EPANET-MSX patterns uncertainty.
|
|
534
559
|
"""
|
|
535
|
-
return deepcopy(self.
|
|
560
|
+
return deepcopy(self._local_msx_patterns)
|
|
536
561
|
|
|
537
562
|
def get_attributes(self) -> dict:
|
|
538
|
-
attribs = {"global_pipe_length_uncertainty": self.
|
|
539
|
-
"global_pipe_roughness_uncertainty": self.
|
|
540
|
-
"global_pipe_diameter_uncertainty": self.
|
|
541
|
-
"global_base_demand_uncertainty": self.
|
|
542
|
-
"global_demand_pattern_uncertainty": self.
|
|
543
|
-
"global_elevation_uncertainty": self.
|
|
544
|
-
"global_constants_uncertainty": self.
|
|
545
|
-
"global_parameters_uncertainty": self.
|
|
546
|
-
"local_pipe_length_uncertainty": self.
|
|
547
|
-
"local_pipe_roughness_uncertainty": self.
|
|
548
|
-
"local_pipe_diameter_uncertainty": self.
|
|
549
|
-
"local_base_demand_uncertainty": self.
|
|
550
|
-
"local_demand_pattern_uncertainty": self.
|
|
551
|
-
"local_elevation_uncertainty": self.
|
|
552
|
-
"local_constants_uncertainty": self.
|
|
553
|
-
"local_parameters_uncertainty": self.
|
|
554
|
-
"local_patterns_uncertainty": self.
|
|
555
|
-
"local_msx_patterns_uncertainty": self.
|
|
563
|
+
attribs = {"global_pipe_length_uncertainty": self._global_pipe_length,
|
|
564
|
+
"global_pipe_roughness_uncertainty": self._global_pipe_roughness,
|
|
565
|
+
"global_pipe_diameter_uncertainty": self._global_pipe_diameter,
|
|
566
|
+
"global_base_demand_uncertainty": self._global_base_demand,
|
|
567
|
+
"global_demand_pattern_uncertainty": self._global_demand_pattern,
|
|
568
|
+
"global_elevation_uncertainty": self._global_elevation,
|
|
569
|
+
"global_constants_uncertainty": self._global_constants,
|
|
570
|
+
"global_parameters_uncertainty": self._global_parameters,
|
|
571
|
+
"local_pipe_length_uncertainty": self._local_pipe_length,
|
|
572
|
+
"local_pipe_roughness_uncertainty": self._local_pipe_roughness,
|
|
573
|
+
"local_pipe_diameter_uncertainty": self._local_pipe_diameter,
|
|
574
|
+
"local_base_demand_uncertainty": self._local_base_demand,
|
|
575
|
+
"local_demand_pattern_uncertainty": self._local_demand_pattern,
|
|
576
|
+
"local_elevation_uncertainty": self._local_elevation,
|
|
577
|
+
"local_constants_uncertainty": self._local_constants,
|
|
578
|
+
"local_parameters_uncertainty": self._local_parameters,
|
|
579
|
+
"local_patterns_uncertainty": self._local_patterns,
|
|
580
|
+
"local_msx_patterns_uncertainty": self._local_msx_patterns,
|
|
556
581
|
"seed": self.__seed}
|
|
557
582
|
|
|
558
583
|
return super().get_attributes() | attribs
|
|
@@ -562,45 +587,101 @@ class ModelUncertainty(JsonSerializable):
|
|
|
562
587
|
raise TypeError("Can not compare 'ModelUncertainty' instance " +
|
|
563
588
|
f"with '{type(other)}' instance")
|
|
564
589
|
|
|
565
|
-
return self.
|
|
566
|
-
and self.
|
|
567
|
-
and self.
|
|
568
|
-
and self.
|
|
569
|
-
and self.
|
|
570
|
-
and self.
|
|
571
|
-
and self.
|
|
572
|
-
and self.
|
|
573
|
-
and self.
|
|
574
|
-
and self.
|
|
575
|
-
and self.
|
|
576
|
-
and self.
|
|
577
|
-
and self.
|
|
578
|
-
and self.
|
|
579
|
-
and self.
|
|
580
|
-
and self.
|
|
581
|
-
and self.
|
|
582
|
-
and self.
|
|
590
|
+
return self._global_pipe_length == other.global_pipe_length \
|
|
591
|
+
and self._global_pipe_roughness == other.global_pipe_roughness \
|
|
592
|
+
and self._global_pipe_diameter == other.global_pipe_diameter \
|
|
593
|
+
and self._global_base_demand == other.global_base_demand \
|
|
594
|
+
and self._global_demand_pattern == other.global_demand_pattern \
|
|
595
|
+
and self._global_elevation == other.global_elevation \
|
|
596
|
+
and self._global_parameters == other.global_parameters \
|
|
597
|
+
and self._global_constants == other.global_constants \
|
|
598
|
+
and self._local_pipe_length == other.local_pipe_length \
|
|
599
|
+
and self._local_pipe_roughness == other.local_pipe_roughness \
|
|
600
|
+
and self._local_pipe_diameter == other.local_pipe_diameter \
|
|
601
|
+
and self._local_base_demand == other.local_base_demand \
|
|
602
|
+
and self._local_demand_pattern == other.local_demand_pattern \
|
|
603
|
+
and self._local_elevation == other.local_elevation \
|
|
604
|
+
and self._local_parameters == other.local_parameters \
|
|
605
|
+
and self._local_constants == other.local_constants \
|
|
606
|
+
and self._local_patterns == other.local_patterns \
|
|
607
|
+
and self._local_msx_patterns == other.local_msx_patterns \
|
|
583
608
|
and self.__seed == other.seed
|
|
584
609
|
|
|
585
610
|
def __str__(self) -> str:
|
|
586
|
-
return f"global_pipe_length: {self.
|
|
587
|
-
f"global_pipe_roughness: {self.
|
|
588
|
-
f"global_pipe_diameter: {self.
|
|
589
|
-
f"global_demand_base: {self.
|
|
590
|
-
f"global_demand_pattern: {self.
|
|
591
|
-
f"global_elevation: {self.
|
|
592
|
-
f"global_constants: {self.
|
|
593
|
-
f"global_parameters: {self.
|
|
594
|
-
f"local_pipe_length: {self.
|
|
595
|
-
f"local_pipe_roughness: {self.
|
|
596
|
-
f"local_pipe_diameter: {self.
|
|
597
|
-
f"local_demand_base: {self.
|
|
598
|
-
f"local_demand_pattern: {self.
|
|
599
|
-
f"local_elevation: {self.
|
|
600
|
-
f"local_constants: {self.
|
|
601
|
-
f"local_parameters: {self.
|
|
602
|
-
f"local_patterns: {self.
|
|
603
|
-
f"local_msx_patterns: {self.
|
|
611
|
+
return f"global_pipe_length: {self._global_pipe_length} " +\
|
|
612
|
+
f"global_pipe_roughness: {self._global_pipe_roughness} " + \
|
|
613
|
+
f"global_pipe_diameter: {self._global_pipe_diameter} " + \
|
|
614
|
+
f"global_demand_base: {self._global_base_demand} " + \
|
|
615
|
+
f"global_demand_pattern: {self._global_demand_pattern} " + \
|
|
616
|
+
f"global_elevation: {self._global_elevation} " + \
|
|
617
|
+
f"global_constants: {self._global_constants} " + \
|
|
618
|
+
f"global_parameters: {self._global_parameters}" + \
|
|
619
|
+
f"local_pipe_length: {self._local_pipe_length} " +\
|
|
620
|
+
f"local_pipe_roughness: {self._local_pipe_roughness} " + \
|
|
621
|
+
f"local_pipe_diameter: {self._local_pipe_diameter} " + \
|
|
622
|
+
f"local_demand_base: {self._local_base_demand} " + \
|
|
623
|
+
f"local_demand_pattern: {self._local_demand_pattern} " + \
|
|
624
|
+
f"local_elevation: {self._local_elevation} " + \
|
|
625
|
+
f"local_constants: {self._local_constants} " + \
|
|
626
|
+
f"local_parameters: {self._local_parameters} " + \
|
|
627
|
+
f"local_patterns: {self._local_patterns} " + \
|
|
628
|
+
f"local_msx_patterns: {self._local_msx_patterns} + seed: {self.__seed}"
|
|
629
|
+
|
|
630
|
+
def undo(self, epanet_api: epyt.epanet) -> None:
|
|
631
|
+
"""
|
|
632
|
+
Undo all applied uncertainties -- i.e, resets the properties to their original value.
|
|
633
|
+
|
|
634
|
+
Note that this function can only be used if `cache_original` (of the constructor)
|
|
635
|
+
was set to True (default).
|
|
636
|
+
|
|
637
|
+
Parameters
|
|
638
|
+
----------
|
|
639
|
+
epanet_api : `epyt.epanet <https://epanet-python-toolkit-epyt.readthedocs.io/en/stable/api.html#epyt.epanet.epanet>`_
|
|
640
|
+
Interface to EPANET and EPANET-MSX
|
|
641
|
+
"""
|
|
642
|
+
if self.__cache_original is False:
|
|
643
|
+
raise ValueError("Caching was disabled by the user")
|
|
644
|
+
|
|
645
|
+
if self._cache_links_length is not None:
|
|
646
|
+
epanet_api.setLinkLength(self._cache_links_length)
|
|
647
|
+
|
|
648
|
+
if self._cache_links_diameter is not None:
|
|
649
|
+
epanet_api.setLinkDiameter(self._cache_links_diameter)
|
|
650
|
+
|
|
651
|
+
if self._cache_links_roughness_coeff is not None:
|
|
652
|
+
epanet_api.setLinkRoughnessCoeff(self._cache_links_roughness_coeff)
|
|
653
|
+
|
|
654
|
+
if self._cache_nodes_base_demand is not None:
|
|
655
|
+
for node_idx in self._cache_nodes_base_demand.keys():
|
|
656
|
+
for demand_category, base_demand in self._cache_nodes_base_demand[node_idx].items():
|
|
657
|
+
epanet_api.setNodeBaseDemands(node_idx, demand_category + 1, base_demand)
|
|
658
|
+
|
|
659
|
+
if self._cache_nodes_demand_pattern is not None:
|
|
660
|
+
for pattern_id, demand_pattern in self._cache_nodes_demand_pattern.items():
|
|
661
|
+
for t, v in enumerate(demand_pattern):
|
|
662
|
+
epanet_api.setPatternValue(pattern_id, t+1, v)
|
|
663
|
+
|
|
664
|
+
if self._cache_nodes_elevation is not None:
|
|
665
|
+
epanet_api.setNodeElevations(self._cache_nodes_elevation)
|
|
666
|
+
|
|
667
|
+
if self._cache_patterns is not None:
|
|
668
|
+
for pattern_idx, pattern in self._cache_patterns.items():
|
|
669
|
+
epanet_api.setPattern(pattern_idx, pattern)
|
|
670
|
+
|
|
671
|
+
if self._cache_msx_constants is not None:
|
|
672
|
+
epanet_api.setMSXConstantsValue(self._cache_msx_constants)
|
|
673
|
+
|
|
674
|
+
if self._cache_msx_links_parameters is not None:
|
|
675
|
+
for pipe_idx, parameters_pipes_val in self._cache_msx_links_parameters.items():
|
|
676
|
+
epanet_api.setMSXParametersPipesValue(pipe_idx, parameters_pipes_val)
|
|
677
|
+
|
|
678
|
+
if self._cache_msx_tanks_parameters is not None:
|
|
679
|
+
for tank_idx, parameters_tanks_val in self._cache_msx_tanks_parameters.items():
|
|
680
|
+
epanet_api.setMSXParametersTanksValue(tank_idx, parameters_tanks_val)
|
|
681
|
+
|
|
682
|
+
if self._cache_msx_patterns is not None:
|
|
683
|
+
for pattern_idx, pattern in self._cache_msx_patterns:
|
|
684
|
+
epanet_api.setMSXPattern(pattern_idx, pattern)
|
|
604
685
|
|
|
605
686
|
def apply(self, epanet_api: epyt.epanet) -> None:
|
|
606
687
|
"""
|
|
@@ -613,174 +694,227 @@ class ModelUncertainty(JsonSerializable):
|
|
|
613
694
|
"""
|
|
614
695
|
np_rand_gen = np.random.default_rng(seed=self.__seed)
|
|
615
696
|
|
|
616
|
-
if self.
|
|
617
|
-
self.
|
|
697
|
+
if self._global_pipe_length is not None:
|
|
698
|
+
self._global_pipe_length.set_random_generator(np_rand_gen)
|
|
618
699
|
|
|
619
700
|
link_length = epanet_api.getLinkLength()
|
|
620
|
-
|
|
701
|
+
self._cache_links_length = np.copy(link_length)
|
|
702
|
+
|
|
703
|
+
link_length = self._global_pipe_length.apply_batch(link_length)
|
|
621
704
|
epanet_api.setLinkLength(link_length)
|
|
622
705
|
|
|
623
|
-
if self.
|
|
624
|
-
self.
|
|
706
|
+
if self._local_pipe_length is not None:
|
|
707
|
+
self._local_pipe_length.set_random_generator(np_rand_gen)
|
|
708
|
+
self._cache_links_length = epanet_api.getLinkLength()
|
|
625
709
|
|
|
626
|
-
for pipe_id, uncertainty in self.
|
|
710
|
+
for pipe_id, uncertainty in self._local_pipe_length.items():
|
|
627
711
|
link_idx = epanet_api.getLinkIndex(pipe_id)
|
|
628
712
|
link_length = epanet_api.getLinkLength(link_idx)
|
|
713
|
+
|
|
629
714
|
link_length = uncertainty.apply(link_length)
|
|
630
715
|
epanet_api.setLinkLength(link_idx, link_length)
|
|
631
716
|
|
|
632
|
-
if self.
|
|
633
|
-
self.
|
|
717
|
+
if self._global_pipe_diameter is not None:
|
|
718
|
+
self._global_pipe_diameter.set_random_generator(np_rand_gen)
|
|
634
719
|
|
|
635
720
|
link_diameters = epanet_api.getLinkDiameter()
|
|
636
|
-
|
|
721
|
+
self._cache_links_diameter = np.copy(link_diameters)
|
|
722
|
+
|
|
723
|
+
link_diameters = self._global_pipe_diameter.apply_batch(link_diameters)
|
|
637
724
|
epanet_api.setLinkDiameter(link_diameters)
|
|
638
725
|
|
|
639
|
-
if self.
|
|
640
|
-
self.
|
|
726
|
+
if self._local_pipe_diameter is not None:
|
|
727
|
+
self._local_pipe_diameter.set_random_generator(np_rand_gen)
|
|
728
|
+
self._cache_links_diameter = epanet_api.getLinkDiameter()
|
|
641
729
|
|
|
642
|
-
for pipe_id, uncertainty in self.
|
|
730
|
+
for pipe_id, uncertainty in self._local_pipe_diameter.items():
|
|
643
731
|
link_idx = epanet_api.getLinkIndex(pipe_id)
|
|
644
732
|
link_diameter = epanet_api.getLinkDiameter(link_idx)
|
|
733
|
+
|
|
645
734
|
link_diameter = uncertainty.apply(link_diameter)
|
|
646
735
|
epanet_api.setLinkDiameter(link_idx, link_diameter)
|
|
647
736
|
|
|
648
|
-
if self.
|
|
649
|
-
self.
|
|
737
|
+
if self._global_pipe_roughness is not None:
|
|
738
|
+
self._global_pipe_roughness.set_random_generator(np_rand_gen)
|
|
650
739
|
|
|
651
740
|
coeffs = epanet_api.getLinkRoughnessCoeff()
|
|
652
|
-
|
|
741
|
+
self._cache_links_roughness_coeff = np.copy(coeffs)
|
|
742
|
+
|
|
743
|
+
coeffs = self._global_pipe_roughness.apply_batch(coeffs)
|
|
653
744
|
epanet_api.setLinkRoughnessCoeff(coeffs)
|
|
654
745
|
|
|
655
|
-
if self.
|
|
656
|
-
self.
|
|
746
|
+
if self._local_pipe_roughness is not None:
|
|
747
|
+
self._local_pipe_roughness.set_random_generator(np_rand_gen)
|
|
748
|
+
self._cache_links_roughness_coeff = epanet_api.getLinkRoughnessCoeff()
|
|
657
749
|
|
|
658
|
-
for pipe_id, uncertainty in self.
|
|
750
|
+
for pipe_id, uncertainty in self._local_pipe_roughness.items():
|
|
659
751
|
link_idx = epanet_api.getLinkIndex(pipe_id)
|
|
660
752
|
link_roughness_coeff = epanet_api.getLinkRoughnessCoeff(link_idx)
|
|
753
|
+
|
|
661
754
|
link_roughness_coeff = uncertainty.apply(link_roughness_coeff)
|
|
662
755
|
epanet_api.setLinkRoughnessCoeff(link_idx, link_roughness_coeff)
|
|
663
756
|
|
|
664
|
-
if self.
|
|
665
|
-
self.
|
|
757
|
+
if self._global_base_demand is not None:
|
|
758
|
+
self._global_base_demand.set_random_generator(np_rand_gen)
|
|
666
759
|
|
|
760
|
+
self._cache_nodes_base_demand = {}
|
|
667
761
|
all_nodes_idx = epanet_api.getNodeIndex()
|
|
668
762
|
for node_idx in all_nodes_idx:
|
|
763
|
+
self._cache_nodes_base_demand[node_idx] = {}
|
|
669
764
|
n_demand_categories = epanet_api.getNodeDemandCategoriesNumber(node_idx)
|
|
670
765
|
for demand_category in range(n_demand_categories):
|
|
671
766
|
base_demand = epanet_api.getNodeBaseDemands(node_idx)[demand_category + 1]
|
|
672
|
-
|
|
767
|
+
self._cache_nodes_base_demand[node_idx][demand_category] = base_demand
|
|
768
|
+
|
|
769
|
+
base_demand = self._global_base_demand.apply(base_demand)
|
|
673
770
|
epanet_api.setNodeBaseDemands(node_idx, demand_category + 1, base_demand)
|
|
674
771
|
|
|
675
|
-
if self.
|
|
676
|
-
self.
|
|
772
|
+
if self._local_base_demand is not None:
|
|
773
|
+
self._local_base_demand.set_random_generator(np_rand_gen)
|
|
677
774
|
|
|
678
|
-
|
|
775
|
+
self._cache_nodes_base_demand = {}
|
|
776
|
+
for node_id, uncertainty in self._local_base_demand.items():
|
|
679
777
|
node_idx = epanet_api.getNodeIndex(node_id)
|
|
778
|
+
self._cache_nodes_base_demand[node_idx] = {}
|
|
680
779
|
n_demand_categories = epanet_api.getNodeDemandCategoriesNumber(node_idx)
|
|
681
780
|
for demand_category in range(n_demand_categories):
|
|
682
781
|
base_demand = epanet_api.getNodeBaseDemands(node_idx)[demand_category + 1]
|
|
782
|
+
self._cache_nodes_base_demand[node_idx][demand_category] = base_demand
|
|
783
|
+
|
|
683
784
|
base_demand = uncertainty.apply(base_demand)
|
|
684
785
|
epanet_api.setNodeBaseDemands(node_idx, demand_category + 1, base_demand)
|
|
685
786
|
|
|
686
|
-
if self.
|
|
687
|
-
self.
|
|
787
|
+
if self._global_demand_pattern is not None:
|
|
788
|
+
self._global_demand_pattern.set_random_generator(np_rand_gen)
|
|
688
789
|
|
|
790
|
+
self._cache_nodes_demand_pattern = {}
|
|
689
791
|
demand_patterns_idx = epanet_api.getNodeDemandPatternIndex()
|
|
690
792
|
demand_patterns_id = np.unique([demand_patterns_idx[k]
|
|
691
793
|
for k in demand_patterns_idx.keys()])
|
|
692
794
|
for pattern_id in demand_patterns_id:
|
|
693
795
|
if pattern_id == 0:
|
|
694
796
|
continue
|
|
797
|
+
|
|
695
798
|
pattern_length = epanet_api.getPatternLengths(pattern_id)
|
|
799
|
+
demand_pattern = np.zeros(pattern_length)
|
|
800
|
+
|
|
696
801
|
for t in range(pattern_length):
|
|
697
802
|
v = epanet_api.getPatternValue(pattern_id, t+1)
|
|
698
|
-
|
|
803
|
+
demand_pattern[t] = v
|
|
804
|
+
|
|
805
|
+
v_ = self._global_demand_pattern.apply(v)
|
|
699
806
|
epanet_api.setPatternValue(pattern_id, t+1, v_)
|
|
700
807
|
|
|
701
|
-
|
|
702
|
-
self.__local_demand_pattern.set_random_generator(np_rand_gen)
|
|
808
|
+
self._cache_nodes_demand_pattern[pattern_id] = demand_pattern
|
|
703
809
|
|
|
810
|
+
if self._local_demand_pattern is not None:
|
|
811
|
+
self._local_demand_pattern.set_random_generator(np_rand_gen)
|
|
812
|
+
|
|
813
|
+
self._cache_nodes_demand_pattern = {}
|
|
704
814
|
patterns_id = epanet_api.getPatternNameID()
|
|
705
815
|
paterns_idx = epanet_api.getPatternIndex()
|
|
706
816
|
|
|
707
|
-
for pattern_id, uncertainty in self.
|
|
817
|
+
for pattern_id, uncertainty in self._local_demand_pattern.items():
|
|
708
818
|
pattern_idx = paterns_idx[patterns_id.index(pattern_id)]
|
|
709
819
|
pattern_length, = epanet_api.getPatternLengths(pattern_id)
|
|
820
|
+
demand_pattern = np.zeros(pattern_length)
|
|
821
|
+
|
|
710
822
|
for t in range(pattern_length):
|
|
711
823
|
v = epanet_api.getPatternValue(pattern_idx, t+1)
|
|
824
|
+
demand_pattern[t] = v
|
|
825
|
+
|
|
712
826
|
v_ = uncertainty.apply(v)
|
|
713
827
|
epanet_api.setPatternValue(pattern_idx, t+1, v_)
|
|
714
828
|
|
|
715
|
-
|
|
716
|
-
|
|
829
|
+
self._cache_nodes_demand_pattern[pattern_id] = demand_pattern
|
|
830
|
+
|
|
831
|
+
if self._global_elevation is not None:
|
|
832
|
+
self._global_elevation.set_random_generator(np_rand_gen)
|
|
717
833
|
|
|
718
834
|
elevations = epanet_api.getNodeElevations()
|
|
719
|
-
|
|
835
|
+
self._cache_nodes_elevation = np.copy(elevations)
|
|
836
|
+
|
|
837
|
+
elevations = self._global_elevation.apply_batch(elevations)
|
|
720
838
|
epanet_api.setNodeElevations(elevations)
|
|
721
839
|
|
|
722
|
-
if self.
|
|
723
|
-
self.
|
|
840
|
+
if self._local_elevation is not None:
|
|
841
|
+
self._local_elevation.set_random_generator(np_rand_gen)
|
|
842
|
+
self._cache_nodes_elevation = epanet_api.getNodeElevations()
|
|
724
843
|
|
|
725
|
-
for node_id, uncertainty in self.
|
|
844
|
+
for node_id, uncertainty in self._local_elevation.items():
|
|
726
845
|
node_idx = epanet_api.getNodeIndex(node_id)
|
|
727
846
|
elevation = epanet_api.getNodeElevations(node_idx)
|
|
847
|
+
|
|
728
848
|
elevation = uncertainty.apply(elevation)
|
|
729
849
|
epanet_api.setNodeElevations(node_idx, elevation)
|
|
730
850
|
|
|
731
|
-
if self.
|
|
732
|
-
self.
|
|
851
|
+
if self._local_patterns is not None:
|
|
852
|
+
self._local_patterns.set_random_generator(np_rand_gen)
|
|
853
|
+
self._cache_patterns = {}
|
|
733
854
|
|
|
734
|
-
for pattern_id, uncertainty in self.
|
|
855
|
+
for pattern_id, uncertainty in self._local_patterns.items():
|
|
735
856
|
pattern_idx = epanet_api.getPatternIndex(pattern_id)
|
|
736
857
|
pattern_length = epanet_api.getPatternLengths(pattern_idx)
|
|
737
858
|
pattern = np.array([epanet_api.getPatternValue(pattern_idx, t+1)
|
|
738
859
|
for t in range(pattern_length)])
|
|
860
|
+
self._cache_patterns[pattern_idx] = pattern
|
|
861
|
+
|
|
739
862
|
pattern = uncertainty.apply_batch(pattern)
|
|
740
863
|
epanet_api.setPattern(pattern_idx, pattern)
|
|
741
864
|
|
|
742
865
|
if epanet_api.MSXFile is not None:
|
|
743
|
-
if self.
|
|
744
|
-
self.
|
|
866
|
+
if self._global_constants is not None:
|
|
867
|
+
self._global_constants.set_random_generator(np_rand_gen)
|
|
745
868
|
|
|
746
869
|
constants = np.array(epanet_api.getMSXConstantsValue())
|
|
747
|
-
|
|
870
|
+
self._cache_msx_patterns = np.copy(constants)
|
|
871
|
+
|
|
872
|
+
constants = self._global_constants.apply_batch(constants)
|
|
748
873
|
epanet_api.setMSXConstantsValue(constants)
|
|
749
874
|
|
|
750
|
-
if self.
|
|
751
|
-
self.
|
|
875
|
+
if self._local_constants:
|
|
876
|
+
self._local_constants.set_random_generator(np_rand_gen)
|
|
877
|
+
|
|
878
|
+
self._cache_msx_patterns = np.array(epanet_api.getMSXConstantsValue())
|
|
752
879
|
|
|
753
|
-
for constant_id, uncertainty in self.
|
|
880
|
+
for constant_id, uncertainty in self._local_constants.items():
|
|
754
881
|
idx = epanet_api.MSXgetindex(ToolkitConstants.MSX_CONSTANT, constant_id)
|
|
755
882
|
constant = epanet_api.msx.MSXgetconstant(idx)
|
|
883
|
+
|
|
756
884
|
constant = uncertainty.apply(constant)
|
|
757
885
|
epanet_api.msx.MSXsetconstant(idx, constant)
|
|
758
886
|
|
|
759
|
-
if self.
|
|
760
|
-
self.
|
|
887
|
+
if self._global_parameters is not None:
|
|
888
|
+
self._global_parameters.set_random_generator(np_rand_gen)
|
|
761
889
|
|
|
890
|
+
self._cache_msx_links_parameters = {}
|
|
762
891
|
parameters_pipes = epanet_api.getMSXParametersPipesValue()
|
|
763
892
|
for i, pipe_idx in enumerate(epanet_api.getLinkPipeIndex()):
|
|
764
893
|
if len(parameters_pipes[i]) == 0:
|
|
765
894
|
continue
|
|
766
895
|
|
|
767
|
-
|
|
896
|
+
self._cache_msx_links_parameters[pipe_idx] = np.array(parameters_pipes[i])
|
|
897
|
+
parameters_pipes_val = self._global_parameters.apply_batch(
|
|
768
898
|
np.array(parameters_pipes[i]))
|
|
769
899
|
epanet_api.setMSXParametersPipesValue(pipe_idx, parameters_pipes_val)
|
|
770
900
|
|
|
901
|
+
self._cache_msx_tanks_parameters = {}
|
|
771
902
|
parameters_tanks = epanet_api.getMSXParametersTanksValue()
|
|
772
903
|
for i, tank_idx in enumerate(epanet_api.getNodeTankIndex()):
|
|
773
904
|
if parameters_tanks[i] is None or len(parameters_tanks[i]) == 0:
|
|
774
905
|
continue
|
|
775
906
|
|
|
776
|
-
|
|
907
|
+
self._cache_msx_tanks_parameters[tank_idx] = np.array(parameters_tanks[i])
|
|
908
|
+
parameters_tanks_val = self._global_parameters.apply_batch(
|
|
777
909
|
np.array(parameters_tanks[i]))
|
|
778
910
|
epanet_api.setMSXParametersTanksValue(tank_idx, parameters_tanks_val)
|
|
779
911
|
|
|
780
|
-
if self.
|
|
781
|
-
self.
|
|
912
|
+
if self._local_parameters is not None:
|
|
913
|
+
self._local_parameters.set_random_generator(np_rand_gen)
|
|
914
|
+
self._cache_msx_links_parameters = {}
|
|
915
|
+
self._cache_msx_tanks_parameters = {}
|
|
782
916
|
|
|
783
|
-
for (param_id, item_type, item_id), uncertainty in self.
|
|
917
|
+
for (param_id, item_type, item_id), uncertainty in self._local_parameters.items():
|
|
784
918
|
idx, = epanet_api.getMSXParametersIndex([param_id])
|
|
785
919
|
|
|
786
920
|
if item_type == ToolkitConstants.MSX_NODE:
|
|
@@ -792,19 +926,27 @@ class ModelUncertainty(JsonSerializable):
|
|
|
792
926
|
"ToolkitConstants.MSX_NODE or ToolkitConstants.MSX_LINK")
|
|
793
927
|
|
|
794
928
|
parameter = epanet_api.msx.MSXgetparameter(item_type, item_idx, idx)
|
|
929
|
+
if item_type == ToolkitConstants.MSX_NODE:
|
|
930
|
+
self._cache_msx_tanks_parameters[item_idx] = parameter
|
|
931
|
+
elif item_type == ToolkitConstants.MSX_LINK:
|
|
932
|
+
self._cache_msx_links_parameters[item_idx] = parameter
|
|
933
|
+
|
|
795
934
|
parameter = uncertainty.apply(parameter)
|
|
796
935
|
epanet_api.msx.MSXsetparameter(item_type, item_idx, idx, parameter)
|
|
797
936
|
|
|
798
|
-
if self.
|
|
799
|
-
self.
|
|
937
|
+
if self._local_msx_patterns is not None:
|
|
938
|
+
self._local_msx_patterns.set_random_generator(np_rand_gen)
|
|
939
|
+
self._cache_msx_patterns = {}
|
|
800
940
|
|
|
801
|
-
for pattern_id, uncertainty in self.
|
|
941
|
+
for pattern_id, uncertainty in self._local_msx_patterns.items():
|
|
802
942
|
pattern_idx, = epanet_api.getMSXPatternsIndex([pattern_id])
|
|
803
943
|
pattern = epanet_api.getMSXConstantsValue([pattern_idx])
|
|
944
|
+
self._cache_msx_patterns[pattern_idx] = np.copy(pattern)
|
|
945
|
+
|
|
804
946
|
pattern = uncertainty.apply_batch(pattern)
|
|
805
947
|
epanet_api.setMSXPattern(pattern_idx, pattern)
|
|
806
948
|
else:
|
|
807
|
-
if self.
|
|
808
|
-
self.
|
|
809
|
-
self.
|
|
949
|
+
if self._local_msx_patterns is not None or self._local_parameters is not None or \
|
|
950
|
+
self._local_constants is not None or self._global_constants is not None or \
|
|
951
|
+
self._global_parameters is not None:
|
|
810
952
|
warnings.warn("Ignoring EPANET-MSX uncertainties because not .msx file was loaded")
|