OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
LinkData.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2//
3// Copyright 2026 Caleb Buahin
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
39
40#ifndef OPENSWMM_ENGINE_LINK_DATA_HPP
41#define OPENSWMM_ENGINE_LINK_DATA_HPP
42
43#include <vector>
44#include <cstdint>
45#include <string>
46#include <algorithm>
47
48namespace openswmm {
49
50// ============================================================================
51// Link type enumerations
52// ============================================================================
53
58enum class LinkType : int8_t {
60 PUMP = 1,
62 WEIR = 3,
64};
65
98
103enum class FlowClass : int8_t {
104 DRY = 0,
111};
112
113// ============================================================================
114// LinkData — SoA layout
115// ============================================================================
116
125struct LinkData {
126
127 // -----------------------------------------------------------------------
128 // Static connectivity — set at parse time
129 // -----------------------------------------------------------------------
130
132 std::vector<LinkType> type;
133
138 std::vector<int> node1;
139
144 std::vector<int> node2;
145
150 std::vector<double> offset1;
151
156 std::vector<double> offset2;
157
162 std::vector<double> q0;
163
168 std::vector<double> q_limit;
169
170 // -----------------------------------------------------------------------
171 // Conduit-specific properties (valid when type[i] == CONDUIT)
172 // -----------------------------------------------------------------------
173
175 std::vector<XsectShape> xsect_shape;
176
178 std::vector<double> xsect_y_full;
179
181 std::vector<double> xsect_a_full;
182
184 std::vector<double> xsect_w_max;
185
201 std::vector<double> xsect_geom1;
202 std::vector<double> xsect_geom2;
203 std::vector<double> xsect_geom3;
204 std::vector<double> xsect_geom4;
205
210 std::vector<int> xsect_curve;
211
212 // Phase 6: conduit-config fields (roughness, length, slope, mod_length,
213 // barrels, beta, rough_factor, q_full, q_max) moved to ConduitData in
214 // LinkSubtypes.hpp — the relational link side-tables are the single store.
215
220 std::vector<double> xsect_r_full;
221
226 std::vector<double> xsect_s_full;
227
232 std::vector<double> xsect_s_max;
233
238 std::vector<double> xsect_y_bot;
239
244 std::vector<double> xsect_a_bot;
245
251 std::vector<double> xsect_s_bot;
252
257 std::vector<double> xsect_r_bot;
258
263 std::vector<double> xsect_yw_max;
264
270 std::vector<int> xsect_batch_shape;
271
276 std::vector<double> setting;
277
282 std::vector<double> target_setting;
283
292 std::vector<double> time_last_set;
293
298 std::vector<int> direction;
299
300 // -----------------------------------------------------------------------
301 // Pump-specific properties (valid when type[i] == PUMP)
302 // -----------------------------------------------------------------------
303
304 // Phase 6: pump-config fields (pump_curve, pump_init_state, pump_startup,
305 // pump_shutoff, pump_curve_type) moved to PumpData in LinkSubtypes.hpp.
306 // pump_curve_name stays on base (shared with outlet/conduit-IRREGULAR).
307
309 std::vector<std::string> pump_curve_name;
310
311 // -----------------------------------------------------------------------
312 // Conduit loss coefficients
313 // -----------------------------------------------------------------------
314
315 // Phase 6: conduit loss/seep/culvert + per-step state (loss_inlet,
316 // loss_outlet, loss_avg, seep_rate, evap_loss_rate, seep_loss_rate,
317 // culvert_code, normal_flow_limited, inlet_control) moved to ConduitData.
318 // has_flap_gate and dqdh stay on base (shared / multi-owner).
319
321 std::vector<uint8_t> has_flap_gate;
322
327 std::vector<double> dqdh;
328
329 // -----------------------------------------------------------------------
330 // Weir / Orifice / Outlet — shared geometric properties
331 // -----------------------------------------------------------------------
332
333 // Phase 6: weir/orifice/outlet config (crest_height, cd, param1, param2,
334 // orate) moved to OrificeData/WeirData/OutletData in LinkSubtypes.hpp.
335
336 // -----------------------------------------------------------------------
337 // State variables — updated each timestep
338 // -----------------------------------------------------------------------
339
344 std::vector<double> flow;
345
350 std::vector<double> depth;
351
356 std::vector<double> volume;
357
365 std::vector<double> slot_volume;
366
367 // Slot-storage share statistics (FV slot program R0). All stay 0.0
368 // under the dynamic-wave router. The share of a link at an instant is
369 // slot_volume/volume; the run-level share is the ratio of the two
370 // time integrals below, never an average of instantaneous ratios.
371
373 std::vector<double> stat_slot_vol_dt;
375 std::vector<double> stat_vol_dt;
377 std::vector<double> stat_peak_slot_share;
379 std::vector<double> stat_time_slot_above;
380
385 std::vector<double> froude;
386
388 std::vector<FlowClass> flow_class;
389
391 std::vector<uint8_t> is_closed;
392
393 // Phase 6: full_state (per-step up/down full bitmask) moved to ConduitData.
394
395 // -----------------------------------------------------------------------
396 // Previous-step state
397 // -----------------------------------------------------------------------
398
400 std::vector<double> old_flow;
401
403 std::vector<double> old_depth;
404
406 std::vector<double> old_volume;
407
408 // -----------------------------------------------------------------------
409 // Per-link quality state — flat 2D: [link * n_pollutants + pollutant]
410 // -----------------------------------------------------------------------
411
417 std::vector<double> conc;
418
420 std::vector<double> conc_old;
421
424
425 // -----------------------------------------------------------------------
426 // Per-object INP comment
427 // -----------------------------------------------------------------------
428
434 std::vector<std::string> comments;
435
442 std::vector<std::string> tags;
443
444 // -----------------------------------------------------------------------
445 // Report flag — per-object output filter
446 // -----------------------------------------------------------------------
447
450 std::vector<char> rpt_flag;
451
452 // -----------------------------------------------------------------------
453 // Cumulative statistics
454 // -----------------------------------------------------------------------
455
460 std::vector<double> stat_vol_flow;
461
466 std::vector<double> stat_max_flow;
467
472 std::vector<double> stat_max_veloc;
473
478 std::vector<double> stat_max_filling;
479
484 std::vector<double> stat_time_surcharged;
485
492 static constexpr int N_FLOW_CLASSES = 7;
493 std::vector<long> stat_flow_class;
494 std::vector<long> stat_norm_ltd;
495 std::vector<long> stat_inlet_ctrl;
496
499 std::vector<double> stat_max_flow_date;
500
503 std::vector<double> stat_time_full_upstream;
504
507 std::vector<double> stat_time_full_dnstream;
508
511 std::vector<double> stat_time_full_both;
512
515 std::vector<double> stat_time_capacity_limited;
516
523 std::vector<double> stat_total_load;
525
526 // -----------------------------------------------------------------------
527 // Pump utilization statistics (valid when type[i] == PUMP)
528 // -----------------------------------------------------------------------
529
531 std::vector<int> stat_pump_cycles;
532
534 std::vector<double> stat_pump_on_time;
535
537 std::vector<double> stat_pump_volume;
538
544 std::vector<double> stat_pump_energy;
545
547 std::vector<bool> stat_pump_was_on;
548
556 std::vector<long> stat_flow_turns;
557
564 std::vector<int> stat_flow_turn_sign;
565
572 std::vector<double> stat_time_courant_critical;
573
574 // -----------------------------------------------------------------------
575 // Capacity management
576 // -----------------------------------------------------------------------
577
578 int count() const noexcept { return static_cast<int>(type.size()); }
579
580 void resize(int n) {
581 const auto un = static_cast<std::size_t>(n);
582
583 type.assign(un, LinkType::CONDUIT);
584 node1.assign(un, -1);
585 node2.assign(un, -1);
586 offset1.assign(un, 0.0);
587 offset2.assign(un, 0.0);
588 q0.assign(un, 0.0);
589 q_limit.assign(un, 0.0);
590
592 xsect_y_full.assign(un, 0.0);
593 xsect_a_full.assign(un, 0.0);
594 xsect_w_max.assign(un, 0.0);
595 xsect_geom1.assign(un, 0.0);
596 xsect_geom2.assign(un, 0.0);
597 xsect_geom3.assign(un, 0.0);
598 xsect_geom4.assign(un, 0.0);
599 xsect_curve.assign(un, -1);
600 xsect_r_full.assign(un, 0.0);
601 xsect_s_full.assign(un, 0.0);
602 xsect_s_max.assign(un, 0.0);
603 xsect_y_bot.assign(un, 0.0);
604 xsect_a_bot.assign(un, 0.0);
605 xsect_s_bot.assign(un, 0.0);
606 xsect_r_bot.assign(un, 0.0);
607 xsect_yw_max.assign(un, 0.0);
608 xsect_batch_shape.assign(un, 0);
609 setting.assign(un, 1.0);
610 target_setting.assign(un, 1.0);
611 time_last_set.assign(un, 0.0);
612 direction.assign(un, 1);
613
614 has_flap_gate.assign(un, 0);
615 dqdh.assign(un, 0.0);
616 pump_curve_name.resize(un);
617
618 flow.assign(un, 0.0);
619 depth.assign(un, 0.0);
620 volume.assign(un, 0.0);
621 slot_volume.assign(un, 0.0);
622 stat_slot_vol_dt.assign(un, 0.0);
623 stat_vol_dt.assign(un, 0.0);
624 stat_peak_slot_share.assign(un, 0.0);
625 stat_time_slot_above.assign(un, 0.0);
626 froude.assign(un, 0.0);
627 flow_class.assign(un, FlowClass::DRY);
628 is_closed.assign(un, 0);
629 old_flow.assign(un, 0.0);
630 old_depth.assign(un, 0.0);
631 old_volume.assign(un, 0.0);
632
633 comments.assign(un, std::string{});
634 tags.assign(un, std::string{});
635
636 rpt_flag.assign(un, 0);
637
638 stat_vol_flow.assign(un, 0.0);
639 stat_max_flow.assign(un, 0.0);
640 stat_max_veloc.assign(un, 0.0);
641 stat_max_filling.assign(un, 0.0);
642 stat_time_surcharged.assign(un, 0.0);
643 stat_flow_class.assign(un * N_FLOW_CLASSES, 0L);
644 stat_norm_ltd.assign(un, 0L);
645 stat_inlet_ctrl.assign(un, 0L);
646 stat_max_flow_date.assign(un, 0.0);
647 stat_time_full_upstream.assign(un, 0.0);
648 stat_time_full_dnstream.assign(un, 0.0);
649 stat_time_full_both.assign(un, 0.0);
650 stat_time_capacity_limited.assign(un, 0.0);
651 stat_pump_cycles.assign(un, 0);
652 stat_pump_on_time.assign(un, 0.0);
653 stat_pump_volume.assign(un, 0.0);
654 stat_pump_energy.assign(un, 0.0);
655 stat_pump_was_on.assign(un, false);
656 stat_flow_turns.assign(un, 0L);
657 stat_flow_turn_sign.assign(un, 0);
658 stat_time_courant_critical.assign(un, 0.0);
659 }
660
664 void grow_to(int n) {
665 if (n <= count()) return;
666 const auto un = static_cast<std::size_t>(n);
667 auto g = [&](auto& vec, auto def) { vec.resize(un, def); };
668 g(type, LinkType::CONDUIT); g(node1, -1); g(node2, -1);
669 g(offset1, 0.0); g(offset2, 0.0); g(q0, 0.0); g(q_limit, 0.0);
671 g(xsect_y_full, 0.0); g(xsect_a_full, 0.0); g(xsect_w_max, 0.0);
672 g(xsect_geom1, 0.0); g(xsect_geom2, 0.0); g(xsect_geom3, 0.0); g(xsect_geom4, 0.0);
673 g(xsect_curve, -1);
674 g(xsect_r_full, 0.0); g(xsect_s_full, 0.0); g(xsect_s_max, 0.0);
675 g(xsect_y_bot, 0.0); g(xsect_a_bot, 0.0);
676 g(xsect_s_bot, 0.0); g(xsect_r_bot, 0.0);
677 g(xsect_yw_max, 0.0); g(xsect_batch_shape, 0);
678 g(setting, 1.0); g(target_setting, 1.0); g(time_last_set, 0.0);
679 g(direction, 1);
680 g(has_flap_gate, uint8_t{0}); g(dqdh, 0.0);
681 pump_curve_name.resize(un);
682 g(flow, 0.0); g(depth, 0.0); g(volume, 0.0); g(slot_volume, 0.0);
683 g(stat_slot_vol_dt, 0.0); g(stat_vol_dt, 0.0);
685 g(froude, 0.0); g(flow_class, FlowClass::DRY); g(is_closed, uint8_t{0});
686 g(old_flow, 0.0); g(old_depth, 0.0); g(old_volume, 0.0);
687 comments.resize(un, std::string{});
688 tags.resize(un, std::string{});
689
690 g(rpt_flag, static_cast<char>(0));
691 g(stat_vol_flow, 0.0); g(stat_max_flow, 0.0);
692 g(stat_max_veloc, 0.0); g(stat_max_filling, 0.0);
696 g(stat_pump_cycles, 0); g(stat_pump_on_time, 0.0);
697 g(stat_pump_volume, 0.0); g(stat_pump_energy, 0.0);
698 g(stat_pump_was_on, false);
701 g(stat_norm_ltd, 0L); g(stat_inlet_ctrl, 0L);
702 // stat_flow_class is flat 2D [n * N_FLOW_CLASSES]
703 stat_flow_class.resize(un * N_FLOW_CLASSES, 0L);
704 // Note: conc, conc_old handled by resize_quality()
705 // Note: stat_total_load handled by resize_loads()
706 }
707
726 void reserve_to(int n) {
727 if (n <= 0) return;
728 const auto un = static_cast<std::size_t>(n);
729 if (type.capacity() >= un) return;
730 auto r = [&](auto& vec) { vec.reserve(un); };
731 r(type); r(node1); r(node2); r(offset1);
732 r(offset2); r(q0); r(q_limit); r(xsect_shape);
738 r(direction); r(has_flap_gate); r(dqdh); r(flow);
739 r(depth); r(volume); r(froude); r(flow_class);
740 r(is_closed); r(old_flow); r(old_depth); r(old_volume);
748 }
749
758 void erase_at(int idx) {
759 const auto ui = static_cast<std::size_t>(idx);
760 auto e = [&](auto& v) { if (ui < v.size()) v.erase(v.begin() + static_cast<std::ptrdiff_t>(idx)); };
761
762 e(type); e(node1); e(node2); e(offset1); e(offset2); e(q0); e(q_limit);
763
768
770
771 e(has_flap_gate); e(dqdh);
772
773 e(flow); e(depth); e(volume); e(froude); e(flow_class); e(is_closed);
774 e(old_flow); e(old_depth); e(old_volume);
775 e(comments); e(tags); e(rpt_flag);
776
784
785 // Flat 2D: stat_flow_class [link * N_FLOW_CLASSES + class]
786 {
787 const auto base = ui * static_cast<std::size_t>(N_FLOW_CLASSES);
788 const auto end = base + static_cast<std::size_t>(N_FLOW_CLASSES);
789 if (end <= stat_flow_class.size())
790 stat_flow_class.erase(stat_flow_class.begin() + static_cast<std::ptrdiff_t>(base),
791 stat_flow_class.begin() + static_cast<std::ptrdiff_t>(end));
792 }
793
794 // Flat 2D quality arrays: [link * np + p]
795 if (conc_n_pollutants > 0) {
796 const auto np = static_cast<std::size_t>(conc_n_pollutants);
797 const auto base = ui * np;
798 auto erase2d = [&](auto& v) {
799 if (base + np <= v.size())
800 v.erase(v.begin() + static_cast<std::ptrdiff_t>(base),
801 v.begin() + static_cast<std::ptrdiff_t>(base + np));
802 };
803 erase2d(conc); erase2d(conc_old);
804 }
805
806 // Flat 2D stat load: [link * np + p]
807 if (stat_n_pollutants > 0) {
808 const auto np = static_cast<std::size_t>(stat_n_pollutants);
809 const auto base = ui * np;
810 if (base + np <= stat_total_load.size())
811 stat_total_load.erase(
812 stat_total_load.begin() + static_cast<std::ptrdiff_t>(base),
813 stat_total_load.begin() + static_cast<std::ptrdiff_t>(base + np));
814 }
815 }
816
820 void resize_loads(int n_pollutants) {
821 stat_n_pollutants = n_pollutants;
822 if (n_pollutants > 0) {
823 auto total = static_cast<std::size_t>(count()) *
824 static_cast<std::size_t>(n_pollutants);
825 stat_total_load.assign(total, 0.0);
826 }
827 }
828
832 void resize_quality(int n_pollutants) {
833 conc_n_pollutants = n_pollutants;
834 if (n_pollutants > 0) {
835 auto total = static_cast<std::size_t>(count()) *
836 static_cast<std::size_t>(n_pollutants);
837 conc.assign(total, 0.0);
838 conc_old.assign(total, 0.0);
839 }
840 }
841
846 type.shrink_to_fit();
847 node1.shrink_to_fit();
848 node2.shrink_to_fit();
849 offset1.shrink_to_fit();
850 offset2.shrink_to_fit();
851 q0.shrink_to_fit();
852 q_limit.shrink_to_fit();
853
854 xsect_shape.shrink_to_fit();
855 xsect_y_full.shrink_to_fit();
856 xsect_a_full.shrink_to_fit();
857 xsect_w_max.shrink_to_fit();
858 xsect_curve.shrink_to_fit();
859 xsect_r_full.shrink_to_fit();
860 xsect_s_full.shrink_to_fit();
861 xsect_s_max.shrink_to_fit();
862 xsect_y_bot.shrink_to_fit();
863 xsect_a_bot.shrink_to_fit();
864 xsect_s_bot.shrink_to_fit();
865 xsect_r_bot.shrink_to_fit();
866 xsect_yw_max.shrink_to_fit();
867 xsect_batch_shape.shrink_to_fit();
868 setting.shrink_to_fit();
869 target_setting.shrink_to_fit();
870 direction.shrink_to_fit();
871
872 has_flap_gate.shrink_to_fit();
873 dqdh.shrink_to_fit();
874 pump_curve_name.shrink_to_fit();
875
876 flow.shrink_to_fit();
877 depth.shrink_to_fit();
878 volume.shrink_to_fit();
879 froude.shrink_to_fit();
880 flow_class.shrink_to_fit();
881 is_closed.shrink_to_fit();
882 old_flow.shrink_to_fit();
883 old_depth.shrink_to_fit();
884 old_volume.shrink_to_fit();
885 conc.shrink_to_fit();
886 conc_old.shrink_to_fit();
887
888 comments.shrink_to_fit();
889 tags.shrink_to_fit();
890
891 rpt_flag.shrink_to_fit();
892
893 stat_vol_flow.shrink_to_fit();
894 stat_max_flow.shrink_to_fit();
895 stat_max_veloc.shrink_to_fit();
896 stat_max_filling.shrink_to_fit();
897 stat_time_surcharged.shrink_to_fit();
898 stat_flow_class.shrink_to_fit();
899 stat_norm_ltd.shrink_to_fit();
900 stat_inlet_ctrl.shrink_to_fit();
901 stat_max_flow_date.shrink_to_fit();
902 stat_time_full_upstream.shrink_to_fit();
903 stat_time_full_dnstream.shrink_to_fit();
904 stat_time_full_both.shrink_to_fit();
905 stat_time_capacity_limited.shrink_to_fit();
906 stat_pump_cycles.shrink_to_fit();
907 stat_pump_on_time.shrink_to_fit();
908 stat_pump_volume.shrink_to_fit();
909 stat_pump_energy.shrink_to_fit();
910 stat_pump_was_on.shrink_to_fit();
911 stat_total_load.shrink_to_fit();
912 }
913
914 void save_state() noexcept {
915 std::copy(flow.begin(), flow.end(), old_flow.begin());
916 std::copy(depth.begin(), depth.end(), old_depth.begin());
917 std::copy(volume.begin(), volume.end(), old_volume.begin());
918 std::copy(conc.begin(), conc.end(), conc_old.begin());
919 }
920
921 void reset_state() noexcept {
922 std::fill(flow.begin(), flow.end(), 0.0);
923 std::fill(depth.begin(), depth.end(), 0.0);
924 std::fill(volume.begin(), volume.end(), 0.0);
925 std::fill(froude.begin(), froude.end(), 0.0);
926 std::fill(flow_class.begin(), flow_class.end(), FlowClass::DRY);
927 std::fill(old_flow.begin(), old_flow.end(), 0.0);
928 std::fill(old_depth.begin(), old_depth.end(), 0.0);
929 std::fill(old_volume.begin(), old_volume.end(), 0.0);
930 std::fill(conc.begin(), conc.end(), 0.0);
931 std::fill(conc_old.begin(), conc_old.end(), 0.0);
932 }
933};
934
935} /* namespace openswmm */
936
937#endif /* OPENSWMM_ENGINE_LINK_DATA_HPP */
Definition NodeCoupling.cpp:16
LinkType
Link type codes.
Definition LinkData.hpp:58
@ CONDUIT
Definition LinkData.hpp:59
@ PUMP
Definition LinkData.hpp:60
@ WEIR
Definition LinkData.hpp:62
@ ORIFICE
Definition LinkData.hpp:61
@ OUTLET
Definition LinkData.hpp:63
FlowClass
Link flow state.
Definition LinkData.hpp:103
@ SUPERCRITICAL
Definition LinkData.hpp:108
@ DN_CRITICAL
Definition LinkData.hpp:110
@ SUBCRITICAL
Definition LinkData.hpp:107
@ UP_CRITICAL
Definition LinkData.hpp:109
@ DRY
Definition LinkData.hpp:104
@ UP_DRY
Definition LinkData.hpp:105
@ DN_DRY
Definition LinkData.hpp:106
XsectShape
Conduit cross-section shape code.
Definition LinkData.hpp:70
@ TRIANGULAR
Definition LinkData.hpp:76
@ RECT_CLOSED
Definition LinkData.hpp:73
@ ARCH
Arch pipe.
Definition LinkData.hpp:91
@ FORCE_MAIN
Circular force main (Hazen-Williams or D-W)
Definition LinkData.hpp:94
@ RECT_OPEN
Definition LinkData.hpp:74
@ BASKETHANDLE
Definition LinkData.hpp:85
@ STREET_XSECT
Street cross-section.
Definition LinkData.hpp:95
@ SEMICIRCULAR
Definition LinkData.hpp:86
@ IRREGULAR
User-supplied shape curve.
Definition LinkData.hpp:92
@ HORIZ_ELLIPSE
Horizontal elliptical pipe.
Definition LinkData.hpp:89
@ VERT_ELLIPSE
Vertical elliptical pipe.
Definition LinkData.hpp:90
@ CUSTOM
Shape from CURVE_SHAPE table.
Definition LinkData.hpp:93
@ RECT_ROUND
Rectangular-round bottom.
Definition LinkData.hpp:88
@ EGGSHAPED
Definition LinkData.hpp:80
@ SEMIELLIPTICAL
Definition LinkData.hpp:84
@ CATENARY
Definition LinkData.hpp:83
@ CIRCULAR
Definition LinkData.hpp:71
@ TRAPEZOIDAL
Definition LinkData.hpp:75
@ MODBASKETHANDLE
Definition LinkData.hpp:79
@ HORSESHOE
Definition LinkData.hpp:81
@ DUMMY
Dummy (no geometry)
Definition LinkData.hpp:96
@ PARABOLIC
Definition LinkData.hpp:77
@ POWER
Definition LinkData.hpp:78
@ RECT_TRIANG
Rectangular-triangular bottom.
Definition LinkData.hpp:87
@ FILLED_CIRCULAR
Definition LinkData.hpp:72
@ GOTHIC
Definition LinkData.hpp:82