mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-10-30 17:47:50 +00:00
Compare commits
561 Commits
v1.0
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
049b077ee4 | ||
|
|
ab2080a805 | ||
|
|
8ab54e76df | ||
|
|
0465397b1d | ||
|
|
d3ec39d506 | ||
|
|
bfe68a5948 | ||
|
|
2ea3363613 | ||
|
|
89cce6e6a3 | ||
|
|
0f10ac706c | ||
|
|
66d26f0ffa | ||
|
|
3c96914482 | ||
|
|
61b1e73362 | ||
|
|
03435079cc | ||
|
|
6661907c1d | ||
|
|
fe811f725c | ||
|
|
f015c6abed | ||
|
|
8598e6591f | ||
|
|
5e2259062c | ||
|
|
d483802a86 | ||
|
|
9a0eece69c | ||
|
|
1657bfd05f | ||
|
|
49bf558916 | ||
|
|
99f44a597b | ||
|
|
71293c1c18 | ||
|
|
bd7c70f902 | ||
|
|
20c92d4338 | ||
|
|
f0158e7202 | ||
|
|
62408ddc98 | ||
|
|
b4f87b36db | ||
|
|
9f49a115a1 | ||
|
|
c7d2305589 | ||
|
|
5826a649de | ||
|
|
fa826391f6 | ||
|
|
3481ba8ee3 | ||
|
|
b4ab0b55de | ||
|
|
0370b45d8a | ||
|
|
468e689094 | ||
|
|
aafd82b16d | ||
|
|
60ee331153 | ||
|
|
3a8ce74355 | ||
|
|
fd44463238 | ||
|
|
a21f3fe6ee | ||
|
|
0ccbb2960c | ||
|
|
84ba2da553 | ||
|
|
e693d96ca1 | ||
|
|
81cb7f8133 | ||
|
|
3471969956 | ||
|
|
7a0985c362 | ||
|
|
b79a9e2e67 | ||
|
|
1e037fe6f5 | ||
|
|
0897be57c1 | ||
|
|
4172b06b19 | ||
|
|
32a4875e46 | ||
|
|
c577a75725 | ||
|
|
8827e0cf6f | ||
|
|
b0012fe399 | ||
|
|
31e634615b | ||
|
|
8300a55e39 | ||
|
|
5b939bc57a | ||
|
|
2f1ab9cc50 | ||
|
|
42ba3eb98d | ||
|
|
9eb87fc8e1 | ||
|
|
8fab9bb945 | ||
|
|
1ead232a78 | ||
|
|
b15c8c60ab | ||
|
|
66bdeb0e4d | ||
|
|
1a2e090104 | ||
|
|
a8e280e29b | ||
|
|
edb54b02ac | ||
|
|
83d3f32fe0 | ||
|
|
085a379592 | ||
|
|
37bd5d0404 | ||
|
|
f788b81d21 | ||
|
|
2ff1ce6b34 | ||
|
|
41a1e40d14 | ||
|
|
921e8d2d3c | ||
|
|
c009d28f7d | ||
|
|
898eada097 | ||
|
|
bdfc55e801 | ||
|
|
57f264bedb | ||
|
|
fbe4fa3cf0 | ||
|
|
b2ef345f35 | ||
|
|
471ea7dfba | ||
|
|
1b52f638ff | ||
|
|
84ab38a75f | ||
|
|
916e5377f8 | ||
|
|
534bfd881e | ||
|
|
7c4015324d | ||
|
|
8499ee52f4 | ||
|
|
cc1123863c | ||
|
|
ca382806f6 | ||
|
|
3559fc61c2 | ||
|
|
33581cdcc9 | ||
|
|
991eb02964 | ||
|
|
286e321a2d | ||
|
|
56f158113d | ||
|
|
024f6ff963 | ||
|
|
8118a0f4f4 | ||
|
|
eb89d8fd86 | ||
|
|
a938c1738b | ||
|
|
4f88882513 | ||
|
|
4e8d8b7ddd | ||
|
|
afb7d75749 | ||
|
|
488d0e1fe8 | ||
|
|
708442e4cd | ||
|
|
a2d905dfb1 | ||
|
|
d564fe3e2a | ||
|
|
ea21cce1c0 | ||
|
|
aa9b4aefbe | ||
|
|
8655030e59 | ||
|
|
8107ddeb79 | ||
|
|
76c8e55f06 | ||
|
|
a7b1ab47d8 | ||
|
|
879f587ab9 | ||
|
|
8af2d80219 | ||
|
|
315eea1f55 | ||
|
|
b61e541e15 | ||
|
|
81f88e78c7 | ||
|
|
87cc3dac00 | ||
|
|
89e28cc7be | ||
|
|
2ba29a78c5 | ||
|
|
f990a6c1be | ||
|
|
424e5a4786 | ||
|
|
6a7a04ebb1 | ||
|
|
0366fc2956 | ||
|
|
48b7d71f02 | ||
|
|
715baf2a1c | ||
|
|
e55cea776e | ||
|
|
b388d143fd | ||
|
|
c592c572d8 | ||
|
|
dfa0a26a28 | ||
|
|
609cd94798 | ||
|
|
022f743db1 | ||
|
|
1957beb1b6 | ||
|
|
9ca72d6105 | ||
|
|
e8e126a6ce | ||
|
|
7849782173 | ||
|
|
149a0da8c9 | ||
|
|
1e7c70a59b | ||
|
|
c9d8282e7f | ||
|
|
e7084a2c29 | ||
|
|
d79d2e0724 | ||
|
|
402155c225 | ||
|
|
e5ec669419 | ||
|
|
8f424e8c9d | ||
|
|
fea2b84bb9 | ||
|
|
0c918940c4 | ||
|
|
a63a6ac0ec | ||
|
|
9f58b914d2 | ||
|
|
029bac4b03 | ||
|
|
a27ad57220 | ||
|
|
8d31d924f2 | ||
|
|
8c3b514f90 | ||
|
|
3df27fe315 | ||
|
|
a6087ce354 | ||
|
|
aae0382523 | ||
|
|
b0c2acb1b5 | ||
|
|
a52c96ae2e | ||
|
|
bf28821b5b | ||
|
|
328bd6ea71 | ||
|
|
ec9eb8d054 | ||
|
|
f8c8526045 | ||
|
|
d8c236bb44 | ||
|
|
33ff0910b8 | ||
|
|
faa69917d9 | ||
|
|
d9f5ca9827 | ||
|
|
c817ef7335 | ||
|
|
07de489d6b | ||
|
|
acafc78456 | ||
|
|
325721545e | ||
|
|
dbe2bf560c | ||
|
|
7872cc2203 | ||
|
|
25b4d0e755 | ||
|
|
9af1c90664 | ||
|
|
6b4d44a3f1 | ||
|
|
2faf8d2cdd | ||
|
|
676c94ddf2 | ||
|
|
6f93b64f84 | ||
|
|
54bf426472 | ||
|
|
1862ce9104 | ||
|
|
3771c13d32 | ||
|
|
f1d0230dad | ||
|
|
182929cc96 | ||
|
|
81585c5a86 | ||
|
|
2f52c11589 | ||
|
|
0f4d8573cf | ||
|
|
660b8b3c6e | ||
|
|
71d6a1138c | ||
|
|
a6e741d8fe | ||
|
|
58bcf65cf6 | ||
|
|
27ce55de38 | ||
|
|
36ca22db9b | ||
|
|
33a8de9b39 | ||
|
|
22b76e36db | ||
|
|
528ff31590 | ||
|
|
4d6966cbd3 | ||
|
|
9c9e3be967 | ||
|
|
2dd4745ef7 | ||
|
|
4e786a32b5 | ||
|
|
6ecb2c85e2 | ||
|
|
cd234a909b | ||
|
|
c249f44ea1 | ||
|
|
ed1f51393a | ||
|
|
8bd43130ab | ||
|
|
6c975a53a1 | ||
|
|
8a1001cd40 | ||
|
|
beb2b576aa | ||
|
|
8f3923046b | ||
|
|
88c68d2065 | ||
|
|
8bcde72a10 | ||
|
|
4653dbcf4b | ||
|
|
cde08b32a4 | ||
|
|
2eed891f8d | ||
|
|
c0b84e84c8 | ||
|
|
2c20fd3f9f | ||
|
|
f4db56ca29 | ||
|
|
5b6d58ac7d | ||
|
|
ecb8bd9fbe | ||
|
|
2f9385451f | ||
|
|
1a1346461b | ||
|
|
27dcd29074 | ||
|
|
93986f36c3 | ||
|
|
a6ab8055b1 | ||
|
|
31ea479d7f | ||
|
|
89fb2e047b | ||
|
|
8f705e6173 | ||
|
|
8f735316f5 | ||
|
|
0d7a1871a1 | ||
|
|
33832b3d25 | ||
|
|
4da7f0cc38 | ||
|
|
e29f8485ea | ||
|
|
2da344a563 | ||
|
|
2a0cb8e14f | ||
|
|
e1dc3dc357 | ||
|
|
8259124f73 | ||
|
|
0422956ac6 | ||
|
|
ff82c5171b | ||
|
|
f9bd6310f1 | ||
|
|
471eab126e | ||
|
|
6ad011d12d | ||
|
|
561c8aff85 | ||
|
|
5cf5dd2234 | ||
|
|
fb9915d301 | ||
|
|
7ab67194d6 | ||
|
|
603ac9d8c5 | ||
|
|
a3c7811e9d | ||
|
|
a3778dfe8b | ||
|
|
2dff934612 | ||
|
|
89d666948e | ||
|
|
c3499142b0 | ||
|
|
d8feccc715 | ||
|
|
16173355f3 | ||
|
|
f46134fda5 | ||
|
|
bfecff0412 | ||
|
|
168f1891cf | ||
|
|
862845b4ac | ||
|
|
b7a5dbff49 | ||
|
|
5be30d89a7 | ||
|
|
d94dc51d88 | ||
|
|
22acd88d44 | ||
|
|
fd406c106b | ||
|
|
16134b5caf | ||
|
|
2c485efced | ||
|
|
279d08a0e8 | ||
|
|
1d4a8998e1 | ||
|
|
47a41e7980 | ||
|
|
ecfc4a8cb2 | ||
|
|
2d66b6266b | ||
|
|
b7afb5f9d2 | ||
|
|
58c16a59ac | ||
|
|
f09789f5ef | ||
|
|
b2e12cd3e0 | ||
|
|
71b157a8ba | ||
|
|
cb8affe9b2 | ||
|
|
3f7180c706 | ||
|
|
f0bc2dc62f | ||
|
|
9c95fd6b69 | ||
|
|
c0fda8c3a2 | ||
|
|
bac20af381 | ||
|
|
626211a320 | ||
|
|
783aaa8cb4 | ||
|
|
768bd8af19 | ||
|
|
3894f52194 | ||
|
|
dcfa9edb1c | ||
|
|
4ebdb5629c | ||
|
|
75b0668fc2 | ||
|
|
5fe94ed463 | ||
|
|
db21b97603 | ||
|
|
8074d0c548 | ||
|
|
2d611afbb0 | ||
|
|
bc42507724 | ||
|
|
ff82ab5718 | ||
|
|
62fe374e15 | ||
|
|
e519a3bc39 | ||
|
|
4f146d12ee | ||
|
|
46d6074ad5 | ||
|
|
cbb61f1240 | ||
|
|
0e9f3c3576 | ||
|
|
92f11dc075 | ||
|
|
3aa0a0999b | ||
|
|
e86fbcfa5b | ||
|
|
0b2ee6fdaf | ||
|
|
35f3866882 | ||
|
|
d86bea80d3 | ||
|
|
13b4b5072f | ||
|
|
efae43f122 | ||
|
|
45e8c8692b | ||
|
|
f8fc2a5050 | ||
|
|
3c20d57cc4 | ||
|
|
2cb3858330 | ||
|
|
925d36a561 | ||
|
|
0ffaca91cc | ||
|
|
d3a0f1d969 | ||
|
|
37704db583 | ||
|
|
aadd038bbe | ||
|
|
6d601b4267 | ||
|
|
194798d881 | ||
|
|
1a10495645 | ||
|
|
0e2316513e | ||
|
|
72ce4e2fad | ||
|
|
4ad7311e18 | ||
|
|
fa2b0e8fad | ||
|
|
78eb926693 | ||
|
|
3613efbaab | ||
|
|
2e732854b3 | ||
|
|
f9560d6b1d | ||
|
|
51b0826398 | ||
|
|
af0adb454d | ||
|
|
cdd4c571b0 | ||
|
|
9c440764c7 | ||
|
|
6e94834033 | ||
|
|
1720ed23c9 | ||
|
|
137fab1d92 | ||
|
|
0fee63fa81 | ||
|
|
d5f0d80eed | ||
|
|
b7d4d43f56 | ||
|
|
c0379a1981 | ||
|
|
e5db8e42d1 | ||
|
|
27cf9806f0 | ||
|
|
5dbb5cd112 | ||
|
|
af75569eb8 | ||
|
|
aebf2ff270 | ||
|
|
3bcdeda3e9 | ||
|
|
7433667243 | ||
|
|
d7c009167f | ||
|
|
79f198d6fe | ||
|
|
315a12b9df | ||
|
|
e14d145f2c | ||
|
|
f839da39f0 | ||
|
|
45ca7a63ed | ||
|
|
5d187255ae | ||
|
|
7adf6aed59 | ||
|
|
b22a7a0234 | ||
|
|
8805723114 | ||
|
|
88db4358f5 | ||
|
|
a5d9685caf | ||
|
|
94ff8e6beb | ||
|
|
f3400d9bc1 | ||
|
|
6a6591e41d | ||
|
|
a3a53f3b06 | ||
|
|
2f39abfdb8 | ||
|
|
92239d66fc | ||
|
|
178813806f | ||
|
|
265dbffc53 | ||
|
|
a67a08a4d0 | ||
|
|
6d15f55304 | ||
|
|
0dbcd1f265 | ||
|
|
a0f6380f90 | ||
|
|
ab69cf5bf4 | ||
|
|
3e55a5526a | ||
|
|
b6216bb701 | ||
|
|
fa3ea3aaa7 | ||
|
|
7b56e3a6c3 | ||
|
|
00ad542a11 | ||
|
|
c3e00eea2c | ||
|
|
5c8dd911e3 | ||
|
|
407fd62da5 | ||
|
|
c92f7ca0d8 | ||
|
|
676901e113 | ||
|
|
c099b53a03 | ||
|
|
fd065e4e7c | ||
|
|
fd97527561 | ||
|
|
7b9647a063 | ||
|
|
bf943f1347 | ||
|
|
dbff610d77 | ||
|
|
46aae9486e | ||
|
|
70066de390 | ||
|
|
106af9d444 | ||
|
|
f0be267f9f | ||
|
|
edbaec7265 | ||
|
|
f4537a538b | ||
|
|
d6eb6f33d2 | ||
|
|
7a139c261a | ||
|
|
2e7aa213ed | ||
|
|
d9344287e4 | ||
|
|
b9768a81e9 | ||
|
|
3418c07512 | ||
|
|
5f8621c224 | ||
|
|
c05f3555a3 | ||
|
|
f21827395b | ||
|
|
894c7bb17a | ||
|
|
2028cfae4d | ||
|
|
2064f65c04 | ||
|
|
1de4a4eaa2 | ||
|
|
06ff45d0c2 | ||
|
|
81474e252e | ||
|
|
0069265905 | ||
|
|
dec15f6797 | ||
|
|
f6041cd844 | ||
|
|
ec20d3981b | ||
|
|
08a867ef5a | ||
|
|
76c8296a5d | ||
|
|
f65059dd7f | ||
|
|
e23fef3f64 | ||
|
|
30234f913c | ||
|
|
2dd017bddc | ||
|
|
b25a298087 | ||
|
|
8635a7c182 | ||
|
|
46d25df241 | ||
|
|
0338ccb08f | ||
|
|
8899a575b8 | ||
|
|
b4407b1ff3 | ||
|
|
75f0aebe8f | ||
|
|
5a2dd53636 | ||
|
|
3555154c3e | ||
|
|
6c92c282e7 | ||
|
|
bd1847e5ba | ||
|
|
38727d6203 | ||
|
|
7e6d557d01 | ||
|
|
5d3ce91839 | ||
|
|
1f34e3005e | ||
|
|
8e27437086 | ||
|
|
b4cbe8029e | ||
|
|
92f3fd2063 | ||
|
|
1112b331ef | ||
|
|
ec34e84a3a | ||
|
|
bc9eee326a | ||
|
|
c9106c3a6f | ||
|
|
fa949f977a | ||
|
|
4c2d61bb9b | ||
|
|
ec7b14da8c | ||
|
|
771af4991c | ||
|
|
a08ce9ecb7 | ||
|
|
4d84a4f528 | ||
|
|
5d92baf35e | ||
|
|
ac5171e95e | ||
|
|
697ac311fe | ||
|
|
c22d1173af | ||
|
|
c0cc5fa9fd | ||
|
|
4bd9a9cdda | ||
|
|
63f8139dbc | ||
|
|
be731a5977 | ||
|
|
dd4ce4cea4 | ||
|
|
2548a2eee8 | ||
|
|
03948d6785 | ||
|
|
4c1c17eea6 | ||
|
|
b258d22d25 | ||
|
|
aef43e6bca | ||
|
|
3d7362743d | ||
|
|
96f3d5a805 | ||
|
|
2df500e027 | ||
|
|
346f24022a | ||
|
|
cb45c7ef16 | ||
|
|
9cfb57dc4b | ||
|
|
020d852758 | ||
|
|
8d97fcd735 | ||
|
|
097fe3114e | ||
|
|
5e0fd265ff | ||
|
|
6af137a085 | ||
|
|
3cdc8511a8 | ||
|
|
2d515eea4c | ||
|
|
61289119cb | ||
|
|
b6bc995e40 | ||
|
|
b0bb41bac6 | ||
|
|
0927a92652 | ||
|
|
f51061d650 | ||
|
|
74314f00ca | ||
|
|
81c5ef4a23 | ||
|
|
72e329b08e | ||
|
|
50f884663f | ||
|
|
0d81eb4b29 | ||
|
|
978a9407fa | ||
|
|
2c3b74cdc1 | ||
|
|
448e0f54be | ||
|
|
17f638e991 | ||
|
|
b78d3d8eda | ||
|
|
02a7e467e2 | ||
|
|
15304890f5 | ||
|
|
9ea96e431c | ||
|
|
7b3bfea614 | ||
|
|
d68637c2c8 | ||
|
|
f009306030 | ||
|
|
ca97cba18b | ||
|
|
a46c8c5398 | ||
|
|
88c2e2bd70 | ||
|
|
1bbcee8715 | ||
|
|
39a8fa3335 | ||
|
|
fe067e5367 | ||
|
|
5efbd17829 | ||
|
|
fa3e54a747 | ||
|
|
603beccb01 | ||
|
|
4b20afd599 | ||
|
|
adbe283c83 | ||
|
|
1908d7e29a | ||
|
|
7c6e16cfbc | ||
|
|
1480d23088 | ||
|
|
4be3522209 | ||
|
|
5381e0300f | ||
|
|
cde822ebf8 | ||
|
|
72d3525da1 | ||
|
|
dc867fa051 | ||
|
|
eaf3fcade8 | ||
|
|
3df270e4ac | ||
|
|
7937392dfc | ||
|
|
9c1c0f8d1f | ||
|
|
ad2ab0d164 | ||
|
|
c4bed94eb0 | ||
|
|
edc8eb55de | ||
|
|
f4f9868381 | ||
|
|
f103bebe05 | ||
|
|
c168af46bc | ||
|
|
7d82248903 | ||
|
|
e6cb269754 | ||
|
|
ac8a96398a | ||
|
|
86c79c7c60 | ||
|
|
0c47b3f3ea | ||
|
|
b2b500c5dc | ||
|
|
efc8468268 | ||
|
|
205baebd48 | ||
|
|
af9ba2750d | ||
|
|
e04afdbe4c | ||
|
|
e94fd9590e | ||
|
|
4f4f05abdf | ||
|
|
bcf93e1d9f | ||
|
|
48198bdd89 | ||
|
|
fbb4f3e5dd | ||
|
|
cefd1cf030 | ||
|
|
f8fa544e31 | ||
|
|
27885a4cbc | ||
|
|
185a62958f | ||
|
|
1ba748f2a4 | ||
|
|
90a75a9b3d | ||
|
|
215295efb1 | ||
|
|
2413bd9e0d | ||
|
|
356ae650fd | ||
|
|
2444c24545 | ||
|
|
7727708a3a | ||
|
|
0bfacd84f4 | ||
|
|
b271c1ca3c | ||
|
|
75660febc1 | ||
|
|
13aaa174e1 | ||
|
|
d112c728fc | ||
|
|
826af4a9fd | ||
|
|
c9693d355f | ||
|
|
9f37cb8ce6 | ||
|
|
d99e8ca565 | ||
|
|
44312125ab | ||
|
|
7558721642 | ||
|
|
fb49f7fb5d | ||
|
|
ee7f2c2f47 | ||
|
|
833fe006af |
1
.codecov.yml
Normal file
1
.codecov.yml
Normal file
@@ -0,0 +1 @@
|
||||
comment: off
|
||||
3
.docker-entry.sh
Executable file
3
.docker-entry.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
cp -nr /oopt-gnpy/examples /shared
|
||||
exec "$@"
|
||||
52
.docker-travis.sh
Executable file
52
.docker-travis.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
IMAGE_NAME=telecominfraproject/oopt-gnpy
|
||||
IMAGE_TAG=$(git describe --tags)
|
||||
|
||||
if [[ "${TRAVIS_BRANCH}" == "experimental/2019-summit" ]]; then
|
||||
IMAGE_NAME=telecominfraproject/oopt-gnpy-experimental
|
||||
fi
|
||||
|
||||
ALREADY_FOUND=0
|
||||
docker pull ${IMAGE_NAME}:${IMAGE_TAG} && ALREADY_FOUND=1
|
||||
|
||||
if [[ $ALREADY_FOUND == 0 ]]; then
|
||||
docker build . -t ${IMAGE_NAME}
|
||||
docker tag ${IMAGE_NAME} ${IMAGE_NAME}:${IMAGE_TAG}
|
||||
else
|
||||
echo "Image ${IMAGE_NAME}:${IMAGE_TAG} already available, will just update the other tags"
|
||||
fi
|
||||
|
||||
docker images
|
||||
|
||||
do_docker_login() {
|
||||
echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
|
||||
}
|
||||
|
||||
if [[ "${TRAVIS_PULL_REQUEST}" == "false" ]]; then
|
||||
if [[ "${TRAVIS_BRANCH}" == "develop" || "${TRAVIS_BRANCH}" == "docker" ]]; then
|
||||
echo "Publishing latest"
|
||||
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest
|
||||
do_docker_login
|
||||
if [[ $ALREADY_FOUND == 0 ]]; then
|
||||
docker push ${IMAGE_NAME}:${IMAGE_TAG}
|
||||
fi
|
||||
docker push ${IMAGE_NAME}:latest
|
||||
elif [[ "${TRAVIS_BRANCH}" == "master" ]]; then
|
||||
echo "Publishing stable"
|
||||
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:stable
|
||||
do_docker_login
|
||||
if [[ $ALREADY_FOUND == 0 ]]; then
|
||||
docker push ${IMAGE_NAME}:${IMAGE_TAG}
|
||||
fi
|
||||
docker push ${IMAGE_NAME}:stable
|
||||
elif [[ "${TRAVIS_BRANCH}" == "experimental/2019-summit" ]]; then
|
||||
echo "Publishing ad-hoc image for the TIP Summit demo"
|
||||
do_docker_login
|
||||
if [[ $ALREADY_FOUND == 0 ]]; then
|
||||
docker push ${IMAGE_NAME}:${IMAGE_TAG}
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
29
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Environment:**
|
||||
- OS: [e.g. Windows]
|
||||
- Python Version [e.g, 3.7]
|
||||
- Anaconda Version [e.g. 3.7]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
29
.travis.yml
29
.travis.yml
@@ -1,10 +1,27 @@
|
||||
dist: xenial
|
||||
sudo: false
|
||||
language: python
|
||||
services: docker
|
||||
python:
|
||||
- "3.6"
|
||||
# command to install dependencies
|
||||
install:
|
||||
- python setup.py install
|
||||
# command to run tests
|
||||
before_script:
|
||||
- "3.7"
|
||||
install: skip
|
||||
script:
|
||||
- pytest
|
||||
- python setup.py install
|
||||
- pip install pytest-cov rstcheck
|
||||
- pytest --cov-report=xml --cov=gnpy
|
||||
- rstcheck --ignore-roles cite --ignore-directives automodule --recursive --ignore-messages '(Duplicate explicit target name.*)' .
|
||||
- ./examples/transmission_main_example.py
|
||||
- ./examples/path_requests_run.py
|
||||
- ./examples/transmission_main_example.py examples/raman_edfa_example_network.json --sim examples/sim_params.json --show-channels
|
||||
- sphinx-build docs/ x-throwaway-location
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
jobs:
|
||||
include:
|
||||
- stage: test
|
||||
name: Docker image
|
||||
script:
|
||||
- git fetch --unshallow
|
||||
- ./.docker-travis.sh
|
||||
- docker images
|
||||
|
||||
8
.zuul.yaml
Normal file
8
.zuul.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
- project:
|
||||
check:
|
||||
jobs:
|
||||
- noop
|
||||
gate:
|
||||
jobs:
|
||||
- noop
|
||||
@@ -6,16 +6,24 @@ To learn how to contribute, please see CONTRIBUTING.md
|
||||
(*in alphabetical order*)
|
||||
|
||||
- Alessio Ferrari (Politecnico di Torino) <alessio.ferrari@polito.it>
|
||||
- Anders Lindgren (Telia Company) <Anders.X.Lindgren@teliacompany.com>
|
||||
- Andrea d'Amico (Politecnico di Torino) <andrea.damico@polito.it>
|
||||
- Brian Taylor (Facebook) <briantaylor@fb.com>
|
||||
- David Boertjes (Ciena) <dboertje@ciena.com>
|
||||
- Diego Landa (Facebook) <dlanda@fb.com>
|
||||
- Esther Le Rouzic (Orange) <esther.lerouzic@orange.com>
|
||||
- Gabriele Galimberti (Cisco) <ggalimbe@cisco.com>
|
||||
- Gert Grammel (Juniper Networks) <ggrammel@juniper.net>
|
||||
- Gilad Goldfarb (Facebook) <giladg@fb.com>
|
||||
- James Powell (Telecom Infra Project) <james.powell@telecominfraproject.com>
|
||||
- Jan Kundrát (Telecom Infra Project) <jan.kundrat@telecominfraproject.com>
|
||||
- Jeanluc Augé (Orange) <jeanluc.auge@orange.com>
|
||||
- Jonas Mårtensson (RISE) <jonas.martensson@ri.se>
|
||||
- Mattia Cantono (Politecnico di Torino) <mattia.cantono@polito.it>
|
||||
- Miguel Garrich (University Catalunya) <miquel.garrich@upct.es>
|
||||
- Raj Nagarajan (Lumentum) <raj.nagarajan@lumentum.com>
|
||||
- Roberts Miculens (Lattelecom) <roberts.miculens@lattelecom.lv>
|
||||
- Shengxiang Zhu (University of Arizona) <szhu@email.arizona.edu>
|
||||
- Stefan Melin (Telia Company) <Stefan.Melin@teliacompany.com>
|
||||
- Vittorio Curri (Politecnico di Torino) <vittorio.curri@polito.it>
|
||||
- Xufeng Liu (Jabil) <xufeng_liu@jabil.com>
|
||||
|
||||
8
Dockerfile
Normal file
8
Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM python:3.7-slim
|
||||
COPY . /oopt-gnpy
|
||||
WORKDIR /oopt-gnpy
|
||||
RUN python setup.py install
|
||||
WORKDIR /shared
|
||||
ENTRYPOINT ["/oopt-gnpy/.docker-entry.sh"]
|
||||
CMD ["python", "examples/path_requests_run.py", "examples/2019-demo-topology.json", "examples/2019-demo-services.json", "examples/2019-demo-equipment.json", "--rest"]
|
||||
EXPOSE 5000
|
||||
@@ -19,8 +19,8 @@ In order to work the excel file MUST contain at least 2 sheets:
|
||||
Nodes sheet
|
||||
-----------
|
||||
|
||||
Nodes sheet contains seven columns.
|
||||
Each line represents a 'node' (ROADM site or an in line amplifier site ILA)::
|
||||
Nodes sheet contains nine columns.
|
||||
Each line represents a 'node' (ROADM site or an in line amplifier site ILA or a Fused)::
|
||||
|
||||
City (Mandatory) ; State ; Country ; Region ; Latitude ; Longitude ; Type
|
||||
|
||||
@@ -38,6 +38,9 @@ Each line represents a 'node' (ROADM site or an in line amplifier site ILA)::
|
||||
|
||||
- *Longitude*, *Latitude* are not mandatory. If filled they should contain numbers.
|
||||
|
||||
- **Booster_restriction** and **Preamp_restriction** are not mandatory.
|
||||
If used, they must contain one or several amplifier type_variety names separated by ' | '. This information is used to restrict types of amplifiers used in a ROADM node during autodesign. If a ROADM booster or preamp is already specified in the Eqpt sheet , the field is ignored. The field is also ignored if the node is not a ROADM node.
|
||||
|
||||
**There MUST NOT be empty line(s) between two nodes lines**
|
||||
|
||||
|
||||
@@ -166,6 +169,7 @@ This generates a text file meshTopologyExampleV2_eqt_sheet.txt whose content ca
|
||||
- **amp type** is not mandatory.
|
||||
If filled it must contain types listed in `eqpt_config.json <examples/eqpt_config.json>`_ in "Edfa" list "type_variety".
|
||||
If not filled it takes "std_medium_gain" as default value.
|
||||
If filled with fused, a fused element with 0.0 dB loss will be placed instead of an amplifier. This might be used to avoid booster amplifier on a ROADM direction.
|
||||
|
||||
- **amp_gain** is not mandatory. It is the value to be set on the amplifier (in dB).
|
||||
If not filled, it will be determined with design rules in the convert.py file.
|
||||
@@ -189,47 +193,30 @@ Service sheet must contain 11 columns::
|
||||
|
||||
route id ; Source ; Destination ; TRX type ; Mode ; System: spacing ; System: input power (dBm) ; System: nb of channels ; routing: disjoint from ; routing: path ; routing: is loose?
|
||||
|
||||
- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash)
|
||||
- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash or coma)
|
||||
|
||||
- **Source** is mandatory. It is the name of the source node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries)
|
||||
|
||||
- **Destination** is mandatory. It is the name of the destination node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries)
|
||||
|
||||
- **TRX type and mode** are mandatory. They are the variety type and selected mode of the transceiver to be used for the propagation simulation. These modes MUST be defined in the equipment library. The format of the mode is used as the name of the mode. (TODO: maybe add another mode id on Transceiver library ?). In particular the mode selection defines the channel baudrate to be used for the propagation simulation.
|
||||
- **TRX type** is mandatory. They are the variety type and selected mode of the transceiver to be used for the propagation simulation. These modes MUST be defined in the equipment library. The format of the mode is used as the name of the mode. (TODO: maybe add another mode id on Transceiver library ?). In particular the mode selection defines the channel baudrate to be used for the propagation simulation.
|
||||
|
||||
- **System: spacing ; System: input power (dBm) ; System: nb of channels** are mandatory input defining the system parameters for the propagation simulation.
|
||||
- **mode** is optional. If not specified, the program will search for the mode of the defined transponder with the highest baudrate fitting within the spacing value.
|
||||
|
||||
- **System: spacing** is mandatory. Spacing is the channel spacing defined in GHz difined for the feasibility propagation simulation, assuming system full load.
|
||||
|
||||
- **System: input power (dBm) ; System: nb of channels** are optional input defining the system parameters for the propagation simulation.
|
||||
|
||||
- spacing is the channel spacing defined in GHz
|
||||
- input power is the channel optical input power in dBm
|
||||
- nb of channels is the number of channels to be used for the simulation.
|
||||
|
||||
- **routing: disjoint from ; routing: path ; routing: is loose?** are optional.
|
||||
|
||||
- disjoint from: (work not started) identifies the requests from which this request must be disjoint. It is not used yet
|
||||
- path: is the set of ROADM nodes that must be used by the path. It must contain the list of ROADM names that the path must cross. TODO : only ROADM nodes are accepted in this release. Relax this with any type of nodes.
|
||||
- is loose? (in progress) 'no' value means that the list of nodes should be strictly followed, while any other value means that the constraint may be relaxed if the node is not reachable.
|
||||
- disjoint from: identifies the requests from which this request must be disjoint. If filled it must contain request ids separated by ' | '
|
||||
- path: is the set of ROADM nodes that must be used by the path. It must contain the list of ROADM names that the path must cross. TODO : only ROADM nodes are accepted in this release. Relax this with any type of nodes. If filled it must contain ROADM ids separated by ' | '. Exact names are required.
|
||||
- is loose? 'no' value means that the list of nodes should be strictly followed, while any other value means that the constraint may be relaxed if the node is not reachable.
|
||||
|
||||
# to be completed #
|
||||
|
||||
convert_service_sheet.py
|
||||
------------------------
|
||||
|
||||
|
||||
`convert_service_sheet.py <examples/convert_service_sheet.py>`_ converts the service sheet to a json file following the Yang model for requesting Path Computation defined in `draft-ietf-teas-yang-path-computation-01.txt <https://www.ietf.org/id/draft-ietf-teas-yang-path-computation-01.pdf>`_. TODO: verify that this implementation is correct + give feedback to ietf on what is missing for our specific application.
|
||||
For PSE use, additional fields with trx type and mode have been added to the te-bandwidth field.
|
||||
|
||||
**Usage**: convert_service_sheet.py [-h] [-v] [-o OUTPUT] [workbook_name.xls]
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ cd examples
|
||||
$ python convert_service_sheet.py meshTopologyExampleV2.xls -o service_file.json
|
||||
|
||||
-o output_file.json is an optional parameter:
|
||||
|
||||
- if not used, the program output the json data on standard output and on a json file with name 'workbook_name_services.json'.
|
||||
|
||||
A template for the json file can be found here: `service_template.json <service_template.json>`_
|
||||
- ** path bandwidth** is optional. It is the amount of capacity required between source and destination in Gbit/s. Default value is 0.0 Gbit/s.
|
||||
|
||||
path_requests_run.py
|
||||
------------------------
|
||||
@@ -244,6 +231,11 @@ path_requests_run.py
|
||||
|
||||
A function that computes performances for a list of services provided in the service file (accepts json or excel format.
|
||||
|
||||
if the service <file.xls> is in xls format, path_requests_run.py converts it to a json file <file_services.json> following the Yang model for requesting Path Computation defined in `draft-ietf-teas-yang-path-computation-01.txt <https://www.ietf.org/id/draft-ietf-teas-yang-path-computation-01.pdf>`_. For PSE use, additional fields with trx type and mode have been added to the te-bandwidth field.
|
||||
|
||||
A template for the json file can be found here: `service_template.json <service_template.json>`_
|
||||
|
||||
|
||||
If no output file is given, the computation is shown on standard output for demo.
|
||||
If a file is specified with the optional -o argument, the result of the computation is converted into a json format following the Yang model for requesting Path Computation defined in `draft-ietf-teas-yang-path-computation-01.txt <https://www.ietf.org/id/draft-ietf-teas-yang-path-computation-01.pdf>`_. TODO: verify that this implementation is correct + give feedback to ietf on what is missing for our specific application.
|
||||
|
||||
|
||||
520
README.rst
520
README.rst
@@ -1,15 +1,21 @@
|
||||
.. image:: docs/images/GNPy-banner.png
|
||||
:width: 100%
|
||||
:align: left
|
||||
:alt: GNPy with an OLS system
|
||||
|
||||
====================================================================
|
||||
`gnpy`: mesh optical network route planning and optimization library
|
||||
====================================================================
|
||||
|
||||
|docs| |build|
|
||||
|docs| |build| |doi|
|
||||
|
||||
**`gnpy` is an open-source, community-developed library for building route
|
||||
planning and optimization tools in real-world mesh optical networks.**
|
||||
|
||||
`gnpy <http://github.com/telecominfraproject/oopt-gnpy>`__ is:
|
||||
--------------------------------------------------------------
|
||||
|
||||
- a sponsored project of the `OOPT/PSE <https://telecominfraproject.com/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_.
|
||||
- a sponsored project of the `OOPT/PSE <https://telecominfraproject.com/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_
|
||||
- fully community-driven, fully open source library
|
||||
- driven by a consortium of operators, vendors, and academic researchers
|
||||
- intended for rapid development of production-grade route planning tools
|
||||
@@ -18,27 +24,50 @@ planning and optimization tools in real-world mesh optical networks.**
|
||||
|
||||
Documentation: https://gnpy.readthedocs.io
|
||||
|
||||
Get In Touch
|
||||
~~~~~~~~~~~~
|
||||
|
||||
There are `weekly calls <https://telecominfraproject.workplace.com/events/702894886867547/>`__ about our progress.
|
||||
Newcomers, users and telecom operators are especially welcome there.
|
||||
We encourage all interested people outside the TIP to `join the project <https://telecominfraproject.com/apply-for-membership/>`__.
|
||||
|
||||
Branches and Tagged Releases
|
||||
----------------------------
|
||||
|
||||
- the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, validated code. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group. For more information about the validation process, see: https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality
|
||||
- all releases are `available via GitHub <https://github.com/Telecominfraproject/oopt-gnpy/releases>`_
|
||||
- the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, `validated code <https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality>`_. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group.
|
||||
- the `develop <https://github.com/Telecominfraproject/oopt-gnpy/tree/develop>`_ branch contains the latest code under active development, which may not be fully validated and tested.
|
||||
- the `phase-1 <https://github.com/Telecominfraproject/oopt-gnpy/tree/phase-1>`_ branch contains code for Phase I of the OOPT-PSE efforts and is kept only for reference. This branch is unmaintained.
|
||||
|
||||
A brief outline of major (tagged) `gnpy` releases:
|
||||
|
||||
+---------------+-------------+-----------------------------------------------+
|
||||
| release date | version tag | notes |
|
||||
+===============+=============+===============================================+
|
||||
| Oct 16, 2018 | v1.0 | - first "production"-ready release |
|
||||
| | | - open network element model (EDFA, GN-model) |
|
||||
| | | - auto-design functionality |
|
||||
| | | - path request functionality |
|
||||
+---------------+-------------+-----------------------------------------------+
|
||||
|
||||
How to Install
|
||||
--------------
|
||||
|
||||
Using prebuilt Docker images
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Our `Docker images <https://hub.docker.com/r/telecominfraproject/oopt-gnpy>`_ contain everything needed to run all examples from this guide.
|
||||
Docker transparently fetches the image over the network upon first use.
|
||||
On Linux and Mac, run:
|
||||
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ docker run -it --rm --volume $(pwd):/shared telecominfraproject/oopt-gnpy
|
||||
root@bea050f186f7:/shared/examples#
|
||||
|
||||
On Windows, launch from Powershell as:
|
||||
|
||||
.. code-block:: powershell
|
||||
|
||||
PS C:\> docker run -it --rm --volume ${PWD}:/shared telecominfraproject/oopt-gnpy
|
||||
root@89784e577d44:/shared/examples#
|
||||
|
||||
In both cases, a directory named ``examples/`` will appear in your current working directory.
|
||||
GNPy automaticallly populates it with example files from the current release.
|
||||
Remove that directory if you want to start from scratch.
|
||||
|
||||
Using Python on your computer
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**Note**: `gnpy` supports Python 3 only. Python 2 is not supported.
|
||||
`gnpy` requires Python ≥3.6
|
||||
|
||||
@@ -48,10 +77,9 @@ How to Install
|
||||
It is recommended that you use a "virtual environment" when installing `gnpy`.
|
||||
Do not install `gnpy` on your system Python.
|
||||
|
||||
We recommend the use of the Anaconda Python distribution
|
||||
(https://www.anaconda.com/download) which comes with many scientific computing
|
||||
We recommend the use of the `Anaconda Python distribution <https://www.anaconda.com/download>`_ which comes with many scientific computing
|
||||
dependencies pre-installed. Anaconda creates a base "virtual environment" for
|
||||
you automatically. You can also create and manage your conda "virtual
|
||||
you automatically. You can also create and manage your ``conda`` "virtual
|
||||
environments" yourself (see:
|
||||
https://conda.io/docs/user-guide/tasks/manage-environments.html)
|
||||
|
||||
@@ -97,14 +125,13 @@ of the `gnpy` repo and install it with:
|
||||
$ python setup.py install # install
|
||||
|
||||
To test that `gnpy` was successfully installed, you can run this command. If it
|
||||
executes without a `ModuleNotFoundError`, you have successfully installed
|
||||
executes without a ``ModuleNotFoundError``, you have successfully installed
|
||||
`gnpy`.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ python -c 'import gnpy' # attempt to import gnpy
|
||||
|
||||
$ cd oopt-gnpy
|
||||
$ pytest # run tests
|
||||
|
||||
Instructions for First Use
|
||||
@@ -116,32 +143,29 @@ It ships with a number of example programs. Release versions will ship with
|
||||
fully-functional programs.
|
||||
|
||||
**Note**: *If you are a network operator or involved in route planning and
|
||||
optimization for your organization, please contact project maintainer James
|
||||
Powell <james.powell@telecominfraproject>. gnpy is looking for users with
|
||||
optimization for your organization, please contact project maintainer Jan
|
||||
Kundrát <jan.kundrat@telecominfraproject.com>. gnpy is looking for users with
|
||||
specific, delineated use cases to drive requirements for future
|
||||
development.*
|
||||
|
||||
**To get started, run the main transmission example:**
|
||||
This example demonstrates how GNPy can be used to check the expected SNR at the end of the line by varying the channel input power:
|
||||
|
||||
**Note**: *Examples should be run from the examples/ folder.*
|
||||
|
||||
.. code-block:: shell
|
||||
$ pwd
|
||||
/path/to/oopt-gnpy
|
||||
$ cd examples
|
||||
$ python transmission_main_example.py
|
||||
.. image:: https://telecominfraproject.github.io/oopt-gnpy/docs/images/transmission_main_example.svg
|
||||
:width: 100%
|
||||
:align: left
|
||||
:alt: Running a simple simulation example
|
||||
:target: https://asciinema.org/a/252295
|
||||
|
||||
By default, this script operates on a single span network defined in
|
||||
`examples/edfa_example_network.json <examples/edfa_example_network.json>`_
|
||||
|
||||
You can specify a different network at the command line as follows. For
|
||||
example, to use the CORONET Continental US (CONUS) network defined in
|
||||
`examples/coronet_conus_example.json <examples/coronet_conus_example.json>`_:
|
||||
example, to use the CORONET Global network defined in
|
||||
`examples/CORONET_Global_Topology.json <examples/CORONET_Global_Topology.json>`_:
|
||||
|
||||
.. code-block:: shell
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ cd examples
|
||||
$ python transmission_main_example.py CORONET_Global_Topology.json
|
||||
$ ./examples/transmission_main_example.py examples/CORONET_Global_Topology.json
|
||||
|
||||
It is also possible to use an Excel file input (for example
|
||||
`examples/CORONET_Global_Topology.xls <examples/CORONET_Global_Topology.xls>`_).
|
||||
@@ -150,10 +174,9 @@ further instructions on how to prepare the Excel input file, see
|
||||
`Excel_userguide.rst <Excel_userguide.rst>`_.
|
||||
|
||||
The main transmission example will calculate the average signal OSNR and SNR
|
||||
across 93 network elements (transceiver, ROADMs, fibers, and amplifiers)
|
||||
between two transceivers selected by the user. (By default, for the CORONET US
|
||||
network, it will show the transmission of spectral information between Abilene,
|
||||
Texas and Albany, New York.)
|
||||
across network elements (transceiver, ROADMs, fibers, and amplifiers)
|
||||
between two transceivers selected by the user. Additional details are provided by doing ``transmission_main_example.py -h``. (By default, for the CORONET Global
|
||||
network, it will show the transmission of spectral information between Abilene and Albany)
|
||||
|
||||
This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|.
|
||||
|
||||
@@ -171,8 +194,8 @@ Further Instructions for Use (`transmission_main_example.py`, `path_requests_run
|
||||
|
||||
Design and transmission parameters are defined in a dedicated json file. By
|
||||
default, this information is read from `examples/eqpt_config.json
|
||||
<examples/eqpt_config.json>`_. This file defines the equipement librairies that
|
||||
can be customized (EDFAs, fibers, and transcievers).
|
||||
<examples/eqpt_config.json>`_. This file defines the equipment libraries that
|
||||
can be customized (EDFAs, fibers, and transceivers).
|
||||
|
||||
It also defines the simulation parameters (spans, ROADMs, and the spectral
|
||||
information to transmit.)
|
||||
@@ -180,59 +203,59 @@ information to transmit.)
|
||||
The EDFA equipment library is a list of supported amplifiers. New amplifiers
|
||||
can be added and existing ones removed. Three different noise models are available:
|
||||
|
||||
1. `'type_def': 'variable_gain'` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: `nf_min`, `nf_max`, and `gain_flatmax`. It is not a simple interpolation but a 2-stage NF calculation.
|
||||
2. `'type_def': 'fixed_gain'` is a fixed gain model. `NF == Cte == nf0` if `gain_min < gain < gain_flatmax`
|
||||
3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/advanced_config_from.json <examples/advanced_config_from.json>`_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled.
|
||||
1. ``'type_def': 'variable_gain'`` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: ``nf_min``, ``nf_max``, and ``gain_flatmax``. It is not a simple interpolation but a 2-stage NF calculation.
|
||||
2. ``'type_def': 'fixed_gain'`` is a fixed gain model. `NF == Cte == nf0` if `gain_min < gain < gain_flatmax`
|
||||
3. ``'type_def': None`` is an advanced model. A detailed JSON configuration file is required (by default `examples/std_medium_gain_advanced_config.json <examples/std_medium_gain_advanced_config.json>`_). It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled.
|
||||
|
||||
For all amplifier models:
|
||||
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+=========================================+
|
||||
| `type_variety` | (string) | a unique name to ID the amplifier in the|
|
||||
| | | JSON/Excel template topology input file |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `out_voa_auto` | (boolean) | auto_design feature to optimize the |
|
||||
| | | amplifier output VOA. If true, output |
|
||||
| | | VOA is present and will be used to push |
|
||||
| | | amplifier gain to its maximum, within |
|
||||
| | | EOL power margins. |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `allowed_for_design` | (boolean) | If false, the amplifier will not be |
|
||||
| | | picked by auto-design but it can still |
|
||||
| | | be used as a manual input (from JSON or |
|
||||
| | | Excel template topology files.) |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
+------------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
+========================+===========+=========================================+
|
||||
| ``type_variety`` | (string) | a unique name to ID the amplifier in the|
|
||||
| | | JSON/Excel template topology input file |
|
||||
+------------------------+-----------+-----------------------------------------+
|
||||
| ``out_voa_auto`` | (boolean) | auto_design feature to optimize the |
|
||||
| | | amplifier output VOA. If true, output |
|
||||
| | | VOA is present and will be used to push |
|
||||
| | | amplifier gain to its maximum, within |
|
||||
| | | EOL power margins. |
|
||||
+------------------------+-----------+-----------------------------------------+
|
||||
| ``allowed_for_design`` | (boolean) | If false, the amplifier will not be |
|
||||
| | | picked by auto-design but it can still |
|
||||
| | | be used as a manual input (from JSON or |
|
||||
| | | Excel template topology files.) |
|
||||
+------------------------+-----------+-----------------------------------------+
|
||||
|
||||
The fiber library currently describes SSMF but additional fiber types can be entered by the user following the same model:
|
||||
The fiber library currently describes SSMF and NZDF but additional fiber types can be entered by the user following the same model:
|
||||
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+=========================================+
|
||||
| `type_variety` | (string) | a unique name to ID the amplifier in the|
|
||||
| ``type_variety`` | (string) | a unique name to ID the fiber in the |
|
||||
| | | JSON or Excel template topology input |
|
||||
| | | file |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `dispersion` | (number) | (s.m-1.m-1) |
|
||||
| ``dispersion`` | (number) | (s.m-1.m-1) |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `gamma` | (number) | 2pi.n2/(lambda*Aeff) (w-2.m-1) |
|
||||
| ``gamma`` | (number) | 2pi.n2/(lambda*Aeff) (w-2.m-1) |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
|
||||
The transceiver equipment library is a list of supported transceivers. New
|
||||
transceivers can be added and existing ones removed at will by the user. It is
|
||||
used to determine the service list path feasibility when running the
|
||||
path_request_run.py routine.
|
||||
`path_request_run.py routine <examples/path_request_run.py>`_.
|
||||
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+=========================================+
|
||||
| `type_variety` | (string) | a unique name to ID the amplifier in |
|
||||
| ``type_variety`` | (string) | A unique name to ID the transceiver in |
|
||||
| | | the JSON or Excel template topology |
|
||||
| | | input file |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `frequency` | (number) | Min/max as below. |
|
||||
| ``frequency`` | (number) | Min/max as below. |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `mode` | (number) | a list of modes supported by the |
|
||||
| ``mode`` | (number) | A list of modes supported by the |
|
||||
| | | transponder. New modes can be added at |
|
||||
| | | will by the user. The modes are specific|
|
||||
| | | to each transponder type_variety. |
|
||||
@@ -244,138 +267,142 @@ The modes are defined as follows:
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+=========================================+
|
||||
| `format` | (string) | a unique name to ID the mode. |
|
||||
| ``format`` | (string) | a unique name to ID the mode |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `baud_rate` | (number) | in Hz |
|
||||
| ``baud_rate`` | (number) | in Hz |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `OSNR` | (number) | min required OSNR in 0.1nm (dB) |
|
||||
| ``OSNR`` | (number) | min required OSNR in 0.1nm (dB) |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `bit_rate` | (number) | in bit/s |
|
||||
| ``bit_rate`` | (number) | in bit/s |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| `roll_off` | (number) | |
|
||||
| ``roll_off`` | (number) | Not used. |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| ``tx_osnr`` | (number) | In dB. OSNR out from transponder. |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| ``cost`` | (number) | Arbitrary unit |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
|
||||
Simulation parameters are defined as follows.
|
||||
|
||||
Auto-design automatically creates EDFA amplifier network elements when they are
|
||||
missing, after a fiber, or between a ROADM and a fiber. This auto-design
|
||||
functionality can be manually and locally deactivated by introducing a `Fused`
|
||||
network element after a `Fiber` or a `Roadm` that doesn't need amplification.
|
||||
functionality can be manually and locally deactivated by introducing a ``Fused``
|
||||
network element after a ``Fiber`` or a ``Roadm`` that doesn't need amplification.
|
||||
The amplifier is chosen in the EDFA list of the equipment library based on
|
||||
gain, power, and NF criteria. Only the EDFA that are marked
|
||||
`'allowed_for_design': true` are considered.
|
||||
``'allowed_for_design': true`` are considered.
|
||||
|
||||
For amplifiers defined in the topology JSON input but whose gain = 0
|
||||
(placeholder), auto-design will set its gain automatically: see `power_mode` in
|
||||
the `Spans` library to find out how the gain is calculated.
|
||||
For amplifiers defined in the topology JSON input but whose ``gain = 0``
|
||||
(placeholder), auto-design will set its gain automatically: see ``power_mode`` in
|
||||
the ``Spans`` library to find out how the gain is calculated.
|
||||
|
||||
Span configuration is performed as followws. It is not a list (which may change
|
||||
in later releases,) and the user can only modify the value of existing
|
||||
Span configuration is performed as follows. It is not a list (which may change
|
||||
in later releases) and the user can only modify the value of existing
|
||||
parameters:
|
||||
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| field | type | description |
|
||||
+========================+===========+=============================================+
|
||||
| `power_mode` | (boolean) | If false, gain mode. Auto-design sets |
|
||||
| | | amplifier gain = preceeding span loss, |
|
||||
| | | unless the amplifier exists and its |
|
||||
| | | gain > 0 in the topology input json. |
|
||||
| | | If true, power mode (recommended for |
|
||||
| | | auto-design and power sweep.) |
|
||||
| | | Auto-design sets amplifier power |
|
||||
| | | according to delta_power_range. If the |
|
||||
| | | amplifier exists with gain > 0 in the |
|
||||
| | | topology json input, then its gain is |
|
||||
| | | translated into a power target/channel. |
|
||||
| | | Moreover, when performing a power sweep |
|
||||
| | | (see power_range_db in the SI |
|
||||
| | | configuration library) the power sweep |
|
||||
| | | is performed w/r/t this power target, |
|
||||
| | | regardless of preceeding amplifiers |
|
||||
| | | power saturation/limitations. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `delta_power_range_db` | (number) | Auto-design only, power-mode |
|
||||
| | | only. Specifies the [min, max, step] |
|
||||
| | | power excursion/span. It is a relative |
|
||||
| | | power excursion w/r/t the |
|
||||
| | | power_dbm + power_range_db |
|
||||
| | | (power sweep if applicable) defined in |
|
||||
| | | the SI configuration library. This |
|
||||
| | | relative power excursion is = 1/3 of |
|
||||
| | | the span loss difference with the |
|
||||
| | | reference 20 dB span. The 1/3 slope is |
|
||||
| | | derived from the GN model equations. |
|
||||
| | | For example, a 23 dB span loss will be |
|
||||
| | | set to 1 dB more power than a 20 dB |
|
||||
| | | span loss. The 20 dB reference spans |
|
||||
| | | will *always* be set to |
|
||||
| | | power = power_dbm + power_range_db. |
|
||||
| | | To configure the same power in all |
|
||||
| | | spans, use `[0, 0, 0]`. All spans will |
|
||||
| | | be set to |
|
||||
| | | power = power_dbm + power_range_db. |
|
||||
| | | To configure the same power in all spans |
|
||||
| | | and 3 dB more power just for the longest |
|
||||
| | | spans: `[0, 3, 3]`. The longest spans are |
|
||||
| | | set to |
|
||||
| | | power = power_dbm + power_range_db + 3. |
|
||||
| | | To configure a 4 dB power range across |
|
||||
| | | all spans in 0.5 dB steps: `[-2, 2, 0.5]`. |
|
||||
| | | A 17 dB span is set to |
|
||||
| | | power = power_dbm + power_range_db - 1, |
|
||||
| | | a 20 dB span to |
|
||||
| | | power = power_dbm + power_range_db and |
|
||||
| | | a 23 dB span to |
|
||||
| | | power = power_dbm + power_range_db + 1 |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `max_length` | (number) | Split fiber lengths > max_length. |
|
||||
| | | Interest to support high level |
|
||||
| | | topologies that do not specify in line |
|
||||
| | | amplification sites. For example the |
|
||||
| | | CORONET_Global_Topology.xls defines |
|
||||
| | | links > 1000km between 2 sites: it |
|
||||
| | | couldn't be simulated if these links |
|
||||
| | | were not splitted in shorter span |
|
||||
| | | lengths. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `length_unit` | "m"/"km" | Unit for max_length. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `max_loss` | (number) | Not used in the current code |
|
||||
| | | implementation. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `padding` | (number) | In dB. Min span loss before putting an |
|
||||
| | | attenuator before fiber. Attenuator |
|
||||
| | | value |
|
||||
| | | Fiber.att_in = max(0, padding - span_loss). |
|
||||
| | | Padding can be set manually to reach a |
|
||||
| | | higher padding value for a given fiber |
|
||||
| | | by filling in the Fiber/params/att_in |
|
||||
| | | field in the topology json input [1] |
|
||||
| | | but if span_loss = length * loss_coef |
|
||||
| | | + att_in + con_in + con_out < padding, |
|
||||
| | | the specified att_in value will be |
|
||||
| | | completed to have span_loss = padding. |
|
||||
| | | Therefore it is not possible to set |
|
||||
| | | span_loss < padding. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `EOL` | (number) | All fiber span loss ageing. The value |
|
||||
| | | is added to the con_out (fiber output |
|
||||
| | | connector). So the design and the path |
|
||||
| | | feasibility are performed with |
|
||||
| | | span_loss + EOL. EOL cannot be set |
|
||||
| | | manually for a given fiber span |
|
||||
| | | (workaround is to specify higher con_out |
|
||||
| | | loss for this fiber). |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
| `con_in`, `con_out` | (number) | Default values if Fiber/params/con_in/out |
|
||||
| | | is None in the topology input |
|
||||
| | | description. This default value is |
|
||||
| | | ignored if a Fiber/params/con_in/out |
|
||||
| | | value is input in the topology for a |
|
||||
| | | given Fiber. |
|
||||
+------------------------+-----------+---------------------------------------------+
|
||||
|
||||
**[1]**
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
| field | type | description |
|
||||
+=====================================+===========+=============================================+
|
||||
| ``power_mode`` | (boolean) | If false, gain mode. Auto-design sets |
|
||||
| | | amplifier gain = preceding span loss, |
|
||||
| | | unless the amplifier exists and its |
|
||||
| | | gain > 0 in the topology input JSON. |
|
||||
| | | If true, power mode (recommended for |
|
||||
| | | auto-design and power sweep.) |
|
||||
| | | Auto-design sets amplifier power |
|
||||
| | | according to delta_power_range. If the |
|
||||
| | | amplifier exists with gain > 0 in the |
|
||||
| | | topology JSON input, then its gain is |
|
||||
| | | translated into a power target/channel. |
|
||||
| | | Moreover, when performing a power sweep |
|
||||
| | | (see ``power_range_db`` in the SI |
|
||||
| | | configuration library) the power sweep |
|
||||
| | | is performed w/r/t this power target, |
|
||||
| | | regardless of preceding amplifiers |
|
||||
| | | power saturation/limitations. |
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
| ``delta_power_range_db`` | (number) | Auto-design only, power-mode |
|
||||
| | | only. Specifies the [min, max, step] |
|
||||
| | | power excursion/span. It is a relative |
|
||||
| | | power excursion w/r/t the |
|
||||
| | | power_dbm + power_range_db |
|
||||
| | | (power sweep if applicable) defined in |
|
||||
| | | the SI configuration library. This |
|
||||
| | | relative power excursion is = 1/3 of |
|
||||
| | | the span loss difference with the |
|
||||
| | | reference 20 dB span. The 1/3 slope is |
|
||||
| | | derived from the GN model equations. |
|
||||
| | | For example, a 23 dB span loss will be |
|
||||
| | | set to 1 dB more power than a 20 dB |
|
||||
| | | span loss. The 20 dB reference spans |
|
||||
| | | will *always* be set to |
|
||||
| | | power = power_dbm + power_range_db. |
|
||||
| | | To configure the same power in all |
|
||||
| | | spans, use `[0, 0, 0]`. All spans will |
|
||||
| | | be set to |
|
||||
| | | power = power_dbm + power_range_db. |
|
||||
| | | To configure the same power in all spans |
|
||||
| | | and 3 dB more power just for the longest |
|
||||
| | | spans: `[0, 3, 3]`. The longest spans are |
|
||||
| | | set to |
|
||||
| | | power = power_dbm + power_range_db + 3. |
|
||||
| | | To configure a 4 dB power range across |
|
||||
| | | all spans in 0.5 dB steps: `[-2, 2, 0.5]`. |
|
||||
| | | A 17 dB span is set to |
|
||||
| | | power = power_dbm + power_range_db - 1, |
|
||||
| | | a 20 dB span to |
|
||||
| | | power = power_dbm + power_range_db and |
|
||||
| | | a 23 dB span to |
|
||||
| | | power = power_dbm + power_range_db + 1 |
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
| ``max_fiber_lineic_loss_for_raman`` | (number) | Maximum linear fiber loss for Raman |
|
||||
| | | amplification use. |
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
| ``max_length`` | (number) | Split fiber lengths > max_length. |
|
||||
| | | Interest to support high level |
|
||||
| | | topologies that do not specify in line |
|
||||
| | | amplification sites. For example the |
|
||||
| | | CORONET_Global_Topology.xls defines |
|
||||
| | | links > 1000km between 2 sites: it |
|
||||
| | | couldn't be simulated if these links |
|
||||
| | | were not split in shorter span lengths. |
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
| ``length_unit`` | "m"/"km" | Unit for ``max_length``. |
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
| ``max_loss`` | (number) | Not used in the current code |
|
||||
| | | implementation. |
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
| ``padding`` | (number) | In dB. Min span loss before putting an |
|
||||
| | | attenuator before fiber. Attenuator |
|
||||
| | | value |
|
||||
| | | Fiber.att_in = max(0, padding - span_loss). |
|
||||
| | | Padding can be set manually to reach a |
|
||||
| | | higher padding value for a given fiber |
|
||||
| | | by filling in the Fiber/params/att_in |
|
||||
| | | field in the topology json input [1] |
|
||||
| | | but if span_loss = length * loss_coef |
|
||||
| | | + att_in + con_in + con_out < padding, |
|
||||
| | | the specified att_in value will be |
|
||||
| | | completed to have span_loss = padding. |
|
||||
| | | Therefore it is not possible to set |
|
||||
| | | span_loss < padding. |
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
| ``EOL`` | (number) | All fiber span loss ageing. The value |
|
||||
| | | is added to the con_out (fiber output |
|
||||
| | | connector). So the design and the path |
|
||||
| | | feasibility are performed with |
|
||||
| | | span_loss + EOL. EOL cannot be set |
|
||||
| | | manually for a given fiber span |
|
||||
| | | (workaround is to specify higher |
|
||||
| | | ``con_out`` loss for this fiber). |
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
| ``con_in``, | (number) | Default values if Fiber/params/con_in/out |
|
||||
| ``con_out`` | | is None in the topology input |
|
||||
| | | description. This default value is |
|
||||
| | | ignored if a Fiber/params/con_in/out |
|
||||
| | | value is input in the topology for a |
|
||||
| | | given Fiber. |
|
||||
+-------------------------------------+-----------+---------------------------------------------+
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
@@ -396,35 +423,38 @@ parameters:
|
||||
}
|
||||
|
||||
ROADMs can be configured as follows. The user can only modify the value of
|
||||
existing parmeters:
|
||||
existing parameters:
|
||||
|
||||
+-------------------------+-----------+---------------------------------------------+
|
||||
| field | type | description |
|
||||
+=========================+===========+=============================================+
|
||||
|`gain_mode_default_loss` | (number) | Default value if Roadm/params/loss is |
|
||||
| | | None in the topology input description. |
|
||||
| | | This default value is ignored if a |
|
||||
| | | params/loss value is input in the |
|
||||
| | | topology for a given ROADM. |
|
||||
+-------------------------+-----------+---------------------------------------------+
|
||||
|`power_mode_pref` | (number) | Power mode only. Auto-design sets the |
|
||||
| | | power of ROADM ingress amplifiers to |
|
||||
| | | power_dbm + power_range_db, |
|
||||
| | | regardless of existing gain settings |
|
||||
| | | from the topology JSON input. |
|
||||
| | | Auto-design sets the Roadm loss so that |
|
||||
| | | its egress channel power = power_mode_pref, |
|
||||
| | | regardless of existing loss settings |
|
||||
| | | from the topology JSON input. It means |
|
||||
| | | that the ouput power from a ROADM (and |
|
||||
| | | therefore its OSNR contribution) is Cte |
|
||||
| | | and not depending from power_dbm and |
|
||||
| | | power_range_db sweep settings. This |
|
||||
| | | choice is meant to reflect some typical |
|
||||
| | | control loop algorithms. |
|
||||
+-------------------------+-----------+---------------------------------------------+
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| field | type | description |
|
||||
+==========================+===========+=============================================+
|
||||
| ``target_pch_out_db`` | (number) | Auto-design sets the ROADM egress channel |
|
||||
| | | power. This reflects typical control loop |
|
||||
| | | algorithms that adjust ROADM losses to |
|
||||
| | | equalize channels (eg coming from different |
|
||||
| | | ingress direction or add ports) |
|
||||
| | | This is the default value |
|
||||
| | | Roadm/params/target_pch_out_db if no value |
|
||||
| | | is given in the ``Roadm`` element in the |
|
||||
| | | topology input description. |
|
||||
| | | This default value is ignored if a |
|
||||
| | | params/target_pch_out_db value is input in |
|
||||
| | | the topology for a given ROADM. |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``add_drop_osnr`` | (number) | OSNR contribution from the add/drop ports |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``restrictions`` | (dict of | If non-empty, keys ``preamp_variety_list`` |
|
||||
| | strings) | and ``booster_variety_list`` represent |
|
||||
| | | list of ``type_variety`` amplifiers which |
|
||||
| | | are allowed for auto-design within ROADM's |
|
||||
| | | line degrees. |
|
||||
| | | |
|
||||
| | | If no booster should be placed on a degree, |
|
||||
| | | insert a ``Fused`` node on the degree |
|
||||
| | | output. |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
|
||||
The `SpectralInformation` object can be configured as follows. The user can
|
||||
The ``SpectralInformation`` object can be configured as follows. The user can
|
||||
only modify the value of existing parameters. It defines a spectrum of N
|
||||
identical carriers. While the code libraries allow for different carriers and
|
||||
power levels, the current user parametrization only allows one carrier type and
|
||||
@@ -433,19 +463,18 @@ one power/channel definition.
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| field | type | description |
|
||||
+======================+===========+===========================================+
|
||||
| `f_min/max` | (number) | In Hz. Carrier min max excursion |
|
||||
| ``f_min``, | (number) | In Hz. Carrier min max excursion. |
|
||||
| ``f_max`` | | |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `baud_rate` | (number) | In Hz. Simulated baud rate. |
|
||||
| ``baud_rate`` | (number) | In Hz. Simulated baud rate. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `spacing` | (number) | In Hz. Carrier spacing. |
|
||||
| ``spacing`` | (number) | In Hz. Carrier spacing. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `roll_off` | (number) | Not used. |
|
||||
| ``roll_off`` | (number) | Not used. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `OSNR` | (number) | Not used. |
|
||||
| ``tx_osnr`` | (number) | In dB. OSNR out from transponder. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `bit_rate` | (number) | Not used. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `power_dbm` | (number) | Reference channel power. In gain mode |
|
||||
| ``power_dbm`` | (number) | Reference channel power. In gain mode |
|
||||
| | | (see spans/power_mode = false), all gain |
|
||||
| | | settings are offset w/r/t this reference |
|
||||
| | | power. In power mode, it is the |
|
||||
@@ -458,22 +487,30 @@ one power/channel definition.
|
||||
| | | power sweep is defined (see after) the |
|
||||
| | | design is not repeated. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| `power_range_db` | (number) | Power sweep excursion around power_dbm. |
|
||||
| ``power_range_db`` | (number) | Power sweep excursion around power_dbm. |
|
||||
| | | It is not the min and max channel power |
|
||||
| | | values! The reference power becomes: |
|
||||
| | | power_range_db + power_dbm. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
| ``sys_margins`` | (number) | In dB. Added margin on min required |
|
||||
| | | transceiver OSNR. |
|
||||
+----------------------+-----------+-------------------------------------------+
|
||||
|
||||
The `transmission_main_example.py <examples/transmission_main_example.py>`_
|
||||
script propagates a specrum of channels at 32 Gbaud, 50 GHz spacing and 0
|
||||
dBm/channel. These are not yet parametrized but can be modified directly in the
|
||||
script (via the SpectralInformation structure) to accomodate any baud rate,
|
||||
spacing, power or channel count demand.
|
||||
The `transmission_main_example.py <examples/transmission_main_example.py>`_ script propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing and 0 dBm/channel.
|
||||
Launch power can be overridden by using the ``--power`` argument.
|
||||
Spectrum information is not yet parametrized but can be modified directly in the ``eqpt_config.json`` (via the ``SpectralInformation`` -SI- structure) to accommodate any baud rate or spacing.
|
||||
The number of channel is computed based on ``spacing`` and ``f_min``, ``f_max`` values.
|
||||
|
||||
The amplifier's gain is set to exactly compensate for the loss in each network
|
||||
element. The amplifier is currently defined with gain range of 15 dB to 25 dB
|
||||
and 21 dBm max output power. Ripple and NF models are defined in
|
||||
`examples/std_medium_gain_advanced_config.json <examples/std_medium_gain_advanced_config.json>`_
|
||||
An experimental support for Raman amplification is available:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ ./examples/transmission_main_example.py \
|
||||
examples/raman_edfa_example_network.json \
|
||||
--sim examples/sim_params.json --show-channels
|
||||
|
||||
Configuration of Raman pumps (their frequencies, power and pumping direction) is done via the `RamanFiber element in the network topology <examples/raman_edfa_example_network.json>`_.
|
||||
General numeric parameters for simulaiton control are provided in the `examples/sim_params.json <examples/sim_params.json>`_.
|
||||
|
||||
Use `examples/path_requests_run.py <examples/path_requests_run.py>`_ to run multiple optimizations as follows:
|
||||
|
||||
@@ -482,7 +519,7 @@ Use `examples/path_requests_run.py <examples/path_requests_run.py>`_ to run mult
|
||||
$ python path_requests_run.py -h
|
||||
Usage: path_requests_run.py [-h] [-v] [-o OUTPUT] [network_filename] [service_filename] [eqpt_filename]
|
||||
|
||||
The `network_filename` and `service_filename` can be an XLS or JSON file. The `eqpt_filename` must be a JSON file.
|
||||
The ``network_filename`` and ``service_filename`` can be an XLS or JSON file. The ``eqpt_filename`` must be a JSON file.
|
||||
|
||||
To see an example of it, run:
|
||||
|
||||
@@ -493,11 +530,11 @@ To see an example of it, run:
|
||||
|
||||
This program requires a list of connections to be estimated and the equipment
|
||||
library. The program computes performances for the list of services (accepts
|
||||
json or excel format) using the same spectrum propagation modules as
|
||||
transmission_main_example.py. Explanation on the Excel template is provided in
|
||||
JSON or Excel format) using the same spectrum propagation modules as
|
||||
``transmission_main_example.py``. Explanation on the Excel template is provided in
|
||||
the `Excel_userguide.rst <Excel_userguide.rst#service-sheet>`_. Template for
|
||||
the json format can be found here: `service_template.json
|
||||
<service_template.json>`_.
|
||||
the JSON format can be found here: `service-template.json
|
||||
<service-template.json>`_.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
@@ -505,15 +542,15 @@ Contributing
|
||||
``gnpy`` is looking for additional contributors, especially those with experience
|
||||
planning and maintaining large-scale, real-world mesh optical networks.
|
||||
|
||||
To get involved, please contact James Powell
|
||||
<james.powell@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>.
|
||||
To get involved, please contact Jan Kundrát
|
||||
<jan.kundrat@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>.
|
||||
|
||||
``gnpy`` contributions are currently limited to members of `TIP
|
||||
<http://telecominfraproject.com>`_. Membership is free and open to all.
|
||||
|
||||
See the `Onboarding Guide
|
||||
<https://github.com/Telecominfraproject/gnpy/wiki/Onboarding-Guide>`_ for
|
||||
specific details on code contribtions.
|
||||
specific details on code contributions.
|
||||
|
||||
See `AUTHORS.rst <AUTHORS.rst>`_ for past and present contributors.
|
||||
|
||||
@@ -523,7 +560,7 @@ Project Background
|
||||
Data Centers are built upon interchangeable, highly standardized node and
|
||||
network architectures rather than a sum of isolated solutions. This also
|
||||
translates to optical networking. It leads to a push in enabling multi-vendor
|
||||
optical network by disaggregating HW and SW functions and focussing on
|
||||
optical network by disaggregating HW and SW functions and focusing on
|
||||
interoperability. In this paradigm, the burden of responsibility for ensuring
|
||||
the performance of such disaggregated open optical systems falls on the
|
||||
operators. Consequently, operators and vendors are collaborating in defining
|
||||
@@ -564,6 +601,11 @@ implementations.
|
||||
:alt: Build Status
|
||||
:scale: 100%
|
||||
|
||||
.. |doi| image:: https://zenodo.org/badge/96894149.svg
|
||||
:target: https://zenodo.org/badge/latestdoi/96894149
|
||||
:alt: DOI
|
||||
:scale: 100%
|
||||
|
||||
TIP OOPT/PSE & PSE WG Charter
|
||||
-----------------------------
|
||||
|
||||
|
||||
@@ -874,7 +874,7 @@ month={Sept},}
|
||||
number = {7},
|
||||
journal = {Optics Express},
|
||||
urlyear = {2017-11-14},
|
||||
year = {2012-03-26},
|
||||
date = {2012-03-26},
|
||||
year = {2012},
|
||||
pages = {7777},
|
||||
author = {Bononi, A. and Serena, P. and Rossi, N. and Grellier, E. and Vacondio, F.}
|
||||
@@ -1114,7 +1114,7 @@ month={Sept},}
|
||||
number = {26},
|
||||
journal = {Optics Express},
|
||||
urlyear = {2017-11-16},
|
||||
year = {2013-12-30},
|
||||
date = {2013-12-30},
|
||||
year = {2013},
|
||||
pages = {32254},
|
||||
author = {Bononi, Alberto and Beucher, Ottmar and Serena, Paolo}
|
||||
|
||||
@@ -173,5 +173,4 @@ texinfo_documents = [
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
|
||||
|
||||
autodoc_default_flags = ['members', 'undoc-members', 'private-members', 'show-inheritance']
|
||||
|
||||
BIN
docs/images/GNPy-banner.png
Normal file
BIN
docs/images/GNPy-banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 518 KiB |
@@ -43,10 +43,16 @@ Contributors in alphabetical order
|
||||
+==========+============+=======================+======================================+
|
||||
| Alessio | Ferrari | Politecnico di Torino | alessio.ferrari@polito.it |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Anders | Lindgren | Telia Company | Anders.X.Lindgren@teliacompany.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Andrea | d'Amico | Politecnico di Torino | andrea.damico@polito.it |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Brian | Taylor | Facebook | briantaylor@fb.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| David | Boertjes | Ciena | dboertje@ciena.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Diego | Landa | Facebook | dlanda@fb.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Esther | Le Rouzic | Orange | esther.lerouzic@orange.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Gabriele | Galimberti | Cisco | ggalimbe@cisco.com |
|
||||
@@ -57,14 +63,29 @@ Contributors in alphabetical order
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| James | Powell | Telecom Infra Project | james.powell@telecominfraproject.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Jeanluc | Auge | Orange | jeanluc.auge@orange.com |
|
||||
| Jan | Kundrát | Telecom Infra Project | jan.kundrat@telecominfraproject.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Jeanluc | Augé | Orange | jeanluc.auge@orange.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Jonas | Mårtensson | RISE Research Sweden | jonas.martensson@ri.se |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Mattia | Cantono | Politecnico di Torino | mattia.cantono@polito.it |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Miguel | Garrich | University Catalunya | miquel.garrich@upct.es |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Raj | Nagarajan | Lumentum | raj.nagarajan@lumentum.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Roberts | Miculens | Lattelecom | roberts.miculens@lattelecom.lv |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Shengxiang | Zhu | University of Arizona | szhu@email.arizona.edu |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Stefan | Melin | Telia Company | Stefan.Melin@teliacompany.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Vittorio | Curri | Politecnico di Torino | vittorio.curri@polito.it |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
| Xufeng | Liu | Jabil | xufeng_liu@jabil.com |
|
||||
+----------+------------+-----------------------+--------------------------------------+
|
||||
|
||||
PSE WG Charter
|
||||
--------------
|
||||
|
||||
- Goal is to build an end-to-end simulation environment which defines the
|
||||
|
||||
@@ -4,10 +4,39 @@ gnpy\.core package
|
||||
Submodules
|
||||
----------
|
||||
|
||||
gnpy\.core\.ansi_escapes module
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: gnpy.core.ansi_escapes
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
gnpy\.core\.convert module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: gnpy.core.convert
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
gnpy\.core\.elements module
|
||||
---------------------------
|
||||
|
||||
.. automodule:: gnpy.core.elements
|
||||
|
||||
gnpy\.core\.equipment module
|
||||
----------------------------
|
||||
|
||||
.. automodule:: gnpy.core.equipment
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
gnpy\.core\.exceptions module
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: gnpy.core.exceptions
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -16,30 +45,34 @@ gnpy\.core\.execute module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: gnpy.core.execute
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
gnpy\.core\.info module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: gnpy.core.info
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
gnpy\.core\.network module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: gnpy.core.network
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
gnpy\.core\.node module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: gnpy.core.node
|
||||
|
||||
gnpy\.core\.request module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: gnpy.core.request
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
gnpy\.core\.service_sheet module
|
||||
--------------------------------
|
||||
|
||||
.. automodule:: gnpy.core.service_sheet
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -48,23 +81,14 @@ gnpy\.core\.units module
|
||||
------------------------
|
||||
|
||||
.. automodule:: gnpy.core.units
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
gnpy\.core\.utils module
|
||||
------------------------
|
||||
|
||||
.. automodule:: gnpy.core.utils
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: gnpy.core
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
@@ -12,6 +12,3 @@ Module contents
|
||||
---------------
|
||||
|
||||
.. automodule:: gnpy
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
124
examples/2019-demo-equipment.json
Normal file
124
examples/2019-demo-equipment.json
Normal file
@@ -0,0 +1,124 @@
|
||||
{ "Edfa":[
|
||||
|
||||
{
|
||||
"type_variety": "fixed27",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 27,
|
||||
"gain_min": 27,
|
||||
"p_max": 21,
|
||||
"nf0": 5.5,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
|
||||
{
|
||||
"type_variety": "fixed22",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 22,
|
||||
"gain_min": 22,
|
||||
"p_max": 21,
|
||||
"nf0": 5.5,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
],
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127
|
||||
},
|
||||
{
|
||||
"type_variety": "NZDF",
|
||||
"dispersion": 0.5e-05,
|
||||
"gamma": 0.00146
|
||||
},
|
||||
{
|
||||
"type_variety": "LOF",
|
||||
"dispersion": 2.2e-05,
|
||||
"gamma": 0.000843
|
||||
}
|
||||
],
|
||||
"Span":[{
|
||||
"power_mode": false,
|
||||
"delta_power_range_db": [-2,3,0.5],
|
||||
"max_fiber_lineic_loss_for_raman": 0.25,
|
||||
"target_extended_gain": 2.5,
|
||||
"max_length": 150,
|
||||
"length_units": "km",
|
||||
"max_loss": 28,
|
||||
"padding": 10,
|
||||
"EOL": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
}
|
||||
],
|
||||
"Roadm":[{
|
||||
"target_pch_out_db": -25,
|
||||
"add_drop_osnr": 30.00,
|
||||
"restrictions": {
|
||||
"preamp_variety_list":[],
|
||||
"booster_variety_list":[]
|
||||
}
|
||||
}],
|
||||
"SI":[{
|
||||
"f_min": 191.6e12,
|
||||
"baud_rate": 32e9,
|
||||
"f_max":195.1e12,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0,0,1],
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"sys_margins": 2
|
||||
}],
|
||||
"Transceiver":[
|
||||
{
|
||||
"type_variety": "Cassini",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
|
||||
"format": "dp-qpsk",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 37.5e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "16-qam",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 15,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type_variety": "Voyager",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 12,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 37.5e9,
|
||||
"cost":1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
}
|
||||
67
examples/2019-demo-services.json
Normal file
67
examples/2019-demo-services.json
Normal file
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "first",
|
||||
"source": "netconf:10.0.254.93:830",
|
||||
"destination": "netconf:10.0.254.94:830",
|
||||
"src-tp-id": "trx-Amsterdam",
|
||||
"dst-tp-id": "trx-Bremen",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Cassini",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "second",
|
||||
"source": "netconf:10.0.254.93:830",
|
||||
"destination": "netconf:10.0.254.94:830",
|
||||
"src-tp-id": "trx-Amsterdam",
|
||||
"dst-tp-id": "trx-Bremen",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Cassini",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronization": [
|
||||
{
|
||||
"synchronization-id": "some redundancy please",
|
||||
"svec": {
|
||||
"relaxable": "false",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"first",
|
||||
"second"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1263
examples/2019-demo-topology.json
Normal file
1263
examples/2019-demo-topology.json
Normal file
File diff suppressed because it is too large
Load Diff
179
examples/2019-generate-tip-demo.py
Normal file
179
examples/2019-generate-tip-demo.py
Normal file
@@ -0,0 +1,179 @@
|
||||
# How many nodes in the ring topology? Up to eight is supported, then I ran out of cities..
|
||||
HOW_MANY = 3
|
||||
|
||||
# city names
|
||||
ALL_CITIES = [
|
||||
'Amsterdam',
|
||||
'Bremen',
|
||||
'Cologne',
|
||||
'Dueseldorf',
|
||||
'Eindhoven',
|
||||
'Frankfurt',
|
||||
'Ghent',
|
||||
'Hague',
|
||||
]
|
||||
# end of configurable parameters
|
||||
|
||||
|
||||
J = {
|
||||
"elements": [],
|
||||
"connections": [],
|
||||
}
|
||||
|
||||
def unidir_join(a, b):
|
||||
global J
|
||||
J["connections"].append(
|
||||
{"from_node": a, "to_node": b}
|
||||
)
|
||||
|
||||
def mk_edfa(name, gain, voa=0.0):
|
||||
global J
|
||||
J["elements"].append(
|
||||
{"uid": name, "type": "Edfa", "type_variety": f"fixed{gain}", "operational": {"gain_target": gain, "out_voa": voa}}
|
||||
)
|
||||
|
||||
def add_att(a, b, att):
|
||||
global J
|
||||
if att > 0:
|
||||
uid = f"att-({a})-({b})"
|
||||
else:
|
||||
uid = f"splice-({a})-({b})"
|
||||
J["elements"].append(
|
||||
{"uid": uid, "type": "Fused", "params": {"loss": att}},
|
||||
)
|
||||
unidir_join(a, uid)
|
||||
unidir_join(uid, b)
|
||||
return uid
|
||||
|
||||
def build_fiber(city1, city2):
|
||||
global J
|
||||
J["elements"].append(
|
||||
{
|
||||
"uid": f"fiber-{city1}-{city2}",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": 1.5,
|
||||
"con_out": 1.5,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
def unidir_patch(a, b):
|
||||
global J
|
||||
uid = f"patch-({a})-({b})"
|
||||
J["elements"].append(
|
||||
{
|
||||
"uid": uid,
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5,
|
||||
}
|
||||
}
|
||||
)
|
||||
add_att(a, uid, 0.0)
|
||||
add_att(uid, b, 0.0)
|
||||
|
||||
for CITY in (ALL_CITIES[x] for x in range(0, HOW_MANY)):
|
||||
J["elements"].append(
|
||||
{"uid": f"trx-{CITY}", "type": "Transceiver"}
|
||||
)
|
||||
target_pwr = [
|
||||
{"to_node": f"trx-{CITY}", "target_pch_out_db": -25},
|
||||
{"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L1))", "target_pch_out_db": -12},
|
||||
{"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L2))", "target_pch_out_db": -12},
|
||||
]
|
||||
J["elements"].append(
|
||||
{"uid": f"roadm-{CITY}-AD", "type": "Roadm", "params": {"target_pch_out_db": -2.0, "per_degree_target_pch_out_db": target_pwr}}
|
||||
)
|
||||
unidir_join(f"trx-{CITY}", f"roadm-{CITY}-AD")
|
||||
unidir_join(f"roadm-{CITY}-AD", f"trx-{CITY}")
|
||||
|
||||
for n in (1,2):
|
||||
target_pwr = [
|
||||
{"to_node": f"roadm-{CITY}-L{n}-booster", "target_pch_out_db": -23},
|
||||
{"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-AD))", "target_pch_out_db": -12},
|
||||
]
|
||||
for m in (1,2):
|
||||
if m == n:
|
||||
continue
|
||||
target_pwr.append(
|
||||
{"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-L{m}))", "target_pch_out_db": -12},
|
||||
)
|
||||
J["elements"].append(
|
||||
{"uid": f"roadm-{CITY}-L{n}", "type": "Roadm", "params": {"target_pch_out_db": -23.0, "per_degree_target_pch_out_db": target_pwr}}
|
||||
)
|
||||
mk_edfa(f"roadm-{CITY}-L{n}-booster", 22)
|
||||
mk_edfa(f"roadm-{CITY}-L{n}-preamp", 27)
|
||||
unidir_join(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{n}-booster")
|
||||
unidir_join(f"roadm-{CITY}-L{n}-preamp", f"roadm-{CITY}-L{n}")
|
||||
|
||||
unidir_patch(f"roadm-{CITY}-AD", f"roadm-{CITY}-L{n}")
|
||||
unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-AD")
|
||||
for m in (1,2):
|
||||
if m == n:
|
||||
continue
|
||||
#add_att(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}", 22)
|
||||
unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}")
|
||||
|
||||
for city1, city2 in ((ALL_CITIES[i], ALL_CITIES[i + 1] if i < HOW_MANY - 1 else ALL_CITIES[0]) for i in range(0, HOW_MANY)):
|
||||
build_fiber(city1, city2)
|
||||
unidir_join(f"roadm-{city1}-L1-booster", f"fiber-{city1}-{city2}")
|
||||
unidir_join(f"fiber-{city1}-{city2}", f"roadm-{city2}-L2-preamp")
|
||||
build_fiber(city2, city1)
|
||||
unidir_join(f"roadm-{city2}-L2-booster", f"fiber-{city2}-{city1}")
|
||||
unidir_join(f"fiber-{city2}-{city1}", f"roadm-{city1}-L1-preamp")
|
||||
|
||||
|
||||
for _, E in enumerate(J["elements"]):
|
||||
uid = E["uid"]
|
||||
if uid.startswith("roadm-") and (uid.endswith("-L1-booster") or uid.endswith("-L2-booster")):
|
||||
E["operational"]["out_voa"] = 12.0
|
||||
#if uid.endswith("-AD-add"):
|
||||
# E["operational"]["out_voa"] = 21
|
||||
|
||||
translate = {
|
||||
#"trx-Amsterdam": "10.0.254.93",
|
||||
#"trx-Bremen": "10.0.254.94",
|
||||
"trx-Amsterdam": "10.0.254.76",
|
||||
"trx-Bremen": "10.0.254.77",
|
||||
|
||||
# Amsterdam A/D: coherent-v9u
|
||||
"roadm-Amsterdam-AD": "10.0.254.107",
|
||||
# Bremen A/D: -spi
|
||||
"roadm-Bremen-AD": "10.0.254.225",
|
||||
|
||||
# Amsterdam -> Bremen ...QR79
|
||||
"roadm-Amsterdam-L1": "10.0.254.78",
|
||||
# Bremen -> Amsterdam ...QCP9
|
||||
"roadm-Bremen-L2": "10.0.254.102",
|
||||
|
||||
# Bremen -> Cologne ...WKP
|
||||
"roadm-Bremen-L1": "10.0.254.100",
|
||||
# Cologne -> Bremen ...QLK6
|
||||
"roadm-Cologne-L2": "10.0.254.104",
|
||||
|
||||
# Cologne -> Amsterdam ...TQQ
|
||||
"roadm-Cologne-L1": "10.0.254.99",
|
||||
# Amsterdam -> Cologne ...Q7JS
|
||||
"roadm-Amsterdam-L2": "10.0.254.79",
|
||||
|
||||
# spare Line/Degree ...QC8B
|
||||
"spare-line-degree": "10.0.254.101",
|
||||
# spare Add/Drop: ...NNN
|
||||
"spare-add-drop": "10.0.254.228",
|
||||
}
|
||||
|
||||
import json
|
||||
s = json.dumps(J, indent=2)
|
||||
for (old, new) in translate.items():
|
||||
s = s.replace(f'"{old}"', f'"netconf:{new}:830"')
|
||||
print(s)
|
||||
7631
examples/CORONET_CONUS_Topology.json
Normal file
7631
examples/CORONET_CONUS_Topology.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
160
examples/Juniper-BoosterHG.json
Normal file
160
examples/Juniper-BoosterHG.json
Normal file
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"nf_fit_coeff": [
|
||||
0.0008,
|
||||
0.0272,
|
||||
-0.2249,
|
||||
6.4902
|
||||
],
|
||||
"f_min": 191.35e12,
|
||||
"f_max": 196.1e12,
|
||||
"nf_ripple": [
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
],
|
||||
"gain_ripple": [
|
||||
0.15017064489112,
|
||||
0.14157768006701,
|
||||
0.00223094639866,
|
||||
-0.06701528475711,
|
||||
-0.05982935510889,
|
||||
-0.01028161641541,
|
||||
0.02740682579566,
|
||||
0.02795958961474,
|
||||
0.00107516750419,
|
||||
-0.02199015912898,
|
||||
-0.00877407872698,
|
||||
0.0453465242881,
|
||||
0.1204721524288,
|
||||
0.18936662479061,
|
||||
0.23826109715241,
|
||||
0.26956762981574,
|
||||
0.27836159966498,
|
||||
0.26941687604691,
|
||||
0.23579878559464,
|
||||
0.18147717755444,
|
||||
0.1191656197655,
|
||||
0.05921587102177,
|
||||
0.01509526800668,
|
||||
-0.01053287269681,
|
||||
-0.02475397822447,
|
||||
-0.01847257118928,
|
||||
-0.00420121440538,
|
||||
0.01584903685091,
|
||||
0.0399193886097,
|
||||
0.04494451423784,
|
||||
0.04961788107202,
|
||||
0.03378873534338,
|
||||
0.01027114740367,
|
||||
-0.01319618927973,
|
||||
-0.04962835008375,
|
||||
-0.0765630234506,
|
||||
-0.10606051088777,
|
||||
-0.13550774706866,
|
||||
-0.15460322445561,
|
||||
-0.17113588777219,
|
||||
-0.18053287269681,
|
||||
-0.18324644053602,
|
||||
-0.19440221943049,
|
||||
-0.20897508375209,
|
||||
-0.23575900335007,
|
||||
-0.25188965661642,
|
||||
-0.22244242043552,
|
||||
-0.15656302345061
|
||||
],
|
||||
"dgt": [
|
||||
2.4553191172498,
|
||||
2.44342862248888,
|
||||
2.41879254989742,
|
||||
2.38192717604575,
|
||||
2.33147727493671,
|
||||
2.26678136721453,
|
||||
2.19013043016015,
|
||||
2.10336369905543,
|
||||
2.01414465424155,
|
||||
1.92915262384742,
|
||||
1.85543800978691,
|
||||
1.79748596476494,
|
||||
1.75428006928365,
|
||||
1.72461030013125,
|
||||
1.70379790088896,
|
||||
1.68845480656382,
|
||||
1.6761448370895,
|
||||
1.66286684904577,
|
||||
1.64799163036252,
|
||||
1.63068023161292,
|
||||
1.61073904908309,
|
||||
1.58973304612691,
|
||||
1.56750088631614,
|
||||
1.54578500307573,
|
||||
1.5242627235492,
|
||||
1.50335352244996,
|
||||
1.48420288841848,
|
||||
1.46637521309853,
|
||||
1.44977369463316,
|
||||
1.43476940680732,
|
||||
1.42089447397912,
|
||||
1.40864903907609,
|
||||
1.3966294751726,
|
||||
1.38430337205545,
|
||||
1.3710092503689,
|
||||
1.35690844654118,
|
||||
1.3405812000038,
|
||||
1.32210817897091,
|
||||
1.30069883494415,
|
||||
1.27657903892303,
|
||||
1.24931318255134,
|
||||
1.21911100318577,
|
||||
1.18632744096844,
|
||||
1.15209185089701,
|
||||
1.11575888725852,
|
||||
1.07773189112355,
|
||||
1.03941448941778,
|
||||
1.0
|
||||
]
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
convert_service_sheet.py
|
||||
========================
|
||||
|
||||
XLS parser that can be called to create a JSON request file in accordance with
|
||||
Yang model for requesting path computation.
|
||||
|
||||
See: draft-ietf-teas-yang-path-computation-01.txt
|
||||
"""
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO
|
||||
from json import dumps
|
||||
|
||||
from gnpy.core.service_sheet import Request, Element, Request_element
|
||||
from gnpy.core.service_sheet import parse_row, parse_excel, convert_service_sheet
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL))
|
||||
logger.info(f'Converting Service sheet {args.workbook!r} into gnpy JSON format')
|
||||
if args.output is None:
|
||||
data = convert_service_sheet(args.workbook,'eqpt_config.json')
|
||||
print(dumps(data, indent=2))
|
||||
else:
|
||||
data = convert_service_sheet(args.workbook,'eqpt_config.json',args.output)
|
||||
@@ -11,93 +11,91 @@ If not present in the "Nodes" sheet, the "Type" column will be implicitly
|
||||
determined based on the topology.
|
||||
"""
|
||||
|
||||
from sys import exit
|
||||
try:
|
||||
from xlrd import open_workbook
|
||||
except ModuleNotFoundError:
|
||||
exit('Required: `pip install xlrd`')
|
||||
from argparse import ArgumentParser
|
||||
from collections import namedtuple, defaultdict
|
||||
|
||||
PARSER = ArgumentParser()
|
||||
PARSER.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls',
|
||||
help='create the mandatory columns in Eqpt sheet')
|
||||
ALL_ROWS = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
|
||||
|
||||
Shortlink = namedtuple('Link', 'src dest')
|
||||
class Node:
|
||||
""" Node element contains uid, list of connected nodes and eqpt type
|
||||
"""
|
||||
def __init__(self, uid, to_node):
|
||||
self.uid = uid
|
||||
self.to_node = to_node
|
||||
self.eqpt = None
|
||||
|
||||
Shortnode = namedtuple('Node', 'nodename eqt')
|
||||
def __repr__(self):
|
||||
return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n'
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls',
|
||||
help = 'create the mandatory columns in Eqpt sheet ')
|
||||
all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
|
||||
def __str__(self):
|
||||
return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n'
|
||||
|
||||
def read_excel(input_filename):
|
||||
with open_workbook(input_filename) as wb:
|
||||
""" read excel Nodes and Links sheets and create a dict of nodes with
|
||||
their to_nodes and type of eqpt
|
||||
"""
|
||||
with open_workbook(input_filename) as wobo:
|
||||
# reading Links sheet
|
||||
links_sheet = wb.sheet_by_name('Links')
|
||||
links = []
|
||||
nodeoccuranceinlinks = []
|
||||
links_by_src = defaultdict(list)
|
||||
links_by_dest = defaultdict(list)
|
||||
for row in all_rows(links_sheet, start=5):
|
||||
links.append(Shortlink(row[0].value,row[1].value))
|
||||
links_by_src[row[0].value].append(Shortnode(row[1].value,''))
|
||||
links_by_dest[row[1].value].append(Shortnode(row[0].value,''))
|
||||
#print(f'source {links[len(links)-1].src} dest {links[len(links)-1].dest}')
|
||||
nodeoccuranceinlinks.append(row[0].value)
|
||||
nodeoccuranceinlinks.append(row[1].value)
|
||||
links_sheet = wobo.sheet_by_name('Links')
|
||||
nodes = {}
|
||||
for row in ALL_ROWS(links_sheet, start=5):
|
||||
try:
|
||||
nodes[row[0].value].to_node.append(row[1].value)
|
||||
except KeyError:
|
||||
nodes[row[0].value] = Node(row[0].value, [row[1].value])
|
||||
try:
|
||||
nodes[row[1].value].to_node.append(row[0].value)
|
||||
except KeyError:
|
||||
nodes[row[1].value] = Node(row[1].value, [row[0].value])
|
||||
|
||||
# reading Nodes sheet
|
||||
nodes_sheet = wb.sheet_by_name('Nodes')
|
||||
nodes = []
|
||||
node_degree = []
|
||||
for row in all_rows(nodes_sheet, start=5) :
|
||||
nodes_sheet = wobo.sheet_by_name('Nodes')
|
||||
for row in ALL_ROWS(nodes_sheet, start=5):
|
||||
node = row[0].value
|
||||
eqpt = row[6].value
|
||||
try:
|
||||
if eqpt == 'ILA' and len(nodes[node].to_node) != 2:
|
||||
print(f'Inconsistancy ILA node with degree > 2: {node} ')
|
||||
exit()
|
||||
if eqpt == '' and len(nodes[node].to_node) == 2:
|
||||
nodes[node].eqpt = 'ILA'
|
||||
elif eqpt == '' and len(nodes[node].to_node) != 2:
|
||||
nodes[node].eqpt = 'ROADM'
|
||||
else:
|
||||
nodes[node].eqpt = eqpt
|
||||
except KeyError:
|
||||
print(f'inconsistancy between nodes and links sheet: {node} is not listed in links')
|
||||
exit()
|
||||
return nodes
|
||||
|
||||
temp_eqt = row[6].value
|
||||
# verify node degree to confirm eqt type
|
||||
node_degree.append(nodeoccuranceinlinks.count(row[0].value))
|
||||
if temp_eqt.lower() == 'ila' and nodeoccuranceinlinks.count(row[0].value) !=2 :
|
||||
print(f'Inconsistancy: node {nodes[len(nodes)-1]} has degree \
|
||||
{node_degree[len(nodes)-1]} and can not be an ILA ... replaced by ROADM')
|
||||
temp_eqt = 'ROADM'
|
||||
if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) == 2 :
|
||||
temp_eqt = 'ILA'
|
||||
if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) != 2 :
|
||||
temp_eqt = 'ROADM'
|
||||
# print(f'node {nodes[len(nodes)-1]} eqt {temp_eqt}')
|
||||
nodes.append(Shortnode(row[0].value,temp_eqt))
|
||||
# print(len(nodes)-1)
|
||||
print(f'reading: node {nodes[len(nodes)-1].nodename} eqpt {temp_eqt}')
|
||||
return links,nodes, links_by_src , links_by_dest
|
||||
|
||||
def create_eqt_template(links,nodes, links_by_src , links_by_dest, input_filename):
|
||||
def create_eqt_template(nodes, input_filename):
|
||||
""" writes list of node A node Z corresponding to Nodes and Links sheets in order
|
||||
to help user populating Eqpt
|
||||
"""
|
||||
output_filename = f'{input_filename[:-4]}_eqpt_sheet.txt'
|
||||
with open(output_filename,'w') as my_file :
|
||||
with open(output_filename, 'w', encoding='utf-8') as my_file:
|
||||
# print header similar to excel
|
||||
my_file.write('OPTIONAL\n\n\n\
|
||||
\t\tNode a egress amp (from a to z)\t\t\t\t\tNode a ingress amp (from z to a) \
|
||||
\nNode A \tNode Z \tamp type \tatt_in \tamp gain \ttilt \tatt_out\
|
||||
amp type \tatt_in \tamp gain \ttilt \tatt_out\n')
|
||||
|
||||
tab = []
|
||||
temp = []
|
||||
i = 0
|
||||
for lk in links:
|
||||
if [e for n,e in nodes if n==lk.src][0] != 'FUSED' :
|
||||
temp = [lk.src , lk.dest]
|
||||
tab.append(temp)
|
||||
my_file.write(f'{temp[0]}\t{temp[1]}\n')
|
||||
for n in nodes :
|
||||
if n.eqt.lower() == 'roadm' :
|
||||
for src in links_by_dest[n.nodename] :
|
||||
temp = [n.nodename , src.nodename]
|
||||
tab.append(temp)
|
||||
# print(temp)
|
||||
my_file.write(f'{temp[0]}\t{temp[1]}\n')
|
||||
i = i + 1
|
||||
|
||||
for node in nodes.values():
|
||||
if node.eqpt == 'ILA':
|
||||
my_file.write(f'{node.uid}\t{node.to_node[0]}\n')
|
||||
if node.eqpt == 'ROADM':
|
||||
for to_node in node.to_node:
|
||||
my_file.write(f'{node.uid}\t{to_node}\n')
|
||||
|
||||
print(f'File {output_filename} successfully created with Node A - Node Z ' +
|
||||
' entries for Eqpt sheet in excel file.')
|
||||
' entries for Eqpt sheet in excel file.')
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
input_filename = args.workbook
|
||||
links,nodes,links_by_src, links_by_dest = read_excel(input_filename)
|
||||
create_eqt_template(links,nodes, links_by_src , links_by_dest , input_filename)
|
||||
ARGS = PARSER.parse_args()
|
||||
create_eqt_template(read_excel(ARGS.workbook), ARGS.workbook)
|
||||
|
||||
1033
examples/demo.json
Normal file
1033
examples/demo.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"nf_ripple": "NFR0_96.txt",
|
||||
"gain_ripple": "DFG0_96.txt",
|
||||
"dgt": "DGT_96.txt"
|
||||
"nf_ripple": "NFR_96.txt",
|
||||
"gain_ripple": "DFG_96.txt",
|
||||
"dgt": "DGT_96.txt",
|
||||
"nf_fit_coeff": "pNFfit3.txt"
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
-1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01
|
||||
-2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02
|
||||
-1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01
|
||||
-2.0500000000000000e+01 -2.0489473680000000e+01 -2.0478947370000000e+01 -2.0468421050000000e+01 -2.0457894740000000e+01 -2.0447368420000000e+01 -2.0436842110000001e+01 -2.0426315790000000e+01 -2.0415789470000000e+01 -2.0405263160000001e+01 -2.0394736840000000e+01 -2.0384210530000001e+01 -2.0373684210000000e+01 -2.0363157890000000e+01 -2.0352631580000001e+01 -2.0342105260000000e+01 -2.0331578950000001e+01 -2.0321052630000001e+01 -2.0310526320000001e+01 -2.0300000000000001e+01 -2.0289473680000000e+01 -2.0278947370000001e+01 -2.0268421050000001e+01 -2.0257894740000001e+01 -2.0247368420000001e+01 -2.0236842110000001e+01 -2.0226315790000001e+01 -2.0215789470000001e+01 -2.0205263160000001e+01 -2.0194736840000001e+01 -2.0184210530000001e+01 -2.0173684210000001e+01 -2.0163157890000001e+01 -2.0152631580000001e+01 -2.0142105260000001e+01 -2.0131578950000002e+01 -2.0121052630000001e+01 -2.0110526320000002e+01 -2.0100000000000001e+01 -2.0089473680000001e+01 -2.0078947370000002e+01 -2.0068421050000001e+01 -2.0057894739999998e+01 -2.0047368420000002e+01 -2.0036842109999998e+01 -2.0026315790000002e+01 -2.0015789470000001e+01 -2.0005263159999998e+01 -1.9994736840000002e+01 -1.9984210529999999e+01 -1.9973684209999998e+01 -1.9963157890000002e+01 -1.9952631579999998e+01 -1.9942105260000002e+01 -1.9931578949999999e+01 -1.9921052629999998e+01 -1.9910526319999999e+01 -1.9899999999999999e+01 -1.9889473679999998e+01 -1.9878947369999999e+01 -1.9868421049999998e+01 -1.9857894739999999e+01 -1.9847368419999999e+01 -1.9836842109999999e+01 -1.9826315789999999e+01 -1.9815789469999999e+01 -1.9805263159999999e+01 -1.9794736839999999e+01 -1.9784210529999999e+01 -1.9773684209999999e+01 -1.9763157889999999e+01 -1.9752631579999999e+01 -1.9742105259999999e+01 -1.9731578949999999e+01 -1.9721052629999999e+01 -1.9710526320000000e+01 -1.9699999999999999e+01 -1.9689473679999999e+01 -1.9678947369999999e+01 -1.9668421049999999e+01 -1.9657894740000000e+01 -1.9647368419999999e+01 -1.9636842110000000e+01 -1.9626315790000000e+01 -1.9615789469999999e+01 -1.9605263160000000e+01 -1.9594736839999999e+01 -1.9584210530000000e+01 -1.9573684210000000e+01 -1.9563157889999999e+01 -1.9552631580000000e+01 -1.9542105260000000e+01 -1.9531578950000000e+01 -1.9521052630000000e+01 -1.9510526320000000e+01 -1.9500000000000000e+01
|
||||
-2.0500000000000000e+01 -2.0489473680000000e+01 -2.0478947370000000e+01 -2.0468421050000000e+01 -2.0457894740000000e+01 -2.0447368420000000e+01 -2.0436842110000001e+01 -2.0426315790000000e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.9573684210000000e+01 -1.9563157889999999e+01 -1.9552631580000000e+01 -1.9542105260000000e+01 -1.9531578950000000e+01 -1.9521052630000000e+01 -1.9510526320000000e+01 -1.9500000000000000e+01
|
||||
-1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.4460000000000001e+01
|
||||
-1.4460000000000001e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01
|
||||
-1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.4460000000000001e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02
|
||||
@@ -1,8 +0,0 @@
|
||||
7.0000000000000000e+01 1.1700000000000000e+02 1.0800000000000000e+02 1.0800000000000000e+02 3.2000000000000000e+01 7.0000000000000000e+01 1.0800000000000000e+02 9.7000000000000000e+01 1.1600000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
7.9000000000000000e+01 1.1000000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 7.1000000000000000e+01 1.1400000000000000e+02 1.1100000000000000e+02 1.1700000000000000e+02 1.1200000000000000e+02 3.2000000000000000e+01 6.6000000000000000e+01 1.0800000000000000e+02 1.1700000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01
|
||||
7.9000000000000000e+01 1.1000000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 7.1000000000000000e+01 1.1400000000000000e+02 1.1100000000000000e+02 1.1700000000000000e+02 1.1200000000000000e+02 3.2000000000000000e+01 8.2000000000000000e+01 1.0100000000000000e+02 1.0000000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
7.0000000000000000e+01 1.1700000000000000e+02 1.0800000000000000e+02 1.0800000000000000e+02 3.2000000000000000e+01 1.1900000000000000e+02 3.2000000000000000e+01 8.3000000000000000e+01 8.2000000000000000e+01 8.3000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
6.6000000000000000e+01 1.1100000000000000e+02 1.1600000000000000e+02 1.0400000000000000e+02 3.2000000000000000e+01 6.9000000000000000e+01 1.1000000000000000e+02 1.0000000000000000e+02 1.1500000000000000e+02 3.2000000000000000e+01 1.1900000000000000e+02 3.2000000000000000e+01 8.3000000000000000e+01 8.2000000000000000e+01 8.3000000000000000e+01
|
||||
1.0400000000000000e+02 1.0100000000000000e+02 9.7000000000000000e+01 1.1800000000000000e+02 1.2100000000000000e+02 3.2000000000000000e+01 9.8000000000000000e+01 1.0800000000000000e+02 1.1700000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
1.0400000000000000e+02 1.0100000000000000e+02 9.7000000000000000e+01 1.1800000000000000e+02 1.2100000000000000e+02 3.2000000000000000e+01 1.1400000000000000e+02 1.0100000000000000e+02 1.0000000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
1.1900000000000000e+02 1.1100000000000000e+02 1.1400000000000000e+02 1.1500000000000000e+02 1.1600000000000000e+02 3.2000000000000000e+01 9.9000000000000000e+01 9.7000000000000000e+01 1.1500000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
|
||||
@@ -1,301 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Nov 27 12:32:04 2017
|
||||
|
||||
@author: briantaylor
|
||||
"""
|
||||
import numpy as np
|
||||
from numpy import polyfit, polyval, mean
|
||||
from utilities import lin2db, db2lin, itufs, freq2wavelength
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.constants import h
|
||||
|
||||
|
||||
def noise_profile(nf, gain, ffs, df):
|
||||
""" noise_profile(nf, gain, ffs, df) computes amplifier ase
|
||||
|
||||
:param nf: Noise figure in dB
|
||||
:param gain: Actual gain calculated for the EDFA in dB units
|
||||
:param ffs: A numpy array of frequencies
|
||||
:param df: the reference bw in THz
|
||||
:type nf: numpy.ndarray
|
||||
:type gain: numpy.ndarray
|
||||
:type ffs: numpy.ndarray
|
||||
:type df: float
|
||||
:return: the asepower in dBm
|
||||
:rtype: numpy.ndarray
|
||||
|
||||
ASE POWER USING PER CHANNEL GAIN PROFILE
|
||||
INPUTS:
|
||||
NF_dB - Noise figure in dB, vector of length number of channels or
|
||||
spectral slices
|
||||
G_dB - Actual gain calculated for the EDFA, vector of length number of
|
||||
channels or spectral slices
|
||||
ffs - Center frequency grid of the channels or spectral slices in THz,
|
||||
vector of length number of channels or spectral slices
|
||||
dF - width of each channel or spectral slice in THz,
|
||||
vector of length number of channels or spectral slices
|
||||
OUTPUT:
|
||||
ase_dBm - ase in dBm per channel or spectral slice
|
||||
NOTE: the output is the total ASE in the channel or spectral slice. For
|
||||
50GHz channels the ASE BW is effectively 0.4nm. To get to noise power in
|
||||
0.1nm, subtract 6dB.
|
||||
|
||||
ONSR is usually quoted as channel power divided by
|
||||
the ASE power in 0.1nm RBW, regardless of the width of the actual
|
||||
channel. This is a historical convention from the days when optical
|
||||
signals were much smaller (155Mbps, 2.5Gbps, ... 10Gbps) than the
|
||||
resolution of the OSAs that were used to measure spectral power which
|
||||
were set to 0.1nm resolution for convenience. Moving forward into
|
||||
flexible grid and high baud rate signals, it may be convenient to begin
|
||||
quoting power spectral density in the same BW for both signal and ASE,
|
||||
e.g. 12.5GHz."""
|
||||
|
||||
h_mWThz = 1e-3 * h * (1e14)**2
|
||||
nf_lin = db2lin(nf)
|
||||
g_lin = db2lin(gain)
|
||||
ase = h_mWThz * df * ffs * (nf_lin * g_lin - 1)
|
||||
asedb = lin2db(ase)
|
||||
|
||||
return asedb
|
||||
|
||||
|
||||
def gain_profile(dfg, dgt, Pin, gp, gtp):
|
||||
"""
|
||||
:param dfg: design flat gain
|
||||
:param dgt: design gain tilt
|
||||
:param Pin: channing input power profile
|
||||
:param gp: Average gain setpoint in dB units
|
||||
:param gtp: gain tilt setting
|
||||
:type dfg: numpy.ndarray
|
||||
:type dgt: numpy.ndarray
|
||||
:type Pin: numpy.ndarray
|
||||
:type gp: float
|
||||
:type gtp: float
|
||||
:return: gain profile in dBm
|
||||
:rtype: numpy.ndarray
|
||||
|
||||
AMPLIFICATION USING INPUT PROFILE
|
||||
INPUTS:
|
||||
DFG - vector of length number of channels or spectral slices
|
||||
DGT - vector of length number of channels or spectral slices
|
||||
Pin - input powers vector of length number of channels or
|
||||
spectral slices
|
||||
Gp - provisioned gain length 1
|
||||
GTp - provisioned tilt length 1
|
||||
|
||||
OUTPUT:
|
||||
amp gain per channel or spectral slice
|
||||
NOTE: there is no checking done for violations of the total output power
|
||||
capability of the amp.
|
||||
Ported from Matlab version written by David Boerges at Ciena.
|
||||
Based on:
|
||||
R. di Muro, "The Er3+ fiber gain coefficient derived from a dynamic
|
||||
gain
|
||||
tilt technique", Journal of Lightwave Technology, Vol. 18, Iss. 3,
|
||||
Pp. 343-347, 2000.
|
||||
"""
|
||||
err_tolerance = 1.0e-11
|
||||
simple_opt = True
|
||||
|
||||
# TODO make all values linear unit and convert to dB units as needed within
|
||||
# this function.
|
||||
nchan = list(range(len(Pin)))
|
||||
|
||||
# TODO find a way to use these or lose them. Primarily we should have a
|
||||
# way to determine if exceeding the gain or output power of the amp
|
||||
tot_in_power_db = lin2db(np.sum(db2lin(Pin)))
|
||||
avg_gain_db = lin2db(mean(db2lin(dfg)))
|
||||
|
||||
# Linear fit to get the
|
||||
p = polyfit(nchan, dgt, 1)
|
||||
dgt_slope = p[0]
|
||||
|
||||
# Calculate the target slope- Currently assumes equal spaced channels
|
||||
# TODO make it so that supports arbitrary channel spacing.
|
||||
targ_slope = gtp / (len(nchan) - 1)
|
||||
|
||||
# 1st estimate of DGT scaling
|
||||
dgts1 = targ_slope / dgt_slope
|
||||
|
||||
# when simple_opt is true code makes 2 attempts to compute gain and
|
||||
# the internal voa value. This is currently here to provide direct
|
||||
# comparison with original Matlab code. Will be removed.
|
||||
# TODO replace with loop
|
||||
|
||||
if simple_opt:
|
||||
|
||||
# 1st estimate of Er gain & voa loss
|
||||
g1st = dfg + dgt * dgts1
|
||||
voa = lin2db(mean(db2lin(g1st))) - gp
|
||||
|
||||
# 2nd estimate of Amp ch gain using the channel input profile
|
||||
g2nd = g1st - voa
|
||||
pout_db = lin2db(np.sum(db2lin(Pin + g2nd)))
|
||||
dgts2 = gp - (pout_db - tot_in_power_db)
|
||||
|
||||
# Center estimate of amp ch gain
|
||||
xcent = dgts2
|
||||
gcent = g1st - voa + dgt * xcent
|
||||
pout_db = lin2db(np.sum(db2lin(Pin + gcent)))
|
||||
gavg_cent = pout_db - tot_in_power_db
|
||||
|
||||
# Lower estimate of Amp ch gain
|
||||
deltax = np.max(g1st) - np.min(g1st)
|
||||
xlow = dgts2 - deltax
|
||||
glow = g1st - voa + xlow * dgt
|
||||
pout_db = lin2db(np.sum(db2lin(Pin + glow)))
|
||||
gavg_low = pout_db - tot_in_power_db
|
||||
|
||||
# Upper gain estimate
|
||||
xhigh = dgts2 + deltax
|
||||
ghigh = g1st - voa + xhigh * dgt
|
||||
pout_db = lin2db(np.sum(db2lin(Pin + ghigh)))
|
||||
gavg_high = pout_db - tot_in_power_db
|
||||
|
||||
# compute slope
|
||||
slope1 = (gavg_low - gavg_cent) / (xlow - xcent)
|
||||
slope2 = (gavg_cent - gavg_high) / (xcent - xhigh)
|
||||
|
||||
if np.abs(gp - gavg_cent) <= err_tolerance:
|
||||
dgts3 = xcent
|
||||
elif gp < gavg_cent:
|
||||
dgts3 = xcent - (gavg_cent - gp) / slope1
|
||||
else:
|
||||
dgts3 = xcent + (-gavg_cent + gp) / slope2
|
||||
|
||||
gprofile = g1st - voa + dgt * dgts3
|
||||
else:
|
||||
gprofile = None
|
||||
|
||||
return gprofile
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
plt.close('all')
|
||||
fc = itufs(0.05)
|
||||
lc = freq2wavelength(fc) / 1000
|
||||
nchan = list(range(len(lc)))
|
||||
df = np.array([0.05] * (nchan[-1] + 1))
|
||||
# TODO remove path dependence
|
||||
path = ''
|
||||
|
||||
"""
|
||||
DFG_96: Design flat gain at each wavelength in the 96 channel 50GHz ITU
|
||||
grid in dB. This can be experimentally determined by measuring the gain
|
||||
at each wavelength using a full, flat channel (or ASE) load at the input.
|
||||
The amplifier should be set to its maximum flat gain (tilt = 0dB). This
|
||||
measurement captures the ripple of the amplifier. If the amplifier was
|
||||
designed to be mimimum ripple at some other tilt value, then the ripple
|
||||
reflected in this measurement will not be that minimum. However, when
|
||||
the DGT gets applied through the provisioning of tilt, the model should
|
||||
accurately reproduce the expected ripple at that tilt value. One could
|
||||
also do the measurement at some expected tilt value and back-calculate
|
||||
this vector using the DGT method. Alternatively, one could re-write the
|
||||
algorithm to accept a nominal tilt and a tiled version of this vector.
|
||||
"""
|
||||
|
||||
dfg_96 = np.loadtxt(path + 'DFG_96.txt')
|
||||
|
||||
"""maximum gain for flat operation - the amp in the data file was designed
|
||||
for 25dB gain and has an internal VOA for setting the external gain
|
||||
"""
|
||||
|
||||
avg_dfg = dfg_96.mean()
|
||||
|
||||
"""
|
||||
DGT_96: This is the so-called Dynamic Gain Tilt of the EDFA in dB/dB. It
|
||||
is the change in gain at each wavelength corresponding to a 1dB change at
|
||||
the longest wavelength supported. The value can be obtained
|
||||
experimentally or through analysis of the cross sections or Giles
|
||||
parameters of the Er fibre. This is experimentally measured by changing
|
||||
the gain of the amplifier above the maximum flat gain while not changing
|
||||
the internal VOA (i.e. the mid-stage VOA is set to minimum and does not
|
||||
change during the measurement). Note that the measurement can change the
|
||||
gain by an arbitrary amount and divide by the gain change (in dB) which
|
||||
is measured at the reference wavelength (the red end of the band).
|
||||
"""
|
||||
|
||||
dgt_96 = np.loadtxt(path + 'DGT_96.txt')
|
||||
|
||||
"""
|
||||
pNFfit3: Cubic polynomial fit coefficients to noise figure in dB
|
||||
averaged across wavelength as a function of gain change from design flat:
|
||||
|
||||
NFavg = pNFfit3(1)*dG^3 + pNFfit3(2)*dG^2 pNFfit3(3)*dG + pNFfit3(4)
|
||||
where
|
||||
dG = GainTarget - average(DFG_96)
|
||||
note that dG will normally be a negative value.
|
||||
"""
|
||||
|
||||
nf_fitco = np.loadtxt(path + 'pNFfit3.txt')
|
||||
|
||||
"""NFR_96: Noise figure ripple in dB away from the average noise figure
|
||||
across the band. This captures the wavelength dependence of the NF. To
|
||||
calculate the NF across channels, one uses the cubic fit coefficients
|
||||
with the external gain target to get the average nosie figure, NFavg and
|
||||
then adds this to NFR_96:
|
||||
NF_96 = NFR_96 + NFavg
|
||||
"""
|
||||
|
||||
nf_ripple = np.loadtxt(path + 'NFR_96.txt')
|
||||
|
||||
# This is an example to set the provisionable gain and gain-tilt values
|
||||
# Tilt is in units of dB/THz
|
||||
gain_target = 20.0
|
||||
tilt_target = -0.7
|
||||
|
||||
# calculate the NF for the EDFA at this gain setting
|
||||
dg = gain_target - avg_dfg
|
||||
nf_avg = polyval(nf_fitco, dg)
|
||||
nf_96 = nf_ripple + nf_avg
|
||||
|
||||
# get the input power profiles to show
|
||||
pch2d = np.loadtxt(path + 'Pchan2D.txt')
|
||||
|
||||
# Load legend and assemble legend text
|
||||
pch2d_legend_data = np.loadtxt(path + 'Pchan2DLegend.txt')
|
||||
pch2d_legend = []
|
||||
for ea in pch2d_legend_data:
|
||||
s = ''.join([chr(xx) for xx in ea.astype(dtype=int)]).strip()
|
||||
pch2d_legend.append(s)
|
||||
|
||||
# assemble plot
|
||||
axis_font = {'fontname': 'Arial', 'size': '16', 'fontweight': 'bold'}
|
||||
title_font = {'fontname': 'Arial', 'size': '17', 'fontweight': 'bold'}
|
||||
tic_font = {'fontname': 'Arial', 'size': '12'}
|
||||
|
||||
plt.rcParams["font.family"] = "Arial"
|
||||
plt.figure()
|
||||
plt.plot(nchan, pch2d.T, '.-', lw=2)
|
||||
plt.xlabel('Channel Number', **axis_font)
|
||||
plt.ylabel('Channel Power [dBm]', **axis_font)
|
||||
plt.title('Input Power Profiles for Different Channel Loading',
|
||||
**title_font)
|
||||
plt.legend(pch2d_legend, loc=5)
|
||||
plt.grid()
|
||||
plt.ylim((-100, -10))
|
||||
plt.xlim((0, 110))
|
||||
plt.xticks(np.arange(0, 100, 10), **tic_font)
|
||||
plt.yticks(np.arange(-110, -10, 10), **tic_font)
|
||||
|
||||
plt.figure()
|
||||
ea = pch2d[1, :]
|
||||
for ea in pch2d:
|
||||
chgain = gain_profile(dfg_96, dgt_96, ea, gain_target, tilt_target)
|
||||
pase = noise_profile(nf_96, chgain, fc, df)
|
||||
pout = lin2db(db2lin(ea + chgain) + db2lin(pase))
|
||||
plt.plot(nchan, pout, '.-', lw=2)
|
||||
plt.title('Output Power with ASE for Different Channel Loading',
|
||||
**title_font)
|
||||
plt.xlabel('Channel Number', **axis_font)
|
||||
plt.ylabel('Channel Power [dBm]', **axis_font)
|
||||
plt.grid()
|
||||
plt.ylim((-50, 10))
|
||||
plt.xlim((0, 100))
|
||||
plt.xticks(np.arange(0, 100, 10), **tic_font)
|
||||
plt.yticks(np.arange(-50, 10, 10), **tic_font)
|
||||
plt.legend(pch2d_legend, loc=5)
|
||||
plt.show()
|
||||
300
examples/edfa_model/amplifier_models_description.rst
Normal file
300
examples/edfa_model/amplifier_models_description.rst
Normal file
@@ -0,0 +1,300 @@
|
||||
*********************************************
|
||||
Amplifier models and configuration
|
||||
*********************************************
|
||||
|
||||
|
||||
1. Equipment configuration description
|
||||
#######################################
|
||||
|
||||
Equipment description defines equipment types and parameters.
|
||||
It takes place in the default **eqpt_config.json** file.
|
||||
By default **transmission_main_example.py** uses **eqpt_config.json** file and that
|
||||
can be changed with **-e** or **--equipment** command line parameter.
|
||||
|
||||
2. Amplifier parameters and subtypes
|
||||
#######################################
|
||||
|
||||
Several amplifiers can be used by GNpy, so they are defined as an array of equipment parameters in **eqpt_config.json** file.
|
||||
|
||||
- *"type_variety"*:
|
||||
Each amplifier is identified by its unique *"type_variety"*, which is used in the topology files input to reference a specific amplifier. It is a user free defined id.
|
||||
|
||||
For each amplifier *type_variety*, specific parameters are describing its attributes and performance:
|
||||
|
||||
- *"type_def"*:
|
||||
Sets the amplifier model that the simulation will use to calculate the ase noise contribution. 5 models are defined with reserved words:
|
||||
|
||||
- *"advanced_model"*
|
||||
- *"variable_gain"*
|
||||
- *"fixed_gain"*
|
||||
- *"dual_stage"*
|
||||
- *"openroadm"*
|
||||
*see next section for a full description of these models*
|
||||
|
||||
- *"advanced_config_from_json"*:
|
||||
**This parameter is only applicable to the _"advanced_model"_ model**
|
||||
|
||||
json file name describing:
|
||||
|
||||
- nf_fit_coeff
|
||||
- f_min/max
|
||||
- gain_ripple
|
||||
- nf_ripple
|
||||
- dgt
|
||||
|
||||
*see next section for a full description*
|
||||
|
||||
- *"gain_flatmax"*:
|
||||
amplifier maximum gain in dB before its extended gain range: flat or nominal tilt output.
|
||||
|
||||
If gain > gain_flatmax, the amplifier will tilt, based on its dgt function
|
||||
|
||||
If gain > gain_flatmax + target_extended_gain, the amplifier output power is reduced to not exceed the extended gain range.
|
||||
|
||||
- *"gain_min"*:
|
||||
amplifier minimum gain in dB.
|
||||
|
||||
If gain < gain_min, the amplifier input is automatically padded, which results in
|
||||
|
||||
NF += gain_min - gain
|
||||
|
||||
- *"p_max"*:
|
||||
amplifier max output power, full load
|
||||
|
||||
Total signal output power will not be allowed beyond this value
|
||||
|
||||
- *"nf_min/max"*:
|
||||
**These parameters are only applicable to the _"variable_gain"_ model**
|
||||
|
||||
min & max NF values in dB
|
||||
|
||||
NF_min is the amplifier NF @ gain_max
|
||||
|
||||
NF_max is the amplifier NF @ gain_min
|
||||
|
||||
- *"nf_coef"*:
|
||||
**This parameter is only applicable to the *"openroadm"* model**
|
||||
|
||||
[a, b, c, d] 3rd order polynomial coefficients list to define the incremental OSNR vs Pin
|
||||
|
||||
Incremental OSNR is the amplifier OSNR contribution
|
||||
|
||||
Pin is the amplifier channel input power defined in a 50GHz bandwidth
|
||||
|
||||
Incremental OSNR = a*Pin³ + b*Pin² + c*Pin + d
|
||||
|
||||
- *"preamp_variety"*:
|
||||
**This parameter is only applicable to the _"dual_stage"_ model**
|
||||
|
||||
1st stage type_variety
|
||||
|
||||
- *"booster_variety"*:
|
||||
**This parameter is only applicable to the *"dual_stage"* model**
|
||||
|
||||
2nd stage type_variety
|
||||
|
||||
- *"out_voa_auto"*: true/false
|
||||
**power_mode only**
|
||||
|
||||
**This parameter is only applicable to the *"advanced_model"* and *"variable_gain"* models**
|
||||
|
||||
If "out_voa_auto": true, auto_design will chose the output_VOA value that maximizes the amplifier gain within its power capability and therefore minimizes its NF.
|
||||
|
||||
- *"allowed_for_design"*: true/false
|
||||
**auto_design only**
|
||||
|
||||
Tells auto_design if this amplifier can be picked for the design (deactivates unwanted amplifiers)
|
||||
|
||||
It does not prevent the use of an amplifier if it is placed in the topology input.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{"Edfa": [{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26,
|
||||
"gain_min": 15,
|
||||
"p_max": 23,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_low_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16,
|
||||
"gain_min": 8,
|
||||
"p_max": 23,
|
||||
"nf_min": 6.5,
|
||||
"nf_max": 11,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
}
|
||||
]}
|
||||
|
||||
|
||||
3. Amplifier models
|
||||
#######################################
|
||||
|
||||
In an opensource and multi-vendor environnement, it is needed to support different use cases and context. Therefore several models are supported for amplifiers.
|
||||
|
||||
5 types of EDFA definition are possible and referenced by the *"type_def"* parameter with the following reserved words:
|
||||
|
||||
- *"advanced_model"*
|
||||
This model is refered as a whitebox model because of the detailed level of knowledge that is required. The amplifier NF model and ripple definition are described by a json file referenced with *"advanced_config_from_json"*: json filename. This json file contains:
|
||||
|
||||
- nf_fit_coeff: [a,b,c,d]
|
||||
|
||||
3rd order polynomial NF = f(-dg) coeficients list
|
||||
|
||||
dg = gain - gain_max
|
||||
|
||||
- f_min/max: amplifier frequency range in Hz
|
||||
- gain_ripple : [...]
|
||||
|
||||
amplifier gain ripple excursion comb list in dB across the frequency range.
|
||||
- nf_ripple : [...]
|
||||
|
||||
amplifier nf ripple excursion comb list in dB across the frequency range.
|
||||
- dgt : [...]
|
||||
amplifier dynamic gain tilt comb list across the frequency range.
|
||||
|
||||
*See next section for the generation of this json file*
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"Edfa":[{
|
||||
"type_variety": "high_detail_model_example",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
]
|
||||
|
||||
- *"variable_gain"*
|
||||
This model is refered as an operator model because a lower level of knowledge is required. A full polynomial description of the NF cross the gain range is not required. Instead, NF_min and NF_max values are required and used by the code to model a dual stage amplifier with an internal mid stage VOA. NF_min and NF_max values are typically available from equipment suppliers data-sheet.
|
||||
|
||||
There is a default JSON file ”default_edfa_config.json”* to enforce 0 tilt and ripple values because GNpy core algorithm is a multi-carrier propogation.
|
||||
- gain_ripple =[0,...,0]
|
||||
- nf_ripple = [0,...,0]
|
||||
- dgt = [...] generic dgt comb
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"Edfa":[{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26,
|
||||
"gain_min": 15,
|
||||
"p_max": 23,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
}
|
||||
]
|
||||
|
||||
- *"fixed_gain"*
|
||||
This model is also an operator model with a single NF value that emulates basic single coil amplifiers without internal VOA.
|
||||
|
||||
if gain_min < gain < gain_max, NF == nf0
|
||||
|
||||
if gain < gain_min, the amplifier input is automatically padded, which results in
|
||||
|
||||
NF += gain_min - gain
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"Edfa":[{
|
||||
"type_variety": "std_fixed_gain",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21,
|
||||
"gain_min": 20,
|
||||
"p_max": 21,
|
||||
"nf0": 5.5,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
]
|
||||
|
||||
- *"openroadm"*
|
||||
This model is a black box model replicating OpenRoadm MSA spec for ILA.
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"Edfa":[{
|
||||
"type_variety": "low_noise",
|
||||
"type_def": "openroadm",
|
||||
"gain_flatmax": 27,
|
||||
"gain_min": 12,
|
||||
"p_max": 22,
|
||||
"nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62],
|
||||
"allowed_for_design": false
|
||||
}
|
||||
]
|
||||
|
||||
- *"dual_stage"*
|
||||
This model allows the cascade (pre-defined combination) of any 2 amplifiers already described in the eqpt_config.json library.
|
||||
|
||||
- preamp_variety defines the 1st stge type variety
|
||||
|
||||
- booster variety defines the 2nd stage type variety
|
||||
|
||||
Both preamp and booster variety must exist in the eqpt libray
|
||||
The resulting NF is the sum of the 2 amplifiers
|
||||
The preamp is operated to its maximum gain
|
||||
|
||||
- gain_min indicates to auto_design when this dual_stage should be used
|
||||
|
||||
But unlike other models the 1st stage input will not be padded: it is always operated to its maximu gain and min NF. Therefore if gain adaptation and padding is needed it will be performed by the 2nd stage.
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"type_variety": "medium+low_gain",
|
||||
"type_def": "dual_stage",
|
||||
"gain_min": 25,
|
||||
"preamp_variety": "std_medium_gain",
|
||||
"booster_variety": "std_low_gain",
|
||||
"allowed_for_design": true
|
||||
}
|
||||
|
||||
4. advanced_config_from_json
|
||||
#######################################
|
||||
|
||||
The build_oa_json.py library in gnpy/examples/edfa_model can be used to build the json file required for the amplifier advanced_model type_def:
|
||||
|
||||
Update an existing json file with all the 96ch txt files for a given amplifier type
|
||||
amplifier type 'OA_type1' is hard coded but can be modified and other types added
|
||||
returns an updated amplifier json file: output_json_file_name = 'edfa_config.json'
|
||||
amplifier file names
|
||||
|
||||
Convert a set of amplifier files + input json definiton file into a valid edfa_json_file:
|
||||
|
||||
nf_fit_coeff: NF 3rd order polynomial coefficients txt file
|
||||
|
||||
nf = f(dg) with dg = gain_operational - gain_max
|
||||
|
||||
nf_ripple: NF ripple excursion txt file
|
||||
|
||||
gain_ripple: gain ripple txt file
|
||||
|
||||
dgt: dynamic gain txt file
|
||||
|
||||
input json file in argument (defult = 'OA.json')
|
||||
|
||||
the json input file should have the following fields:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"nf_fit_coeff": "nf_filename.txt",
|
||||
"nf_ripple": "nf_ripple_filename.txt",
|
||||
"gain_ripple": "DFG_filename.txt",
|
||||
"dgt": "DGT_filename.txt"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
Created on Tue Jan 30 12:32:00 2018
|
||||
|
||||
@author: jeanluc-auge
|
||||
@comments about amplifier input files from Brian Taylor & Dave Boertjes
|
||||
|
||||
update an existing json file with all the 96ch txt files for a given amplifier type
|
||||
amplifier type 'OA_type1' is hard coded but can be modified and other types added
|
||||
@@ -18,46 +17,29 @@ from gnpy.core.utils import lin2db, db2lin
|
||||
|
||||
"""amplifier file names
|
||||
convert a set of amplifier files + input json definiton file into a valid edfa_json_file:
|
||||
nf_fit_coeff: NF polynomial coefficients txt file (optional)
|
||||
nf_fit_coeff: NF 3rd order polynomial coefficients txt file
|
||||
nf = f(dg)
|
||||
with dg = gain_operational - gain_max
|
||||
nf_ripple: NF ripple excursion txt file
|
||||
dfg: gain txt file
|
||||
gain_ripple: gain ripple txt file
|
||||
dgt: dynamic gain txt file
|
||||
input json file in argument (defult = 'OA.json')
|
||||
|
||||
the json input file should have the following fields:
|
||||
{
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"nf_fit_coeff": "pNFfit3.txt",
|
||||
"nf_ripple": "NFR_96.txt",
|
||||
"dfg": "DFG_96.txt",
|
||||
"dgt": "DGT_96.txt",
|
||||
"nf_model":
|
||||
{
|
||||
"enabled": true,
|
||||
"nf_min": 5.8,
|
||||
"nf_max": 10
|
||||
}
|
||||
"nf_fit_coeff": "nf_filename.txt",
|
||||
"nf_ripple": "nf_ripple_filename.txt",
|
||||
"gain_ripple": "DFG_filename.txt",
|
||||
"dgt": "DGT_filename.txt",
|
||||
}
|
||||
gain_flat = max flat gain (dB)
|
||||
gain_min = min gain (dB) : will consider an input VOA if below (TBD vs throwing an exception)
|
||||
p_max = max power (dBm)
|
||||
nf_fit = boolean (True, False) :
|
||||
if False nf_fit_coeff are ignored and nf_model fields are used
|
||||
|
||||
"""
|
||||
|
||||
input_json_file_name = "OA.json" #default path
|
||||
output_json_file_name = "default_edfa_config.json"
|
||||
param_field ="params"
|
||||
gain_min_field = "gain_min"
|
||||
gain_max_field = "gain_flatmax"
|
||||
gain_ripple_field = "dfg"
|
||||
gain_ripple_field = "gain_ripple"
|
||||
nf_ripple_field = "nf_ripple"
|
||||
nf_fit_coeff = "nf_fit_coeff"
|
||||
nf_model_field = "nf_model"
|
||||
nf_model_enabled_field = "enabled"
|
||||
nf_min_field ="nf_min"
|
||||
nf_max_field = "nf_max"
|
||||
|
||||
def read_file(field, file_name):
|
||||
"""read and format the 96 channels txt files describing the amplifier NF and ripple
|
||||
@@ -76,6 +58,7 @@ def read_file(field, file_name):
|
||||
#consider ripple excursion only to avoid redundant information
|
||||
#because the max flat_gain is already given by the 'gain_flat' field in json
|
||||
#remove the mean component
|
||||
print(file_name, ', mean value =', data.mean(), ' is substracted')
|
||||
data = data - data.mean()
|
||||
data = data.tolist()
|
||||
return data
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
{
|
||||
"params": {
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"nf_fit_coeff": [
|
||||
0.000168241,
|
||||
0.0469961,
|
||||
0.0359549,
|
||||
5.82851
|
||||
],
|
||||
"nf_ripple": [
|
||||
-0.3110761646066259,
|
||||
-0.3110761646066259,
|
||||
-0.31110274831665313,
|
||||
-0.31419329378173544,
|
||||
-0.3172854168606314,
|
||||
-0.32037911876162584,
|
||||
-0.3233255190215882,
|
||||
-0.31624321721895354,
|
||||
-0.30915729645781326,
|
||||
-0.30206775396360075,
|
||||
-0.2949045115165272,
|
||||
-0.26632156113294336,
|
||||
-0.23772399031437283,
|
||||
-0.20911178784023846,
|
||||
-0.18048410390821285,
|
||||
-0.14379944379052215,
|
||||
-0.10709599992470213,
|
||||
-0.07037375788020579,
|
||||
-0.03372858157230583,
|
||||
-0.015660302006048,
|
||||
0.0024172385953583004,
|
||||
0.020504047353947653,
|
||||
0.03860013139908377,
|
||||
0.05670549786742816,
|
||||
0.07482015390297145,
|
||||
0.0838762040768461,
|
||||
0.09284481475528361,
|
||||
0.1018180306253394,
|
||||
0.11079585523492333,
|
||||
0.1020395478432815,
|
||||
0.09310160456603413,
|
||||
0.08415906712621996,
|
||||
0.07521193198077789,
|
||||
0.0676340601339394,
|
||||
0.06005437964543287,
|
||||
0.052470799141237305,
|
||||
0.044883315610536455,
|
||||
0.037679759069084225,
|
||||
0.03047647598902483,
|
||||
0.02326948274513522,
|
||||
0.01605877647020772,
|
||||
0.021248462316134083,
|
||||
0.02657315875107553,
|
||||
0.03190060058247842,
|
||||
0.03723078993416436,
|
||||
0.04256372893215024,
|
||||
0.047899419704645264,
|
||||
0.03915515813685565,
|
||||
0.030289222542492025,
|
||||
0.021418708618354456,
|
||||
0.012573926129294415,
|
||||
0.006240488799898697,
|
||||
-9.622162373026585e-05,
|
||||
-0.006436207679519103,
|
||||
-0.012779471908040341,
|
||||
-0.02038153550619876,
|
||||
-0.027999803010447587,
|
||||
-0.035622012697103154,
|
||||
-0.043236398934156144,
|
||||
-0.04493583574805963,
|
||||
-0.04663615264317309,
|
||||
-0.048337350303318156,
|
||||
-0.050039429413028365,
|
||||
-0.051742390657545205,
|
||||
-0.05342028484370278,
|
||||
-0.05254242298580185,
|
||||
-0.05166410580536087,
|
||||
-0.05078533294804249,
|
||||
-0.04990610405914272,
|
||||
-0.05409792133358102,
|
||||
-0.05832916277634124,
|
||||
-0.06256260169582961,
|
||||
-0.06660356886269536,
|
||||
-0.04779792991567815,
|
||||
-0.028982516728038848,
|
||||
-0.010157321677553965,
|
||||
0.00861320615127981,
|
||||
0.01913736978785662,
|
||||
0.029667009055877668,
|
||||
0.04020212822983975,
|
||||
0.050742731588695494,
|
||||
0.061288823415841555,
|
||||
0.07184040799914815,
|
||||
0.1043252636301016,
|
||||
0.13687829834471027,
|
||||
0.1694483010211072,
|
||||
0.202035284929368,
|
||||
0.23624619427167134,
|
||||
0.27048596623174515,
|
||||
0.30474360397422756,
|
||||
0.3390191214858807,
|
||||
0.36358851509924695,
|
||||
0.38814205928193013,
|
||||
0.41270842850729195,
|
||||
0.4372876328262819,
|
||||
0.4372876328262819
|
||||
],
|
||||
"dgt": [
|
||||
2.714526681131686,
|
||||
2.705443819238505,
|
||||
2.6947834587664494,
|
||||
2.6841217449620203,
|
||||
2.6681935771243177,
|
||||
2.6521732021128046,
|
||||
2.630396440815385,
|
||||
2.602860350286428,
|
||||
2.5696460593920065,
|
||||
2.5364027376452056,
|
||||
2.499446286796604,
|
||||
2.4587748041127506,
|
||||
2.414398437185221,
|
||||
2.3699990328716107,
|
||||
2.322373696229342,
|
||||
2.271520771371253,
|
||||
2.2174389328192197,
|
||||
2.16337565384239,
|
||||
2.1183028432496016,
|
||||
2.082225099873648,
|
||||
2.055100772005235,
|
||||
2.0279625371819305,
|
||||
2.0008103857988204,
|
||||
1.9736443063300082,
|
||||
1.9482128147680253,
|
||||
1.9245345552113182,
|
||||
1.9026104247588487,
|
||||
1.8806927939516411,
|
||||
1.862235672444246,
|
||||
1.847275503201129,
|
||||
1.835814081380705,
|
||||
1.824381436842932,
|
||||
1.8139629377087627,
|
||||
1.8045606557581335,
|
||||
1.7961751115773796,
|
||||
1.7877868031023945,
|
||||
1.7793941781790852,
|
||||
1.7709972329654864,
|
||||
1.7625959636196327,
|
||||
1.7541903672600494,
|
||||
1.7459181197626403,
|
||||
1.737780757913635,
|
||||
1.7297783508684146,
|
||||
1.7217732861435076,
|
||||
1.7137640932265894,
|
||||
1.7057507692361864,
|
||||
1.6918150918099673,
|
||||
1.6719047669939942,
|
||||
1.6460167077689267,
|
||||
1.6201194134191075,
|
||||
1.5986915141218316,
|
||||
1.5817353179379183,
|
||||
1.569199764184379,
|
||||
1.5566577309558969,
|
||||
1.545374152761467,
|
||||
1.5353620432989845,
|
||||
1.5266220576235803,
|
||||
1.5178910621476225,
|
||||
1.5097346239790443,
|
||||
1.502153039909686,
|
||||
1.495145456062699,
|
||||
1.488134243479226,
|
||||
1.48111939735681,
|
||||
1.474100442252211,
|
||||
1.4670307626366115,
|
||||
1.4599103316162523,
|
||||
1.45273959485914,
|
||||
1.445565137158368,
|
||||
1.4340878115214444,
|
||||
1.418273806730323,
|
||||
1.3981208704326855,
|
||||
1.3779439775587023,
|
||||
1.3598972673004606,
|
||||
1.3439818461440451,
|
||||
1.3301807335621048,
|
||||
1.316383926863083,
|
||||
1.3040618749785347,
|
||||
1.2932153453410835,
|
||||
1.2838336236692311,
|
||||
1.2744470198196236,
|
||||
1.2650555289898042,
|
||||
1.2556591482982988,
|
||||
1.2428104897182262,
|
||||
1.2264996957264114,
|
||||
1.2067249615595257,
|
||||
1.1869318618366975,
|
||||
1.1672278304018044,
|
||||
1.1476135933863398,
|
||||
1.1280891949729075,
|
||||
1.108555289615659,
|
||||
1.0895983485572227,
|
||||
1.0712204022764056,
|
||||
1.0534217504465226,
|
||||
1.0356155337864215,
|
||||
1.017807767853702,
|
||||
1.0
|
||||
],
|
||||
"nf_model": {
|
||||
"enabled": true,
|
||||
"nf1": 5.727887800964238,
|
||||
"nf2": 7.727887800964238,
|
||||
"delta_p": 5.238350271545567
|
||||
},
|
||||
"gain_ripple": [
|
||||
0.1359703369791596,
|
||||
0.11822862697916037,
|
||||
0.09542181697916163,
|
||||
0.06245819697916133,
|
||||
0.02602813697916062,
|
||||
-0.0036199830208403228,
|
||||
-0.018326963020840026,
|
||||
-0.0246928330208398,
|
||||
-0.016792253020838643,
|
||||
-0.0028138630208403015,
|
||||
0.017572956979162058,
|
||||
0.038328296979159404,
|
||||
0.054956336979159914,
|
||||
0.0670723869791594,
|
||||
0.07091459697916136,
|
||||
0.07094413697916124,
|
||||
0.07114372697916238,
|
||||
0.07533675697916209,
|
||||
0.08731066697916035,
|
||||
0.10313984697916112,
|
||||
0.12276252697916235,
|
||||
0.14239527697916188,
|
||||
0.15945681697916214,
|
||||
0.1739275269791598,
|
||||
0.1767381569791624,
|
||||
0.17037189697916233,
|
||||
0.15216302697916007,
|
||||
0.13114358697916018,
|
||||
0.10802383697916085,
|
||||
0.08548825697916129,
|
||||
0.06916723697916183,
|
||||
0.05848224697916038,
|
||||
0.05447361697916264,
|
||||
0.05154489697916276,
|
||||
0.04946107697915991,
|
||||
0.04717897697916129,
|
||||
0.04551704697916037,
|
||||
0.04467697697916151,
|
||||
0.04072968697916224,
|
||||
0.03285456697916089,
|
||||
0.023488786979161347,
|
||||
0.01659282697915998,
|
||||
0.013321846979160057,
|
||||
0.011234826979162449,
|
||||
0.01030063697916006,
|
||||
0.00936596697916059,
|
||||
0.00874012697916271,
|
||||
0.00842583697916055,
|
||||
0.006965146979162284,
|
||||
0.0040435869791615175,
|
||||
0.0007104669791608842,
|
||||
-0.0015763130208377163,
|
||||
-0.006936193020838033,
|
||||
-0.016475303020840215,
|
||||
-0.028748483020837767,
|
||||
-0.039618433020837784,
|
||||
-0.051112303020840244,
|
||||
-0.06468462302083822,
|
||||
-0.07868024302083754,
|
||||
-0.09101254302083817,
|
||||
-0.10103437302083762,
|
||||
-0.11041488302083735,
|
||||
-0.11916081302083725,
|
||||
-0.12789859302083784,
|
||||
-0.1353792530208402,
|
||||
-0.14160178302083892,
|
||||
-0.1455411330208385,
|
||||
-0.1484450830208388,
|
||||
-0.14823350302084037,
|
||||
-0.14591937302083835,
|
||||
-0.1409032730208395,
|
||||
-0.13525493302083902,
|
||||
-0.1279646530208396,
|
||||
-0.11963431302083904,
|
||||
-0.11089282302084058,
|
||||
-0.1027863830208382,
|
||||
-0.09717347302083823,
|
||||
-0.09343261302083761,
|
||||
-0.0913487130208388,
|
||||
-0.08906007302083907,
|
||||
-0.0865687230208394,
|
||||
-0.08407607302083875,
|
||||
-0.07844600302084004,
|
||||
-0.06968090302083851,
|
||||
-0.05947139302083926,
|
||||
-0.05095282302083959,
|
||||
-0.042428283020839785,
|
||||
-0.03218106302083967,
|
||||
-0.01819858302084043,
|
||||
-0.0021726530208390216,
|
||||
0.01393231697916164,
|
||||
0.028098946979159933,
|
||||
0.040326236979161934,
|
||||
0.05257029697916238,
|
||||
0.06479749697916048,
|
||||
0.07704745697916238
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,22 @@
|
||||
{ "Edfa":[{
|
||||
"type_variety": "high_detail_model_example",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
}, {
|
||||
"type_variety": "Juniper_BoosterHG",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 10,
|
||||
"p_max": 21,
|
||||
"advanced_config_from_json": "Juniper-BoosterHG.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "operator_model_example",
|
||||
"type_def": "variable_gain",
|
||||
@@ -19,6 +29,35 @@
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "low_noise",
|
||||
"type_def": "openroadm",
|
||||
"gain_flatmax": 27,
|
||||
"gain_min": 12,
|
||||
"p_max": 22,
|
||||
"nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62],
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "standard",
|
||||
"type_def": "openroadm",
|
||||
"gain_flatmax": 27,
|
||||
"gain_min": 12,
|
||||
"p_max": 22,
|
||||
"nf_coef": [-5.952e-4,-6.250e-2,-1.071,28.99],
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "std_high_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 35,
|
||||
"gain_min": 25,
|
||||
"p_max": 21,
|
||||
"nf_min": 5.5,
|
||||
"nf_max": 7,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26,
|
||||
@@ -41,6 +80,17 @@
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "high_power",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16,
|
||||
"gain_min": 8,
|
||||
"p_max": 25,
|
||||
"nf_min": 9,
|
||||
"nf_max": 15,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "std_fixed_gain",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21,
|
||||
@@ -50,25 +100,100 @@
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "test",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"type_variety": "4pumps_raman",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 12,
|
||||
"gain_min": 12,
|
||||
"p_max": 21,
|
||||
"nf_min": 7,
|
||||
"nf_max": 11,
|
||||
"nf0": -1,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"type_variety": "hybrid_4pumps_lowgain",
|
||||
"type_def": "dual_stage",
|
||||
"raman": true,
|
||||
"gain_min": 25,
|
||||
"preamp_variety": "4pumps_raman",
|
||||
"booster_variety": "std_low_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "hybrid_4pumps_mediumgain",
|
||||
"type_def": "dual_stage",
|
||||
"raman": true,
|
||||
"gain_min": 25,
|
||||
"preamp_variety": "4pumps_raman",
|
||||
"booster_variety": "std_medium_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "medium+low_gain",
|
||||
"type_def": "dual_stage",
|
||||
"gain_min": 25,
|
||||
"preamp_variety": "std_medium_gain",
|
||||
"booster_variety": "std_low_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "medium+high_power",
|
||||
"type_def": "dual_stage",
|
||||
"gain_min": 25,
|
||||
"preamp_variety": "std_medium_gain",
|
||||
"booster_variety": "high_power",
|
||||
"allowed_for_design": false
|
||||
}
|
||||
],
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127
|
||||
},
|
||||
{
|
||||
"type_variety": "NZDF",
|
||||
"dispersion": 0.5e-05,
|
||||
"gamma": 0.00146
|
||||
},
|
||||
{
|
||||
"type_variety": "LOF",
|
||||
"dispersion": 2.2e-05,
|
||||
"gamma": 0.000843
|
||||
}
|
||||
],
|
||||
"Spans":[{
|
||||
"RamanFiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127,
|
||||
"raman_efficiency": {
|
||||
"cr":[
|
||||
0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119,
|
||||
0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469,
|
||||
0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826,
|
||||
0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05,
|
||||
7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05,
|
||||
2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05,
|
||||
2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05,
|
||||
1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05,
|
||||
1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07,
|
||||
2E-07, 1E-07
|
||||
],
|
||||
"frequency_offset":[
|
||||
0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12,
|
||||
7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12,
|
||||
13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12,
|
||||
17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12,
|
||||
22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12,
|
||||
28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12,
|
||||
35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12,
|
||||
41.5e12, 42e12
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"Span":[{
|
||||
"power_mode":true,
|
||||
"delta_power_range_db": [0,0,0.5],
|
||||
"delta_power_range_db": [-2,3,0.5],
|
||||
"max_fiber_lineic_loss_for_raman": 0.25,
|
||||
"target_extended_gain": 2.5,
|
||||
"max_length": 150,
|
||||
"length_units": "km",
|
||||
"max_loss": 28,
|
||||
@@ -78,9 +203,13 @@
|
||||
"con_out": 0
|
||||
}
|
||||
],
|
||||
"Roadms":[{
|
||||
"gain_mode_default_loss": 20,
|
||||
"power_mode_pref": -20
|
||||
"Roadm":[{
|
||||
"target_pch_out_db": -20,
|
||||
"add_drop_osnr": 38,
|
||||
"restrictions": {
|
||||
"preamp_variety_list":[],
|
||||
"booster_variety_list":[]
|
||||
}
|
||||
}],
|
||||
"SI":[{
|
||||
"f_min": 191.3e12,
|
||||
@@ -88,10 +217,10 @@
|
||||
"f_max":195.1e12,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0,0,0.5],
|
||||
"power_range_db": [0,0,1],
|
||||
"roll_off": 0.15,
|
||||
"OSNR": 11,
|
||||
"bit_rate":100e9
|
||||
"tx_osnr": 40,
|
||||
"sys_margins": 2
|
||||
}],
|
||||
"Transceiver":[
|
||||
{
|
||||
@@ -107,28 +236,20 @@
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 37.5e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "mode 2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 15,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
},
|
||||
{
|
||||
"format": "PS_SP64_1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
},
|
||||
{
|
||||
"format": "mode_2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 15,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -140,44 +261,45 @@
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "16QAM",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 19,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
},
|
||||
{
|
||||
"format": "QPSK",
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 12,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
}
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"type_variety": "Voyager_16QAM",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "16QAM",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 19,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 37.5e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "QPSK",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 12,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
"format": "mode 3",
|
||||
"baud_rate": 44e9,
|
||||
"OSNR": 18,
|
||||
"bit_rate": 300e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 62.5e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "mode 2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 21,
|
||||
"bit_rate": 400e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "mode 4",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 16,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -1,165 +1,268 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": 0,
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Vannes_KBE",
|
||||
"request-id": "0",
|
||||
"source": "trx Lorient_KMA",
|
||||
"destination": "trx Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
"output-power": 0.0012589254117941673,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": 1,
|
||||
"source": "Brest_KLA",
|
||||
"destination": "Vannes_KBE",
|
||||
"request-id": "1",
|
||||
"source": "trx Brest_KLA",
|
||||
"destination": "trx Vannes_KBE",
|
||||
"src-tp-id": "trx Brest_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": 0.0012589254117941673,
|
||||
"path_bandwidth": 200000000000.0
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lannion_CAS",
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Brest_KLA",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 1,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lorient_KMA",
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Lannion_CAS",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 2,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Lorient_KMA",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 3,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Vannes_KBE",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": 3,
|
||||
"source": "Lannion_CAS",
|
||||
"destination": "Rennes_STA",
|
||||
"request-id": "3",
|
||||
"source": "trx Lannion_CAS",
|
||||
"destination": "trx Rennes_STA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Rennes_STA",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 60000000000.0
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": 4,
|
||||
"source": "Rennes_STA",
|
||||
"destination": "Lannion_CAS",
|
||||
"request-id": "4",
|
||||
"source": "trx Rennes_STA",
|
||||
"destination": "trx Lannion_CAS",
|
||||
"src-tp-id": "trx Rennes_STA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 64,
|
||||
"output-power": 0.0019952623149688794
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": 0.0019952623149688794,
|
||||
"path_bandwidth": 150000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "5",
|
||||
"source": "trx Rennes_STA",
|
||||
"destination": "trx Lannion_CAS",
|
||||
"src-tp-id": "trx Rennes_STA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 63,
|
||||
"output-power": 0.0019952623149688794,
|
||||
"path_bandwidth": 20000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "6",
|
||||
"source": "trx Lannion_CAS",
|
||||
"destination": "trx Lorient_KMA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Lorient_KMA",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 76,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "7",
|
||||
"source": "trx Lannion_CAS",
|
||||
"destination": "trx Lorient_KMA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Lorient_KMA",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 76,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 400000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "7b",
|
||||
"source": "trx Lannion_CAS",
|
||||
"destination": "trx Lorient_KMA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Lorient_KMA",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 50,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 400000000000.0
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronisation": [
|
||||
"synchronization": [
|
||||
{
|
||||
"synchonization-id": 0,
|
||||
"synchronization-id": "3",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"relaxable": "false",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
0,
|
||||
0
|
||||
"3",
|
||||
"1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": 3,
|
||||
"synchronization-id": "4",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"relaxable": "false",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
3,
|
||||
4
|
||||
"4",
|
||||
"5"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -18,148 +18,552 @@ from pathlib import Path
|
||||
from collections import namedtuple
|
||||
from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO
|
||||
from json import dumps, loads
|
||||
from networkx import (draw_networkx_nodes, draw_networkx_edges,
|
||||
draw_networkx_labels, dijkstra_path, NetworkXNoPath)
|
||||
from numpy import mean
|
||||
from examples.convert_service_sheet import convert_service_sheet, Request_element, Element
|
||||
from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element
|
||||
from gnpy.core.utils import load_json
|
||||
from gnpy.core.network import load_network, build_network, set_roadm_loss
|
||||
from gnpy.core.equipment import load_equipment, trx_mode_params
|
||||
from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused
|
||||
from gnpy.core.network import load_network, build_network, save_network, network_from_json
|
||||
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch
|
||||
from gnpy.core.elements import Transceiver, Roadm
|
||||
from gnpy.core.utils import db2lin, lin2db
|
||||
from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate, jsontocsv
|
||||
from gnpy.core.request import (Path_request, Result_element,
|
||||
propagate, jsontocsv, Disjunction, compute_path_dsjctn,
|
||||
requests_aggregation, propagate_and_optimize_mode,
|
||||
BLOCKING_NOPATH, BLOCKING_NOMODE,
|
||||
find_reversed_path)
|
||||
from gnpy.core.exceptions import (ConfigurationError, EquipmentConfigError, NetworkTopologyError,
|
||||
ServiceError, DisjunctionError)
|
||||
import gnpy.core.ansi_escapes as ansi_escapes
|
||||
from gnpy.core.spectrum_assignment import (build_oms_list, pth_assign_spectrum)
|
||||
from copy import copy, deepcopy
|
||||
from textwrap import dedent
|
||||
from math import ceil
|
||||
|
||||
from flask import Flask, jsonify, make_response, request
|
||||
from flask_restful import Api, Resource, reqparse, fields
|
||||
|
||||
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
|
||||
|
||||
logger = getLogger(__name__)
|
||||
LOGGER = getLogger(__name__)
|
||||
|
||||
parser = ArgumentParser(description = 'A function that computes performances for a list of services provided in a json file or an excel sheet.')
|
||||
parser.add_argument('network_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls')
|
||||
parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls')
|
||||
parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json')
|
||||
parser.add_argument('-v', '--verbose', action='count')
|
||||
parser.add_argument('-o', '--output', default=None)
|
||||
PARSER = ArgumentParser(description='A function that computes performances for a list of ' +
|
||||
'services provided in a json file or an excel sheet.')
|
||||
PARSER.add_argument('network_filename', nargs='?', type=Path,\
|
||||
default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\
|
||||
help='input topology file in xls or json')
|
||||
PARSER.add_argument('service_filename', nargs='?', type=Path,\
|
||||
default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\
|
||||
help='input service file in xls or json')
|
||||
PARSER.add_argument('eqpt_filename', nargs='?', type=Path,\
|
||||
default=Path(__file__).parent / 'eqpt_config.json',\
|
||||
help='input equipment library in json. Default is eqpt_config.json')
|
||||
PARSER.add_argument('-bi', '--bidir', action='store_true',\
|
||||
help='considers that all demands are bidir')
|
||||
PARSER.add_argument('-v', '--verbose', action='count', default=0,\
|
||||
help='increases verbosity for each occurence')
|
||||
PARSER.add_argument('-o', '--output', type=Path)
|
||||
PARSER.add_argument('-r', '--rest', action='count', default=0, help='use the REST API')
|
||||
|
||||
NETWORK_FILENAME = 'topoDemov1.json' #'disagregatedTopoDemov1.json' #
|
||||
|
||||
def requests_from_json(json_data,equipment):
|
||||
APP = Flask(__name__, static_url_path="")
|
||||
API = Api(APP)
|
||||
|
||||
def requests_from_json(json_data, equipment):
|
||||
""" converts the json data into a list of requests elements
|
||||
"""
|
||||
requests_list = []
|
||||
|
||||
for req in json_data['path-request']:
|
||||
#print(f'{req}')
|
||||
# init all params from request
|
||||
params = {}
|
||||
params['request_id'] = req['request-id']
|
||||
params['source'] = req['src-tp-id']
|
||||
params['destination'] = req['dst-tp-id']
|
||||
params['source'] = req['source']
|
||||
params['bidir'] = req['bidirectional']
|
||||
params['destination'] = req['destination']
|
||||
params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type']
|
||||
params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode']
|
||||
params['format'] = params['trx_mode']
|
||||
nd_list = req['optimizations']['explicit-route-include-objects']
|
||||
params['nodes_list'] = [n['unnumbered-hop']['node-id'] for n in nd_list]
|
||||
params['loose_list'] = [n['unnumbered-hop']['hop-type'] for n in nd_list]
|
||||
params['spacing'] = req['path-constraints']['te-bandwidth']['spacing']
|
||||
|
||||
trx_params = trx_mode_params(equipment,params['trx_type'],params['trx_mode'],True)
|
||||
try:
|
||||
nd_list = req['explicit-route-objects']['route-object-include-exclude']
|
||||
except KeyError:
|
||||
nd_list = []
|
||||
params['nodes_list'] = [n['num-unnum-hop']['node-id'] for n in nd_list]
|
||||
params['loose_list'] = [n['num-unnum-hop']['hop-type'] for n in nd_list]
|
||||
# recover trx physical param (baudrate, ...) from type and mode
|
||||
# in trx_mode_params optical power is read from equipment['SI']['default'] and
|
||||
# nb_channel is computed based on min max frequency and spacing
|
||||
trx_params = trx_mode_params(equipment, params['trx_type'], params['trx_mode'], True)
|
||||
params.update(trx_params)
|
||||
params['power'] = req['path-constraints']['te-bandwidth']['output-power']
|
||||
params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel']
|
||||
# print(trx_params['min_spacing'])
|
||||
# optical power might be set differently in the request. if it is indicated then the
|
||||
# params['power'] is updated
|
||||
try:
|
||||
if req['path-constraints']['te-bandwidth']['output-power']:
|
||||
params['power'] = req['path-constraints']['te-bandwidth']['output-power']
|
||||
except KeyError:
|
||||
pass
|
||||
# same process for nb-channel
|
||||
f_min = params['f_min']
|
||||
f_max_from_si = params['f_max']
|
||||
try:
|
||||
if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None:
|
||||
nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel']
|
||||
params['nb_channel'] = nch
|
||||
spacing = params['spacing']
|
||||
params['f_max'] = f_min + nch*spacing
|
||||
else:
|
||||
params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
|
||||
except KeyError:
|
||||
params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
|
||||
consistency_check(params, f_max_from_si)
|
||||
|
||||
try:
|
||||
params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth']
|
||||
except KeyError:
|
||||
pass
|
||||
requests_list.append(Path_request(**params))
|
||||
|
||||
return requests_list
|
||||
|
||||
def consistency_check(params, f_max_from_si):
|
||||
""" checks that the requested parameters are consistant (spacing vs nb channel,
|
||||
vs transponder mode...)
|
||||
"""
|
||||
f_min = params['f_min']
|
||||
f_max = params['f_max']
|
||||
max_recommanded_nb_channels = automatic_nch(f_min, f_max, params['spacing'])
|
||||
if params['baud_rate'] is not None:
|
||||
#implicitely means that a mode is defined with min_spacing
|
||||
if params['min_spacing'] > params['spacing']:
|
||||
msg = f'Request {params["request_id"]} has spacing below transponder ' +\
|
||||
f'{params["trx_type"]} {params["trx_mode"]} min spacing value ' +\
|
||||
f'{params["min_spacing"]*1e-9}GHz.\nComputation stopped'
|
||||
print(msg)
|
||||
LOGGER.critical(msg)
|
||||
raise ServiceError(msg)
|
||||
if f_max > f_max_from_si:
|
||||
msg = dedent(f'''
|
||||
Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz
|
||||
is not consistent with frequency range {f_min*1e-12} THz, {f_max*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz.
|
||||
max recommanded nb of channels is {max_recommanded_nb_channels}
|
||||
Computation stopped.''')
|
||||
LOGGER.critical(msg)
|
||||
raise ServiceError(msg)
|
||||
|
||||
def load_requests(filename,eqpt_filename):
|
||||
|
||||
def disjunctions_from_json(json_data):
|
||||
""" reads the disjunction requests from the json dict and create the list
|
||||
of requested disjunctions for this set of requests
|
||||
"""
|
||||
disjunctions_list = []
|
||||
try:
|
||||
temp_test = json_data['synchronization']
|
||||
except KeyError:
|
||||
temp_test = []
|
||||
if temp_test:
|
||||
for snc in json_data['synchronization']:
|
||||
params = {}
|
||||
params['disjunction_id'] = snc['synchronization-id']
|
||||
params['relaxable'] = snc['svec']['relaxable']
|
||||
params['link_diverse'] = 'link' in snc['svec']['disjointness']
|
||||
params['node_diverse'] = 'node' in snc['svec']['disjointness']
|
||||
params['disjunctions_req'] = snc['svec']['request-id-number']
|
||||
disjunctions_list.append(Disjunction(**params))
|
||||
|
||||
return disjunctions_list
|
||||
|
||||
|
||||
def load_requests(filename, eqpt_filename, bidir):
|
||||
""" loads the requests from a json or an excel file into a data string
|
||||
"""
|
||||
if filename.suffix.lower() == '.xls':
|
||||
logger.info('Automatically converting requests from XLS to JSON')
|
||||
json_data = convert_service_sheet(filename,eqpt_filename)
|
||||
LOGGER.info('Automatically converting requests from XLS to JSON')
|
||||
try:
|
||||
json_data = convert_service_sheet(filename, eqpt_filename, bidir=bidir)
|
||||
except ServiceError as this_e:
|
||||
print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
|
||||
exit(1)
|
||||
else:
|
||||
with open(filename) as f:
|
||||
json_data = loads(f.read())
|
||||
with open(filename, encoding='utf-8') as my_f:
|
||||
json_data = loads(my_f.read())
|
||||
return json_data
|
||||
|
||||
def compute_path(network, equipment, pathreqlist):
|
||||
|
||||
def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist):
|
||||
""" use a list but a dictionnary might be helpful to find path based on request_id
|
||||
TODO change all these req, dsjct, res lists into dict !
|
||||
"""
|
||||
path_res_list = []
|
||||
reversed_path_res_list = []
|
||||
propagated_reversed_path_res_list = []
|
||||
|
||||
for pathreq in pathreqlist:
|
||||
#need to rebuid the network for each path because the total power
|
||||
#can be different and the choice of amplifiers in autodesign is power dependant
|
||||
#but the design is the same if the total power is the same
|
||||
#TODO parametrize the total spectrum power so the same design can be shared
|
||||
for i, pathreq in enumerate(pathreqlist):
|
||||
|
||||
# use the power specified in requests but might be different from the one
|
||||
# specified for design the power is an optional parameter for requests
|
||||
# definition if optional, use the one defines in eqt_config.json
|
||||
p_db = lin2db(pathreq.power*1e3)
|
||||
p_total_db = p_db + lin2db(pathreq.nb_channel)
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
pathreq.nodes_list.append(pathreq.destination)
|
||||
#we assume that the destination is a strict constraint
|
||||
pathreq.loose_list.append('strict')
|
||||
print(f'request {pathreq.request_id}')
|
||||
print(f'Computing path from {pathreq.source} to {pathreq.destination}')
|
||||
print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output
|
||||
total_path = compute_constrained_path(network, pathreq)
|
||||
print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}\n')
|
||||
# adding first node to be clearer on the output
|
||||
print(f'with path constraint: {[pathreq.source] + pathreq.nodes_list}')
|
||||
|
||||
# pathlist[i] contains the whole path information for request i
|
||||
# last element is a transciver and where the result of the propagation is
|
||||
# recorded.
|
||||
# Important Note: since transceivers attached to roadms are actually logical
|
||||
# elements to simulate performance, several demands having the same destination
|
||||
# may use the same transponder for the performance simulation. This is why
|
||||
# we use deepcopy: to ensure that each propagation is recorded and not overwritten
|
||||
total_path = deepcopy(pathlist[i])
|
||||
print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}')
|
||||
# for debug
|
||||
# print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}')
|
||||
if total_path :
|
||||
total_path = propagate(total_path,pathreq,equipment, show=False)
|
||||
else:
|
||||
total_path = []
|
||||
# we record the last tranceiver object in order to have th whole
|
||||
# information about spectrum. Important Note: since transceivers
|
||||
# attached to roadms are actually logical elements to simulate
|
||||
# performance, several demands having the same destination may use
|
||||
# the same transponder for the performance simaulation. This is why
|
||||
# we use deepcopy: to ensure each propagation is recorded and not
|
||||
# overwritten
|
||||
if total_path:
|
||||
if pathreq.baud_rate is not None:
|
||||
# means that at this point the mode was entered/forced by user and thus a
|
||||
# baud_rate was defined
|
||||
total_path = propagate(total_path, pathreq, equipment)
|
||||
temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2)
|
||||
if temp_snr01nm < pathreq.OSNR:
|
||||
msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
|
||||
f' {pathreq.source} to {pathreq.destination} does not pass with' +\
|
||||
f' {pathreq.tsp_mode}\n\tcomputedSNR in 0.1nm = {temp_snr01nm} ' +\
|
||||
f'- required osnr {pathreq.OSNR}'
|
||||
print(msg)
|
||||
LOGGER.warning(msg)
|
||||
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
|
||||
else:
|
||||
total_path, mode = propagate_and_optimize_mode(total_path, pathreq, equipment)
|
||||
# if no baudrate satisfies spacing, no mode is returned and the last explored mode
|
||||
# a warning is shown in the propagate_and_optimize_mode
|
||||
# propagate_and_optimize_mode function returns the mode with the highest bitrate
|
||||
# that passes. if no mode passes, then a attribute blocking_reason is added on
|
||||
# pathreq that contains the reason for blocking: 'NO_PATH', 'NO_FEASIBLE_MODE', ...
|
||||
try:
|
||||
if pathreq.blocking_reason in BLOCKING_NOPATH:
|
||||
total_path = []
|
||||
elif pathreq.blocking_reason in BLOCKING_NOMODE:
|
||||
pathreq.baud_rate = mode['baud_rate']
|
||||
pathreq.tsp_mode = mode['format']
|
||||
pathreq.format = mode['format']
|
||||
pathreq.OSNR = mode['OSNR']
|
||||
pathreq.tx_osnr = mode['tx_osnr']
|
||||
pathreq.bit_rate = mode['bit_rate']
|
||||
# other blocking reason should not appear at this point
|
||||
except AttributeError:
|
||||
pathreq.baud_rate = mode['baud_rate']
|
||||
pathreq.tsp_mode = mode['format']
|
||||
pathreq.format = mode['format']
|
||||
pathreq.OSNR = mode['OSNR']
|
||||
pathreq.tx_osnr = mode['tx_osnr']
|
||||
pathreq.bit_rate = mode['bit_rate']
|
||||
|
||||
# reversed path is needed for correct spectrum assignment
|
||||
reversed_path = find_reversed_path(pathlist[i])
|
||||
if pathreq.bidir:
|
||||
# only propagate if bidir is true, but needs the reversed path anyway for
|
||||
# correct spectrum assignment
|
||||
rev_p = deepcopy(reversed_path)
|
||||
|
||||
print(f'\n\tPropagating Z to A direction {pathreq.destination} to {pathreq.source}')
|
||||
print(f'\tPath (roadsm) {[r.uid for r in rev_p if isinstance(r,Roadm)]}\n')
|
||||
propagated_reversed_path = propagate(rev_p, pathreq, equipment)
|
||||
temp_snr01nm = round(mean(propagated_reversed_path[-1].snr +\
|
||||
lin2db(pathreq.baud_rate/(12.5e9))), 2)
|
||||
if temp_snr01nm < pathreq.OSNR:
|
||||
msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
|
||||
f' {pathreq.source} to {pathreq.destination} does not pass with' +\
|
||||
f' {pathreq.tsp_mode}\n' +\
|
||||
f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}'
|
||||
print(msg)
|
||||
LOGGER.warning(msg)
|
||||
# TODO selection of mode should also be on reversed direction !!
|
||||
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
|
||||
else:
|
||||
propagated_reversed_path = []
|
||||
else:
|
||||
msg = 'Total path is empty. No propagation'
|
||||
print(msg)
|
||||
LOGGER.info(msg)
|
||||
reversed_path = []
|
||||
propagated_reversed_path = []
|
||||
|
||||
path_res_list.append(total_path)
|
||||
reversed_path_res_list.append(reversed_path)
|
||||
propagated_reversed_path_res_list.append(propagated_reversed_path)
|
||||
# print to have a nice output
|
||||
print('')
|
||||
return path_res_list, reversed_path_res_list, propagated_reversed_path_res_list
|
||||
|
||||
def correct_route_list(network, pathreqlist):
|
||||
""" prepares the format of route list of nodes to be consistant
|
||||
remove wrong names, remove endpoints
|
||||
also correct source and destination
|
||||
"""
|
||||
anytype = [n.uid for n in network.nodes()]
|
||||
# TODO there is a problem of identification of fibers in case of parallel fibers
|
||||
# between two adjacent roadms so fiber constraint is not supported
|
||||
transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)]
|
||||
for pathreq in pathreqlist:
|
||||
for i, n_id in enumerate(pathreq.nodes_list):
|
||||
# replace possibly wrong name with a formated roadm name
|
||||
# print(n_id)
|
||||
if n_id not in anytype:
|
||||
# find nodes name that include constraint among all possible names except
|
||||
# transponders (not yet supported as constraints).
|
||||
nodes_suggestion = [uid for uid in anytype \
|
||||
if n_id.lower() in uid.lower() and uid not in transponders]
|
||||
if pathreq.loose_list[i] == 'LOOSE':
|
||||
if len(nodes_suggestion) > 0:
|
||||
new_n = nodes_suggestion[0]
|
||||
print(f'invalid route node specified:\
|
||||
\n\'{n_id}\', replaced with \'{new_n}\'')
|
||||
pathreq.nodes_list[i] = new_n
|
||||
else:
|
||||
print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\',' +\
|
||||
f' could not use it as constraint, skipped!'+'\x1b[0m')
|
||||
pathreq.nodes_list.remove(n_id)
|
||||
pathreq.loose_list.pop(i)
|
||||
else:
|
||||
msg = f'\x1b[1;33;40m'+f'could not find node: {n_id} in network topology.' +\
|
||||
f' Strict constraint can not be applied.' + '\x1b[0m'
|
||||
LOGGER.critical(msg)
|
||||
raise ValueError(msg)
|
||||
if pathreq.source not in transponders:
|
||||
msg = f'\x1b[1;31;40m' + f'Request: {pathreq.request_id}: could not find' +\
|
||||
f' transponder source: {pathreq.source}.'+'\x1b[0m'
|
||||
LOGGER.critical(msg)
|
||||
print(f'{msg}\nComputation stopped.')
|
||||
raise ServiceError(msg)
|
||||
|
||||
if pathreq.destination not in transponders:
|
||||
msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find' +\
|
||||
f' transponder destination: {pathreq.destination}.'+'\x1b[0m'
|
||||
LOGGER.critical(msg)
|
||||
print(f'{msg}\nComputation stopped.')
|
||||
raise ServiceError(msg)
|
||||
|
||||
# TODO remove endpoints from this list in case they were added by the user
|
||||
# in the xls or json files
|
||||
return pathreqlist
|
||||
|
||||
def correct_disjn(disjn):
|
||||
""" clean disjunctions to remove possible repetition
|
||||
"""
|
||||
local_disjn = disjn.copy()
|
||||
for elem in local_disjn:
|
||||
for dis_elem in local_disjn:
|
||||
if set(elem.disjunctions_req) == set(dis_elem.disjunctions_req) and\
|
||||
elem.disjunction_id != dis_elem.disjunction_id:
|
||||
local_disjn.remove(dis_elem)
|
||||
return local_disjn
|
||||
|
||||
path_res_list.append(deepcopy(total_path))
|
||||
return path_res_list
|
||||
|
||||
def path_result_json(pathresult):
|
||||
""" create the response dictionnary
|
||||
"""
|
||||
data = {
|
||||
'path': [n.json for n in pathresult]
|
||||
'response': [n.json for n in pathresult]
|
||||
}
|
||||
return data
|
||||
|
||||
def compute_requests(network, data, equipment):
|
||||
""" Main program calling functions
|
||||
"""
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL))
|
||||
logger.info(f'Computing path requests {args.service_filename} into JSON format')
|
||||
# for debug
|
||||
# print( args.eqpt_filename)
|
||||
data = load_requests(args.service_filename,args.eqpt_filename)
|
||||
equipment = load_equipment(args.eqpt_filename)
|
||||
network = load_network(args.network_filename,equipment)
|
||||
pths = requests_from_json(data, equipment)
|
||||
print(pths)
|
||||
test = compute_path(network, equipment, pths)
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
|
||||
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
save_network(ARGS.network_filename, network)
|
||||
|
||||
#TODO write results
|
||||
oms_list = build_oms_list(network, equipment)
|
||||
|
||||
header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR']
|
||||
try:
|
||||
rqs = requests_from_json(data, equipment)
|
||||
except ServiceError as this_e:
|
||||
print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
|
||||
raise this_e
|
||||
# check that request ids are unique. Non unique ids, may
|
||||
# mess the computation: better to stop the computation
|
||||
all_ids = [r.request_id for r in rqs]
|
||||
if len(all_ids) != len(set(all_ids)):
|
||||
for item in list(set(all_ids)):
|
||||
all_ids.remove(item)
|
||||
msg = f'Requests id {all_ids} are not unique'
|
||||
LOGGER.critical(msg)
|
||||
raise ServiceError(msg)
|
||||
try:
|
||||
rqs = correct_route_list(network, rqs)
|
||||
except ServiceError as this_e:
|
||||
print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
|
||||
raise this_e
|
||||
#exit(1)
|
||||
# pths = compute_path(network, equipment, rqs)
|
||||
dsjn = disjunctions_from_json(data)
|
||||
|
||||
print('\x1b[1;34;40m' + f'List of disjunctions' + '\x1b[0m')
|
||||
print(dsjn)
|
||||
# need to warn or correct in case of wrong disjunction form
|
||||
# disjunction must not be repeated with same or different ids
|
||||
dsjn = correct_disjn(dsjn)
|
||||
|
||||
# Aggregate demands with same exact constraints
|
||||
print('\x1b[1;34;40m' + f'Aggregating similar requests' + '\x1b[0m')
|
||||
|
||||
rqs, dsjn = requests_aggregation(rqs, dsjn)
|
||||
# TODO export novel set of aggregated demands in a json file
|
||||
|
||||
print('\x1b[1;34;40m' + 'The following services have been requested:' + '\x1b[0m')
|
||||
print(rqs)
|
||||
|
||||
print('\x1b[1;34;40m' + f'Computing all paths with constraints' + '\x1b[0m')
|
||||
try:
|
||||
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
|
||||
except DisjunctionError as this_e:
|
||||
print(f'{ansi_escapes.red}Disjunction error:{ansi_escapes.reset} {this_e}')
|
||||
raise this_e
|
||||
|
||||
print('\x1b[1;34;40m' + f'Propagating on selected path' + '\x1b[0m')
|
||||
propagatedpths, reversed_pths, reversed_propagatedpths = \
|
||||
compute_path_with_disjunction(network, equipment, rqs, pths)
|
||||
# Note that deepcopy used in compute_path_with_disjunction returns
|
||||
# a list of nodes which are not belonging to network (they are copies of the node objects).
|
||||
# so there can not be propagation on these nodes.
|
||||
|
||||
pth_assign_spectrum(pths, rqs, oms_list, reversed_pths)
|
||||
|
||||
print('\x1b[1;34;40m'+f'Result summary'+ '\x1b[0m')
|
||||
header = ['req id', ' demand', ' snr@bandwidth A-Z (Z-A)', ' snr@0.1nm A-Z (Z-A)',\
|
||||
' Receiver minOSNR', ' mode', ' Gbit/s', ' nb of tsp pairs',\
|
||||
'N,M or blocking reason']
|
||||
data = []
|
||||
data.append(header)
|
||||
for i, p in enumerate(test):
|
||||
if p:
|
||||
line = [f'{pths[i].source} to {pths[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\
|
||||
f'{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}',\
|
||||
f'{pths[i].OSNR}']
|
||||
else:
|
||||
line = [f'no path from {pths[i].source} to {pths[i].destination} ']
|
||||
for i, this_p in enumerate(propagatedpths):
|
||||
rev_pth = reversed_propagatedpths[i]
|
||||
if rev_pth and this_p:
|
||||
psnrb = f'{round(mean(this_p[-1].snr),2)} ({round(mean(rev_pth[-1].snr),2)})'
|
||||
psnr = f'{round(mean(this_p[-1].snr_01nm), 2)}' +\
|
||||
f' ({round(mean(rev_pth[-1].snr_01nm),2)})'
|
||||
elif this_p:
|
||||
psnrb = f'{round(mean(this_p[-1].snr),2)}'
|
||||
psnr = f'{round(mean(this_p[-1].snr_01nm),2)}'
|
||||
|
||||
try :
|
||||
if rqs[i].blocking_reason in BLOCKING_NOPATH:
|
||||
line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} :',\
|
||||
f'-', f'-', f'-', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9,2)}',\
|
||||
f'-', f'{rqs[i].blocking_reason}']
|
||||
else:
|
||||
line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', psnrb,\
|
||||
psnr, f'-', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9, 2)}',\
|
||||
f'-', f'{rqs[i].blocking_reason}']
|
||||
except AttributeError:
|
||||
line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', psnrb,\
|
||||
psnr, f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9,2)}',\
|
||||
f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }', f'({rqs[i].N},{rqs[i].M})']
|
||||
data.append(line)
|
||||
|
||||
col_width = max(len(word) for row in data for word in row) # padding
|
||||
col_width = max(len(word) for row in data for word in row[2:]) # padding
|
||||
firstcol_width = max(len(row[0]) for row in data) # padding
|
||||
secondcol_width = max(len(row[1]) for row in data) # padding
|
||||
for row in data:
|
||||
print(''.join(word.ljust(col_width) for word in row))
|
||||
firstcol = ''.join(row[0].ljust(firstcol_width))
|
||||
secondcol = ''.join(row[1].ljust(secondcol_width))
|
||||
remainingcols = ''.join(word.center(col_width, ' ') for word in row[2:])
|
||||
print(f'{firstcol} {secondcol} {remainingcols}')
|
||||
print('\x1b[1;33;40m'+f'Result summary shows mean SNR and OSNR (average over all channels)' +\
|
||||
'\x1b[0m')
|
||||
|
||||
return propagatedpths, reversed_propagatedpths, rqs
|
||||
|
||||
|
||||
|
||||
if args.output :
|
||||
def launch_cli(network, data, equipment):
|
||||
""" Compute requests using network, data and equipment with client line interface
|
||||
"""
|
||||
propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment)
|
||||
#Generate the output
|
||||
if ARGS.output :
|
||||
result = []
|
||||
for p in test:
|
||||
result.append(Result_element(pths[test.index(p)],p))
|
||||
with open(args.output, 'w') as f:
|
||||
f.write(dumps(path_result_json(result), indent=2))
|
||||
fnamecsv = next(s for s in args.output.split('.')) + '.csv'
|
||||
with open(fnamecsv,"w") as fcsv :
|
||||
jsontocsv(path_result_json(result),equipment,fcsv)
|
||||
# assumes that list of rqs and list of propgatedpths have same order
|
||||
for i, pth in enumerate(propagatedpths):
|
||||
result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i]))
|
||||
temp = path_result_json(result)
|
||||
fnamecsv = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.csv'
|
||||
fnamejson = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.json'
|
||||
with open(fnamejson, 'w', encoding='utf-8') as fjson:
|
||||
fjson.write(dumps(path_result_json(result), indent=2, ensure_ascii=False))
|
||||
with open(fnamecsv, "w", encoding='utf-8') as fcsv:
|
||||
jsontocsv(temp, equipment, fcsv)
|
||||
print('\x1b[1;34;40m'+f'saving in {ARGS.output} and {fnamecsv}'+ '\x1b[0m')
|
||||
|
||||
class GnpyAPI(Resource):
|
||||
""" Compute requests using network, data and equipment with rest api
|
||||
"""
|
||||
def get(self):
|
||||
return {"ping": True}, 200
|
||||
|
||||
def post(self):
|
||||
data = request.get_json()
|
||||
equipment = load_equipment('examples/2019-demo-equipment.json')
|
||||
topo_json = load_json('examples/2019-demo-topology.json')
|
||||
network = network_from_json(topo_json, equipment)
|
||||
try:
|
||||
propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment)
|
||||
# Generate the output
|
||||
result = []
|
||||
#assumes that list of rqs and list of propgatedpths have same order
|
||||
for i, pth in enumerate(propagatedpths):
|
||||
result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i]))
|
||||
|
||||
return {"result":path_result_json(result)}, 201
|
||||
except ServiceError as this_e:
|
||||
msg = f'Service error: {this_e}'
|
||||
return {"result": msg}, 400
|
||||
|
||||
API.add_resource(GnpyAPI, '/gnpy-experimental')
|
||||
|
||||
def main(args):
|
||||
""" main function that calls all functions
|
||||
"""
|
||||
LOGGER.info(f'Computing path requests {args.service_filename} into JSON format')
|
||||
print('\x1b[1;34;40m' +\
|
||||
f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m')
|
||||
# for debug
|
||||
# print( args.eqpt_filename)
|
||||
|
||||
try:
|
||||
data = load_requests(args.service_filename, args.eqpt_filename, args.bidir)
|
||||
equipment = load_equipment(args.eqpt_filename)
|
||||
network = load_network(args.network_filename, equipment)
|
||||
except EquipmentConfigError as this_e:
|
||||
print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {this_e}')
|
||||
exit(1)
|
||||
except NetworkTopologyError as this_e:
|
||||
print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {this_e}')
|
||||
exit(1)
|
||||
except ConfigurationError as this_e:
|
||||
print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {this_e}')
|
||||
exit(1)
|
||||
except ServiceError as this_e:
|
||||
print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
|
||||
exit(1)
|
||||
# input_str = raw_input("How will you use your program: c:[cli] , a:[api] ?")
|
||||
# print(input_str)
|
||||
#
|
||||
if ((args.rest == 1) and (args.output is None)):
|
||||
print('you have chosen the rest mode')
|
||||
APP.run(host='0.0.0.0', port=5000, debug=True)
|
||||
elif ((args.rest > 1) or ((args.rest == 1) and (args.output is not None))):
|
||||
print('command is not well formulated')
|
||||
else:
|
||||
launch_cli(network, data, equipment)
|
||||
|
||||
if __name__ == '__main__':
|
||||
ARGS = PARSER.parse_args()
|
||||
basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(ARGS.verbose, DEBUG))
|
||||
main(ARGS)
|
||||
|
||||
98
examples/raman_edfa_example_network.json
Normal file
98
examples/raman_edfa_example_network.json
Normal file
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "Site_A",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "Site A",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span1",
|
||||
"type": "RamanFiber",
|
||||
"type_variety": "SSMF",
|
||||
"operational": {
|
||||
"temperature": 283,
|
||||
"raman_pumps": [
|
||||
{
|
||||
"power": 200e-3,
|
||||
"frequency": 205e12,
|
||||
"propagation_direction": "counterprop"
|
||||
},
|
||||
{
|
||||
"power": 206e-3,
|
||||
"frequency": 201e12,
|
||||
"propagation_direction": "counterprop"
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 15.0,
|
||||
"delta_p": -2,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Site_B",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2,
|
||||
"longitude": 0,
|
||||
"city": "Site B",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "Site_A",
|
||||
"to_node": "Span1"
|
||||
},
|
||||
{
|
||||
"from_node": "Span1",
|
||||
"to_node": "Edfa1"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa1",
|
||||
"to_node": "Site_B"
|
||||
}
|
||||
]
|
||||
}
|
||||
180
examples/serviceDemov1.json
Normal file
180
examples/serviceDemov1.json
Normal file
@@ -0,0 +1,180 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "trx site_a",
|
||||
"destination": "trx site_b",
|
||||
"src-tp-id": "trx site_a",
|
||||
"dst-tp-id": "trx site_b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "trx site_a",
|
||||
"destination": "trx site_b",
|
||||
"src-tp-id": "trx site_a",
|
||||
"dst-tp-id": "trx site_b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 200000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Span1ab",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "STRICT"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "2",
|
||||
"source": "trx site_a",
|
||||
"destination": "trx site_b",
|
||||
"src-tp-id": "trx site_a",
|
||||
"dst-tp-id": "trx site_b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 200000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm site_c",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "STRICT"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "trx site_a",
|
||||
"destination": "trx site_b",
|
||||
"src-tp-id": "trx site_a",
|
||||
"dst-tp-id": "trx site_b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "4",
|
||||
"source": "trx site_a",
|
||||
"destination": "trx site_b",
|
||||
"src-tp-id": "trx site_a",
|
||||
"dst-tp-id": "trx site_b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronization": [
|
||||
{
|
||||
"synchronization-id": "x",
|
||||
"svec": {
|
||||
"relaxable": "false",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"3",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchronization-id": "y",
|
||||
"svec": {
|
||||
"relaxable": "false",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"4",
|
||||
"3",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
14
examples/sim_params.json
Normal file
14
examples/sim_params.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"raman_computed_channels": [1, 18, 37, 56, 75],
|
||||
"raman_parameters": {
|
||||
"flag_raman": true,
|
||||
"space_resolution": 10e3,
|
||||
"tolerance": 1e-8
|
||||
},
|
||||
"nli_parameters": {
|
||||
"nli_method_name": "ggn_spectrally_separated",
|
||||
"wdm_grid_size": 50e9,
|
||||
"dispersion_tolerance": 1,
|
||||
"phase_shift_tollerance": 0.1
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
0.0359549,
|
||||
5.82851
|
||||
],
|
||||
"f_min": 191.35e12,
|
||||
"f_max": 196.1e12,
|
||||
"nf_ripple": [
|
||||
-0.3110761646066259,
|
||||
-0.3110761646066259,
|
||||
|
||||
703
examples/topoDemov1.json
Normal file
703
examples/topoDemov1.json
Normal file
@@ -0,0 +1,703 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx site_a",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "Site a",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm site_a",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "Site a",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span1ab",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 100.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span1ba",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 100.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span2ab",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span2ba",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm site_b",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "Site b",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx site_b",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2,
|
||||
"longitude": 0,
|
||||
"city": "Site b",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "booster1 site_a",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site a",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "preamp site_b",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site b",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "booster1 site_b",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site b",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "preamp1 site_a",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site_a",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "booster2 site_a",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site a",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "preamp2 site_b",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site_b",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "booster2 site_b",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site b",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "preamp2 site_a",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site_a",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "booster3 site_a",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site a",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "preamp3 site_b",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site_b",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "booster3 site_b",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site b",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "preamp3 site_a",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site_a",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm site_c",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "Site c",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "booster1 site_c",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site c",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "preamp1 site_c",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site_c",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "booster2 site_c",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site c",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "preamp2 site_c",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0,
|
||||
"city": "Site_c",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span1ac",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span1ca",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span1bc",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Span1cb",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "trx site_a",
|
||||
"to_node": "roadm site_a"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm site_a",
|
||||
"to_node": "booster1 site_a"
|
||||
},
|
||||
{
|
||||
"from_node": "booster1 site_a",
|
||||
"to_node": "Span1ab"
|
||||
},
|
||||
{
|
||||
"from_node": "Span1ab",
|
||||
"to_node": "preamp site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "preamp site_b",
|
||||
"to_node": "roadm site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm site_b",
|
||||
"to_node": "trx site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm site_a",
|
||||
"to_node": "booster2 site_a"
|
||||
},
|
||||
{
|
||||
"from_node": "booster2 site_a",
|
||||
"to_node": "Span2ab"
|
||||
},
|
||||
{
|
||||
"from_node": "Span2ab",
|
||||
"to_node": "preamp2 site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "preamp2 site_b",
|
||||
"to_node": "roadm site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm site_b",
|
||||
"to_node": "booster1 site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "booster1 site_b",
|
||||
"to_node": "Span1ba"
|
||||
},
|
||||
{
|
||||
"from_node": "Span1ba",
|
||||
"to_node": "preamp1 site_a"
|
||||
},
|
||||
{
|
||||
"from_node": "preamp1 site_a",
|
||||
"to_node": "roadm site_a"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm site_b",
|
||||
"to_node": "booster2 site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "booster2 site_b",
|
||||
"to_node": "Span2ba"
|
||||
},
|
||||
{
|
||||
"from_node": "Span2ba",
|
||||
"to_node": "preamp2 site_a"
|
||||
},
|
||||
{
|
||||
"from_node": "preamp2 site_a",
|
||||
"to_node": "roadm site_a"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm site_a",
|
||||
"to_node": "booster3 site_a"
|
||||
},
|
||||
{
|
||||
"from_node": "booster3 site_a",
|
||||
"to_node": "Span1ac"
|
||||
},
|
||||
{
|
||||
"from_node": "Span1ac",
|
||||
"to_node": "preamp1 site_c"
|
||||
},
|
||||
{
|
||||
"from_node": "preamp1 site_c",
|
||||
"to_node": "roadm site_c"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm site_c",
|
||||
"to_node": "booster1 site_c"
|
||||
},
|
||||
{
|
||||
"from_node": "booster1 site_c",
|
||||
"to_node": "Span1cb"
|
||||
},
|
||||
{
|
||||
"from_node": "Span1cb",
|
||||
"to_node": "preamp3 site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "preamp3 site_b",
|
||||
"to_node": "roadm site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm site_b",
|
||||
"to_node": "booster3 site_b"
|
||||
},
|
||||
{
|
||||
"from_node": "booster3 site_b",
|
||||
"to_node": "Span1bc"
|
||||
},
|
||||
{
|
||||
"from_node": "Span1bc",
|
||||
"to_node": "preamp2 site_c"
|
||||
},
|
||||
{
|
||||
"from_node": "preamp2 site_c",
|
||||
"to_node": "roadm site_c"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm site_c",
|
||||
"to_node": "booster2 site_c"
|
||||
},
|
||||
{
|
||||
"from_node": "booster2 site_c",
|
||||
"to_node": "Span1ca"
|
||||
},
|
||||
{
|
||||
"from_node": "Span1ca",
|
||||
"to_node": "preamp3 site_a"
|
||||
},
|
||||
{
|
||||
"from_node": "preamp3 site_a",
|
||||
"to_node": "roadm site_a"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -18,20 +18,21 @@ from pathlib import Path
|
||||
from json import loads
|
||||
from collections import Counter
|
||||
from logging import getLogger, basicConfig, INFO, ERROR, DEBUG
|
||||
from numpy import arange, mean
|
||||
from matplotlib.pyplot import show, axis, figure, title
|
||||
from numpy import linspace, mean, log10
|
||||
from matplotlib.pyplot import show, axis, figure, title, text
|
||||
from networkx import (draw_networkx_nodes, draw_networkx_edges,
|
||||
draw_networkx_labels, dijkstra_path)
|
||||
from gnpy.core.network import load_network, build_network, save_network
|
||||
from gnpy.core.elements import Transceiver, Fiber, Edfa, Roadm
|
||||
from gnpy.core.network import load_network, build_network, save_network, load_sim_params, configure_network
|
||||
from gnpy.core.elements import Transceiver, Fiber, RamanFiber, Edfa, Roadm
|
||||
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
|
||||
from gnpy.core.request import Path_request, RequestParams, compute_constrained_path, propagate
|
||||
from gnpy.core.request import Path_request, RequestParams, compute_constrained_path, propagate2
|
||||
from gnpy.core.exceptions import ConfigurationError, EquipmentConfigError, NetworkTopologyError
|
||||
import gnpy.core.ansi_escapes as ansi_escapes
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
def plot_results(network, path, source, destination):
|
||||
path_edges = set(zip(path[:-1], path[1:]))
|
||||
edges = set(network.edges()) - path_edges
|
||||
def plot_baseline(network):
|
||||
edges = set(network.edges())
|
||||
pos = {n: (n.lng, n.lat) for n in network.nodes()}
|
||||
labels = {n: n.location.city for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
city_labels = set(labels.values())
|
||||
@@ -44,16 +45,61 @@ def plot_results(network, path, source, destination):
|
||||
fig = figure()
|
||||
kwargs = {'figure': fig, 'pos': pos}
|
||||
plot = draw_networkx_nodes(network, nodelist=network.nodes(), node_color='#ababab', **kwargs)
|
||||
draw_networkx_nodes(network, nodelist=path, node_color='#ff0000', **kwargs)
|
||||
draw_networkx_edges(network, edgelist=edges, edge_color='#ababab', **kwargs)
|
||||
draw_networkx_labels(network, labels=labels, font_size=14, **{**kwargs, 'pos': label_pos})
|
||||
axis('off')
|
||||
show()
|
||||
|
||||
def plot_results(network, path, source, destination, infos):
|
||||
path_edges = set(zip(path[:-1], path[1:]))
|
||||
edges = set(network.edges()) - path_edges
|
||||
pos = {n: (n.lng, n.lat) for n in network.nodes()}
|
||||
nodes = {}
|
||||
for k, (x, y) in pos.items():
|
||||
nodes.setdefault((round(x, 1), round(y, 1)), []).append(k)
|
||||
labels = {n: n.location.city for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
city_labels = set(labels.values())
|
||||
for n in network.nodes():
|
||||
if n.location.city and n.location.city not in city_labels:
|
||||
labels[n] = n.location.city
|
||||
city_labels.add(n.location.city)
|
||||
label_pos = pos
|
||||
|
||||
fig = figure()
|
||||
kwargs = {'figure': fig, 'pos': pos}
|
||||
all_nodes = [n for n in network.nodes() if n not in path]
|
||||
plot = draw_networkx_nodes(network, nodelist=all_nodes, node_color='#ababab', node_size=50, **kwargs)
|
||||
draw_networkx_nodes(network, nodelist=path, node_color='#ff0000', node_size=55, **kwargs)
|
||||
draw_networkx_edges(network, edgelist=edges, edge_color='#ababab', **kwargs)
|
||||
draw_networkx_edges(network, edgelist=path_edges, edge_color='#ff0000', **kwargs)
|
||||
draw_networkx_labels(network, labels=labels, font_size=14, **{**kwargs, 'pos': label_pos})
|
||||
title(f'Propagating from {source.loc.city} to {destination.loc.city}')
|
||||
axis('off')
|
||||
|
||||
heading = 'Spectral Information\n\n'
|
||||
textbox = text(0.85, 0.20, heading, fontsize=14, fontname='Ubuntu Mono',
|
||||
verticalalignment='top', transform=fig.axes[0].transAxes,
|
||||
bbox={'boxstyle': 'round', 'facecolor': 'wheat', 'alpha': 0.5})
|
||||
|
||||
msgs = {(x, y): heading + '\n\n'.join(str(n) for n in ns if n in path)
|
||||
for (x, y), ns in nodes.items()}
|
||||
|
||||
def hover(event):
|
||||
if event.xdata is None or event.ydata is None:
|
||||
return
|
||||
if fig.contains(event):
|
||||
x, y = round(event.xdata, 1), round(event.ydata, 1)
|
||||
if (x, y) in msgs:
|
||||
textbox.set_text(msgs[x, y])
|
||||
else:
|
||||
textbox.set_text(heading)
|
||||
fig.canvas.draw_idle()
|
||||
|
||||
fig.canvas.mpl_connect('motion_notify_event', hover)
|
||||
show()
|
||||
|
||||
|
||||
def main(network, equipment, source, destination, req = None):
|
||||
def main(network, equipment, source, destination, sim_params, req=None):
|
||||
result_dicts = {}
|
||||
network_data = [{
|
||||
'network_name' : str(args.filename),
|
||||
@@ -62,8 +108,8 @@ def main(network, equipment, source, destination, req = None):
|
||||
}]
|
||||
result_dicts.update({'network': network_data})
|
||||
design_data = [{
|
||||
'power_mode' : equipment['Spans']['default'].power_mode,
|
||||
'span_power_range' : equipment['Spans']['default'].delta_power_range_db,
|
||||
'power_mode' : equipment['Span']['default'].power_mode,
|
||||
'span_power_range' : equipment['Span']['default'].delta_power_range_db,
|
||||
'design_pch' : equipment['SI']['default'].power_dbm,
|
||||
'baud_rate' : equipment['SI']['default'].baud_rate
|
||||
}]
|
||||
@@ -71,56 +117,89 @@ def main(network, equipment, source, destination, req = None):
|
||||
simulation_data = []
|
||||
result_dicts.update({'simulation results': simulation_data})
|
||||
|
||||
power_mode = equipment['Spans']['default'].power_mode
|
||||
power_mode = equipment['Span']['default'].power_mode
|
||||
print('\n'.join([f'Power mode is set to {power_mode}',
|
||||
f'=> it can be modified in eqpt_config.json - Spans']))
|
||||
f'=> it can be modified in eqpt_config.json - Span']))
|
||||
|
||||
pref_ch_db = lin2db(req.power*1e3) #reference channel power / span (SL=20dB)
|
||||
pref_total_db = pref_ch_db + lin2db(req.nb_channel) #reference total power / span (SL=20dB)
|
||||
build_network(network, equipment, pref_ch_db, pref_total_db)
|
||||
path = compute_constrained_path(network, req)
|
||||
|
||||
spans = [s.length for s in path if isinstance(s, Fiber)]
|
||||
print(f'\nThere are {len(spans)} fiber spans over {sum(spans):.0f}m between {source.uid} and {destination.uid}')
|
||||
if len([s.length for s in path if isinstance(s, RamanFiber)]):
|
||||
if sim_params is None:
|
||||
print(f'{ansi_escapes.red}Invocation error:{ansi_escapes.reset} RamanFiber requires passing simulation params via --sim-params')
|
||||
exit(1)
|
||||
configure_network(network, sim_params)
|
||||
|
||||
spans = [s.length for s in path if isinstance(s, RamanFiber) or isinstance(s, Fiber)]
|
||||
print(f'\nThere are {len(spans)} fiber spans over {sum(spans)/1000:.0f} km between {source.uid} and {destination.uid}')
|
||||
print(f'\nNow propagating between {source.uid} and {destination.uid}:')
|
||||
|
||||
try:
|
||||
power_range = list(arange(*equipment['SI']['default'].power_range_db))
|
||||
last = equipment['SI']['default'].power_range_db[-2]
|
||||
if len(power_range) == 0 : #bad input that will lead to no simulation
|
||||
power_range = [0] #better than an error message
|
||||
else:
|
||||
power_range.append(last)
|
||||
p_start, p_stop, p_step = equipment['SI']['default'].power_range_db
|
||||
p_num = abs(int(round((p_stop - p_start)/p_step))) + 1 if p_step != 0 else 1
|
||||
power_range = list(linspace(p_start, p_stop, p_num))
|
||||
except TypeError:
|
||||
print('invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]')
|
||||
power_range = [0]
|
||||
|
||||
if not power_mode:
|
||||
#power cannot be changed in gain mode
|
||||
power_range = [0]
|
||||
for dp_db in power_range:
|
||||
req.power = db2lin(pref_ch_db + dp_db)*1e-3
|
||||
print(f'\nPropagating with input power = {lin2db(req.power*1e3):.2f}dBm :')
|
||||
propagate(path, req, equipment, show=len(power_range)==1)
|
||||
print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :')
|
||||
print(destination)
|
||||
simulation_data.append({
|
||||
'Pch_dBm' : pref_ch_db + dp_db,
|
||||
'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2),
|
||||
'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2),
|
||||
'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2),
|
||||
'SNR_total_signal_bw' : round(mean(destination.snr),2)
|
||||
})
|
||||
if power_mode:
|
||||
print(f'\nPropagating with input power = {ansi_escapes.cyan}{lin2db(req.power*1e3):.2f} dBm{ansi_escapes.reset}:')
|
||||
else:
|
||||
print(f'\nPropagating in {ansi_escapes.cyan}gain mode{ansi_escapes.reset}: power cannot be set manually')
|
||||
infos = propagate2(path, req, equipment)
|
||||
if len(power_range) == 1:
|
||||
for elem in path:
|
||||
print(elem)
|
||||
if power_mode:
|
||||
print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:')
|
||||
else:
|
||||
print(f'\nTransmission results:')
|
||||
print(f' Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}')
|
||||
else:
|
||||
print(path[-1])
|
||||
|
||||
#print(f'\n !!!!!!!!!!!!!!!!! TEST POINT !!!!!!!!!!!!!!!!!!!!!')
|
||||
#print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}')
|
||||
# => use "in" or "out" parameter
|
||||
# => use "nli" or "ase" or "signal" or "total" parameter
|
||||
if power_mode:
|
||||
simulation_data.append({
|
||||
'Pch_dBm' : pref_ch_db + dp_db,
|
||||
'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2),
|
||||
'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2),
|
||||
'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2),
|
||||
'SNR_total_signal_bw' : round(mean(destination.snr),2)
|
||||
})
|
||||
else:
|
||||
simulation_data.append({
|
||||
'gain_mode' : 'power canot be set',
|
||||
'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2),
|
||||
'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2),
|
||||
'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2),
|
||||
'SNR_total_signal_bw' : round(mean(destination.snr),2)
|
||||
})
|
||||
write_csv(result_dicts, 'simulation_result.csv')
|
||||
return path
|
||||
return path, infos
|
||||
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('-e', '--equipment', type=Path,
|
||||
default=Path(__file__).parent / 'eqpt_config.json')
|
||||
parser.add_argument('-pl', '--plot', action='store_true', default=False)
|
||||
parser.add_argument('-v', '--verbose', action='count')
|
||||
parser.add_argument('-l', '--list-nodes', action='store_true', default=False, help='list all transceiver nodes')
|
||||
parser.add_argument('--sim-params', type=Path,
|
||||
default=None, help='Path to the JSON containing simulation parameters (required for Raman)')
|
||||
parser.add_argument('--show-channels', action='store_true', help='Show final per-channel OSNR summary')
|
||||
parser.add_argument('-pl', '--plot', action='store_true')
|
||||
parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence')
|
||||
parser.add_argument('-l', '--list-nodes', action='store_true', help='list all transceiver nodes')
|
||||
parser.add_argument('-po', '--power', default=0, help='channel ref power in dBm')
|
||||
#parser.add_argument('-plb', '--power-lower-bound', default=0, help='power sweep lower bound')
|
||||
#parser.add_argument('-pub', '--power-upper-bound', default=1, help='power sweep upper bound')
|
||||
parser.add_argument('-names', '--names-matching', action='store_true', help='display network names that are closed matches')
|
||||
parser.add_argument('filename', nargs='?', type=Path,
|
||||
default=Path(__file__).parent / 'edfa_example_network.json')
|
||||
parser.add_argument('source', nargs='?', help='source node')
|
||||
@@ -129,13 +208,24 @@ parser.add_argument('destination', nargs='?', help='destination node')
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, ERROR))
|
||||
basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, DEBUG))
|
||||
|
||||
equipment = load_equipment(args.equipment)
|
||||
# logger.info(equipment)
|
||||
# print(args.filename)
|
||||
network = load_network(args.filename, equipment)
|
||||
# print(network)
|
||||
try:
|
||||
equipment = load_equipment(args.equipment)
|
||||
network = load_network(args.filename, equipment, args.names_matching)
|
||||
sim_params = load_sim_params(args.sim_params) if args.sim_params is not None else None
|
||||
except EquipmentConfigError as e:
|
||||
print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {e}')
|
||||
exit(1)
|
||||
except NetworkTopologyError as e:
|
||||
print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}')
|
||||
exit(1)
|
||||
except ConfigurationError as e:
|
||||
print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}')
|
||||
exit(1)
|
||||
|
||||
if args.plot:
|
||||
plot_baseline(network)
|
||||
|
||||
transceivers = {n.uid: n for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
|
||||
@@ -149,37 +239,39 @@ if __name__ == '__main__':
|
||||
print(uid)
|
||||
exit()
|
||||
|
||||
#First try to find exact match if source/destination provided
|
||||
if args.source:
|
||||
try:
|
||||
source = next(transceivers[uid] for uid in transceivers if uid == args.source)
|
||||
except StopIteration as e:
|
||||
#TODO code a more advanced regex to find nodes match
|
||||
nodes_suggestion = [uid for uid in transceivers \
|
||||
if args.source.lower() in uid.lower()]
|
||||
source = transceivers[nodes_suggestion[0]] \
|
||||
if len(nodes_suggestion)>0 else list(transceivers.values())[0]
|
||||
print(f'invalid souce node specified, did you mean:\
|
||||
\n{nodes_suggestion}?\
|
||||
\n{args.source!r}, replaced with {source.uid}')
|
||||
del transceivers[source.uid]
|
||||
source = transceivers.pop(args.source, None)
|
||||
valid_source = True if source else False
|
||||
else:
|
||||
source = None
|
||||
logger.info('No source node specified: picking random transceiver')
|
||||
source = list(transceivers.values())[0]
|
||||
|
||||
if args.destination:
|
||||
try:
|
||||
destination = next(transceivers[uid] for uid in transceivers if uid == args.destination)
|
||||
except StopIteration as e:
|
||||
nodes_suggestion = [uid for uid in transceivers \
|
||||
if args.destination.lower() in uid.lower()]
|
||||
destination = transceivers[nodes_suggestion[0]] \
|
||||
if len(nodes_suggestion)>0 else list(transceivers.values())[0]
|
||||
print(f'invalid destination node specified, did you mean:\
|
||||
\n{nodes_suggestion}?\
|
||||
\n{args.destination!r}, replaced with {destination.uid}')
|
||||
destination = transceivers.pop(args.destination, None)
|
||||
valid_destination = True if destination else False
|
||||
else:
|
||||
logger.info('No source node specified: picking random transceiver')
|
||||
destination = list(transceivers.values())[1]
|
||||
destination = None
|
||||
logger.info('No destination node specified: picking random transceiver')
|
||||
|
||||
#If no exact match try to find partial match
|
||||
if args.source and not source:
|
||||
#TODO code a more advanced regex to find nodes match
|
||||
source = next((transceivers.pop(uid) for uid in transceivers \
|
||||
if args.source.lower() in uid.lower()), None)
|
||||
|
||||
if args.destination and not destination:
|
||||
#TODO code a more advanced regex to find nodes match
|
||||
destination = next((transceivers.pop(uid) for uid in transceivers \
|
||||
if args.destination.lower() in uid.lower()), None)
|
||||
|
||||
#If no partial match or no source/destination provided pick random
|
||||
if not source:
|
||||
source = list(transceivers.values())[0]
|
||||
del transceivers[source.uid]
|
||||
|
||||
if not destination:
|
||||
destination = list(transceivers.values())[0]
|
||||
|
||||
logger.info(f'source = {args.source!r}')
|
||||
logger.info(f'destination = {args.destination!r}')
|
||||
@@ -190,16 +282,38 @@ if __name__ == '__main__':
|
||||
params['trx_mode'] = ''
|
||||
params['source'] = source.uid
|
||||
params['destination'] = destination.uid
|
||||
params['bidir'] = False
|
||||
params['nodes_list'] = [destination.uid]
|
||||
params['loose_list'] = ['strict']
|
||||
params['format'] = ''
|
||||
params['path_bandwidth'] = 0
|
||||
trx_params = trx_mode_params(equipment)
|
||||
if args.power:
|
||||
trx_params['power'] = db2lin(float(args.power))*1e-3
|
||||
params.update(trx_params)
|
||||
req = Path_request(**params)
|
||||
path = main(network, equipment, source, destination, req)
|
||||
path, infos = main(network, equipment, source, destination, sim_params, req)
|
||||
save_network(args.filename, network)
|
||||
|
||||
if args.show_channels:
|
||||
print('\nThe total SNR per channel at the end of the line is:')
|
||||
print('{:>5}{:>26}{:>26}{:>28}{:>28}{:>28}' \
|
||||
.format('Ch. #', 'Channel frequency (THz)', 'Channel power (dBm)', 'OSNR ASE (signal bw, dB)', 'SNR NLI (signal bw, dB)', 'SNR total (signal bw, dB)'))
|
||||
for final_carrier, ch_osnr, ch_snr_nl, ch_snr in zip(infos[path[-1]][1].carriers, path[-1].osnr_ase, path[-1].osnr_nli, path[-1].snr):
|
||||
ch_freq = final_carrier.frequency * 1e-12
|
||||
ch_power = lin2db(final_carrier.power.signal*1e3)
|
||||
print('{:5}{:26.2f}{:26.2f}{:28.2f}{:28.2f}{:28.2f}' \
|
||||
.format(final_carrier.channel_number, round(ch_freq, 2), round(ch_power, 2), round(ch_osnr, 2), round(ch_snr_nl, 2), round(ch_snr, 2)))
|
||||
|
||||
if not args.source:
|
||||
print(f'\n(No source node specified: picked {source.uid})')
|
||||
elif not valid_source:
|
||||
print(f'\n(Invalid source node {args.source!r} replaced with {source.uid})')
|
||||
|
||||
if not args.destination:
|
||||
print(f'\n(No destination node specified: picked {destination.uid})')
|
||||
elif not valid_destination:
|
||||
print(f'\n(Invalid destination node {args.destination!r} replaced with {destination.uid})')
|
||||
|
||||
if args.plot:
|
||||
plot_results(network, path, source, destination)
|
||||
plot_results(network, path, source, destination, infos)
|
||||
|
||||
@@ -26,8 +26,8 @@ parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.output_filename,"w") as file :
|
||||
with open(args.filename) as f:
|
||||
with open(args.output_filename, 'w', encoding='utf-8') as file:
|
||||
with open(args.filename, encoding='utf-8') as f:
|
||||
print(f'Reading {args.filename}')
|
||||
json_data = loads(f.read())
|
||||
equipment = load_equipment(args.eqpt_filename)
|
||||
|
||||
13
gnpy/core/ansi_escapes.py
Normal file
13
gnpy/core/ansi_escapes.py
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.core.ansi_escapes
|
||||
======================
|
||||
|
||||
A random subset of ANSI terminal escape codes for colored messages
|
||||
'''
|
||||
|
||||
red = '\x1b[1;31;40m'
|
||||
cyan = '\x1b[1;36;40m'
|
||||
reset = '\x1b[0m'
|
||||
@@ -30,49 +30,186 @@ from collections import namedtuple, Counter, defaultdict
|
||||
from itertools import chain
|
||||
from json import dumps
|
||||
from pathlib import Path
|
||||
from difflib import get_close_matches
|
||||
from gnpy.core.utils import silent_remove
|
||||
from gnpy.core.exceptions import NetworkTopologyError
|
||||
import time
|
||||
|
||||
all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
|
||||
|
||||
class Node(namedtuple('Node', 'city state country region latitude longitude node_type')):
|
||||
def __new__(cls, city, state='', country='', region='', latitude=0, longitude=0, node_type='ILA'):
|
||||
values = [latitude, longitude, node_type]
|
||||
default_values = [0, 0, 'ILA']
|
||||
values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)]
|
||||
return super().__new__(cls, city, state, country, region, *values)
|
||||
class Node(object):
|
||||
def __init__(self, **kwargs):
|
||||
super(Node, self).__init__()
|
||||
self.update_attr(kwargs)
|
||||
|
||||
class Link(namedtuple('Link', 'from_city to_city \
|
||||
east_distance east_fiber east_lineic east_con_in east_con_out east_pmd east_cable \
|
||||
west_distance west_fiber west_lineic west_con_in west_con_out west_pmd west_cable \
|
||||
distance_units')):
|
||||
def __new__(cls, from_city, to_city,
|
||||
east_distance, east_fiber='SSMF', east_lineic=0.2,
|
||||
east_con_in=None, east_con_out=None, east_pmd=0.1, east_cable='',
|
||||
west_distance='', west_fiber='', west_lineic='',
|
||||
west_con_in='', west_con_out='', west_pmd='', west_cable='',
|
||||
distance_units='km'):
|
||||
east_values = [east_distance, east_fiber, east_lineic, east_con_in, east_con_out,
|
||||
east_pmd, east_cable]
|
||||
west_values = [west_distance, west_fiber, west_lineic, west_con_in, west_con_out,
|
||||
west_pmd, west_cable]
|
||||
default_values = [80,'SSMF',0.2,None,None,0.1,'']
|
||||
east_values = [x[0] if x[0] != '' else x[1] for x in zip(east_values,default_values)]
|
||||
west_values = [x[0] if x[0] != '' else x[1] for x in zip(west_values,east_values)]
|
||||
return super().__new__(cls, from_city, to_city, *east_values, *west_values, distance_units)
|
||||
def update_attr(self, kwargs):
|
||||
clean_kwargs = {k:v for k,v in kwargs.items() if v !=''}
|
||||
for k,v in self.default_values.items():
|
||||
v = clean_kwargs.get(k,v)
|
||||
setattr(self, k, v)
|
||||
|
||||
class Eqpt(namedtuple('Eqpt', 'from_city to_city \
|
||||
egress_amp_type egress_att_in egress_amp_gain egress_amp_tilt egress_amp_att_out\
|
||||
ingress_amp_type ingress_att_in ingress_amp_gain ingress_amp_tilt ingress_amp_att_out')):
|
||||
def __new__(cls, from_city='', to_city='',
|
||||
egress_amp_type='', egress_att_in=0, egress_amp_gain=0, egress_amp_tilt=0, egress_amp_att_out=0,
|
||||
ingress_amp_type='', ingress_att_in=0, ingress_amp_gain=0, ingress_amp_tilt=0, ingress_amp_att_out=0):
|
||||
values = [from_city, to_city,
|
||||
egress_amp_type, egress_att_in, egress_amp_gain, egress_amp_tilt, egress_amp_att_out,
|
||||
ingress_amp_type, ingress_att_in, ingress_amp_gain, ingress_amp_tilt, ingress_amp_att_out]
|
||||
default_values = ['','','',0,0,0,0,'',0,0,0,0]
|
||||
values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)]
|
||||
return super().__new__(cls, *values)
|
||||
default_values = \
|
||||
{
|
||||
'city': '',
|
||||
'state': '',
|
||||
'country': '',
|
||||
'region': '',
|
||||
'latitude': 0,
|
||||
'longitude': 0,
|
||||
'node_type': 'ILA',
|
||||
'booster_restriction' : '',
|
||||
'preamp_restriction' : ''
|
||||
}
|
||||
|
||||
class Link(object):
|
||||
"""attribtes from west parse_ept_headers dict
|
||||
+node_a, node_z, west_fiber_con_in, east_fiber_con_in
|
||||
"""
|
||||
def __init__(self, **kwargs):
|
||||
super(Link, self).__init__()
|
||||
self.update_attr(kwargs)
|
||||
self.distance_units = 'km'
|
||||
|
||||
def update_attr(self, kwargs):
|
||||
clean_kwargs = {k:v for k,v in kwargs.items() if v !=''}
|
||||
for k,v in self.default_values.items():
|
||||
v = clean_kwargs.get(k,v)
|
||||
setattr(self, k, v)
|
||||
k = 'west' + k.split('east')[-1]
|
||||
v = clean_kwargs.get(k,v)
|
||||
setattr(self, k, v)
|
||||
|
||||
def __eq__(self, link):
|
||||
return (self.from_city == link.from_city and self.to_city == link.to_city) \
|
||||
or (self.from_city == link.to_city and self.to_city == link.from_city)
|
||||
|
||||
default_values = \
|
||||
{
|
||||
'from_city': '',
|
||||
'to_city': '',
|
||||
'east_distance': 80,
|
||||
'east_fiber': 'SSMF',
|
||||
'east_lineic': 0.2,
|
||||
'east_con_in': None,
|
||||
'east_con_out': None,
|
||||
'east_pmd': 0.1,
|
||||
'east_cable': ''
|
||||
}
|
||||
|
||||
|
||||
class Eqpt(object):
|
||||
def __init__(self, **kwargs):
|
||||
super(Eqpt, self).__init__()
|
||||
self.update_attr(kwargs)
|
||||
|
||||
def update_attr(self, kwargs):
|
||||
clean_kwargs = {k:v for k,v in kwargs.items() if v !=''}
|
||||
for k,v in self.default_values.items():
|
||||
v_east = clean_kwargs.get(k,v)
|
||||
setattr(self, k, v_east)
|
||||
k = 'west' + k.split('east')[-1]
|
||||
v_west = clean_kwargs.get(k,v)
|
||||
setattr(self, k, v_west)
|
||||
|
||||
default_values = \
|
||||
{
|
||||
'from_city': '',
|
||||
'to_city': '',
|
||||
'east_amp_type': '',
|
||||
'east_att_in': 0,
|
||||
'east_amp_gain': None,
|
||||
'east_amp_dp': None,
|
||||
'east_tilt': 0,
|
||||
'east_att_out': None
|
||||
}
|
||||
|
||||
|
||||
def read_header(my_sheet, line, slice_):
|
||||
""" return the list of headers !:= ''
|
||||
header_i = [(header, header_column_index), ...]
|
||||
in a {line, slice1_x, slice_y} range
|
||||
"""
|
||||
Param_header = namedtuple('Param_header', 'header colindex')
|
||||
try:
|
||||
header = [x.value.strip() for x in my_sheet.row_slice(line, slice_[0], slice_[1])]
|
||||
header_i = [Param_header(header,i+slice_[0]) for i, header in enumerate(header) if header != '']
|
||||
except Exception:
|
||||
header_i = []
|
||||
if header_i != [] and header_i[-1].colindex != slice_[1]:
|
||||
header_i.append(Param_header('',slice_[1]))
|
||||
return header_i
|
||||
|
||||
def read_slice(my_sheet, line, slice_, header):
|
||||
"""return the slice range of a given header
|
||||
in a defined range {line, slice_x, slice_y}"""
|
||||
header_i = read_header(my_sheet, line, slice_)
|
||||
slice_range = (-1,-1)
|
||||
if header_i != []:
|
||||
try:
|
||||
slice_range = next((h.colindex,header_i[i+1].colindex) \
|
||||
for i,h in enumerate(header_i) if header in h.header)
|
||||
except Exception:
|
||||
pass
|
||||
return slice_range
|
||||
|
||||
|
||||
def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in):
|
||||
"""return a dict of header_slice
|
||||
key = column index
|
||||
value = header name"""
|
||||
|
||||
|
||||
for h0 in input_headers_dict:
|
||||
slice_out = read_slice(my_sheet, start_line, slice_in, h0)
|
||||
iteration = 1
|
||||
while slice_out == (-1,-1) and iteration < 10:
|
||||
#try next lines
|
||||
#print(h0, iteration)
|
||||
slice_out = read_slice(my_sheet, start_line+iteration, slice_in, h0)
|
||||
iteration += 1
|
||||
if slice_out == (-1, -1):
|
||||
if h0 in ('east', 'Node A', 'Node Z', 'City') :
|
||||
print(f'\x1b[1;31;40m'+f'CRITICAL: missing _{h0}_ header: EXECUTION ENDS'+ '\x1b[0m')
|
||||
exit()
|
||||
else:
|
||||
print(f'missing header {h0}')
|
||||
elif not isinstance(input_headers_dict[h0], dict):
|
||||
headers[slice_out[0]] = input_headers_dict[h0]
|
||||
else:
|
||||
headers = parse_headers(my_sheet, input_headers_dict[h0], headers, start_line+1, slice_out)
|
||||
if headers == {}:
|
||||
print(f'\x1b[1;31;40m'+f'CRITICAL ERROR: could not find any header to read _ ABORT'+ '\x1b[0m')
|
||||
exit()
|
||||
return headers
|
||||
|
||||
def parse_row(row, headers):
|
||||
#print([label for label in ept.values()])
|
||||
#print([i for i in ept.keys()])
|
||||
#print(row[i for i in ept.keys()])
|
||||
return {f: r.value for f, r in \
|
||||
zip([label for label in headers.values()], [row[i] for i in headers])}
|
||||
#if r.ctype != XL_CELL_EMPTY}
|
||||
|
||||
def parse_sheet(my_sheet, input_headers_dict, header_line, start_line, column):
|
||||
headers = parse_headers(my_sheet, input_headers_dict, {}, header_line, (0,column))
|
||||
for row in all_rows(my_sheet, start=start_line):
|
||||
yield parse_row(row[0: column], headers)
|
||||
|
||||
def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
|
||||
duplicate_links = []
|
||||
for l1 in links:
|
||||
for l2 in links:
|
||||
if l1 is not l2 and l1 == l2 and l2 not in duplicate_links:
|
||||
print(f'\nWARNING\n \
|
||||
link {l1.from_city}-{l1.to_city} is duplicate \
|
||||
\nthe 1st duplicate link will be removed but you should check Links sheet input')
|
||||
duplicate_links.append(l1)
|
||||
#if duplicate_links != []:
|
||||
#time.sleep(3)
|
||||
for l in duplicate_links:
|
||||
links.remove(l)
|
||||
|
||||
def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
try :
|
||||
test_nodes = [n for n in nodes_by_city if not n in links_by_city]
|
||||
test_links = [n for n in links_by_city if not n in nodes_by_city]
|
||||
@@ -81,7 +218,7 @@ def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
and (test_links == [] or test_links ==[''])\
|
||||
and (test_eqpts == [] or test_eqpts ==[''])
|
||||
except AssertionError:
|
||||
print(f'!names in Nodes and Links sheets do no match, check:\
|
||||
print(f'CRITICAL error: \nNames in Nodes and Links sheets do no match, check:\
|
||||
\n{test_nodes} in Nodes sheet\
|
||||
\n{test_links} in Links sheet\
|
||||
\n{test_eqpts} in Eqpt sheet')
|
||||
@@ -94,13 +231,14 @@ def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
#TODO : put in log rather than print
|
||||
print(f'invalid node type ({nodes_by_city[city].node_type})\
|
||||
specified in {city}, replaced by ROADM')
|
||||
nodes_by_city[city] = nodes_by_city[city]._replace(node_type='ROADM')
|
||||
nodes = [n._replace(node_type='ROADM') if n.city==city else n for n in nodes]
|
||||
return nodes
|
||||
nodes_by_city[city].node_type = 'ROADM'
|
||||
for n in nodes:
|
||||
if n.city==city:
|
||||
n.node_type='ROADM'
|
||||
return nodes, links
|
||||
|
||||
def convert_file(input_filename, filter_region=[]):
|
||||
def convert_file(input_filename, names_matching=False, filter_region=[]):
|
||||
nodes, links, eqpts = parse_excel(input_filename)
|
||||
|
||||
if filter_region:
|
||||
nodes = [n for n in nodes if n.region.lower() in filter_region]
|
||||
cities = {n.city for n in nodes}
|
||||
@@ -111,6 +249,27 @@ def convert_file(input_filename, filter_region=[]):
|
||||
|
||||
global nodes_by_city
|
||||
nodes_by_city = {n.city: n for n in nodes}
|
||||
#create matching dictionary for node name mismatch analysis
|
||||
|
||||
cities = {''.join(c.strip() for c in n.city.split('C+L')).lower(): n.city for n in nodes}
|
||||
cities_to_match = [k for k in cities]
|
||||
city_match_dic = defaultdict(list)
|
||||
for city in cities:
|
||||
if city in cities_to_match:
|
||||
cities_to_match.remove(city)
|
||||
matches = get_close_matches(city, cities_to_match, 4, 0.85)
|
||||
for m in matches:
|
||||
city_match_dic[cities[city]].append(cities[m])
|
||||
#check lower case/upper case
|
||||
for city in nodes_by_city:
|
||||
for match_city in nodes_by_city:
|
||||
if match_city.lower() == city.lower() and match_city != city:
|
||||
city_match_dic[city].append(match_city)
|
||||
|
||||
if names_matching:
|
||||
print('\ncity match dictionary:',city_match_dic)
|
||||
with open('name_match_dictionary.json', 'w', encoding='utf-8') as city_match_dic_file:
|
||||
city_match_dic_file.write(dumps(city_match_dic, indent=2, ensure_ascii=False))
|
||||
|
||||
global links_by_city
|
||||
links_by_city = defaultdict(list)
|
||||
@@ -123,7 +282,7 @@ def convert_file(input_filename, filter_region=[]):
|
||||
for eqpt in eqpts:
|
||||
eqpts_by_city[eqpt.from_city].append(eqpt)
|
||||
|
||||
nodes = sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city)
|
||||
nodes, links = sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city)
|
||||
|
||||
data = {
|
||||
'elements':
|
||||
@@ -140,22 +299,37 @@ def convert_file(input_filename, filter_region=[]):
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Roadm'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] +
|
||||
[{'uid': f'ingress fused spans in {x.city}',
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' \
|
||||
and x.booster_restriction == '' and x.preamp_restriction == ''] +
|
||||
[{'uid': f'roadm {x.city}',
|
||||
'params' : {
|
||||
'restrictions': {
|
||||
'preamp_variety_list': silent_remove(x.preamp_restriction.split(' | '),''),
|
||||
'booster_variety_list': silent_remove(x.booster_restriction.split(' | '),'')
|
||||
}
|
||||
},
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Roadm'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' and \
|
||||
(x.booster_restriction != '' or x.preamp_restriction != '')] +
|
||||
[{'uid': f'west fused spans in {x.city}',
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Fused'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] +
|
||||
[{'uid': f'egress fused spans in {x.city}',
|
||||
[{'uid': f'east fused spans in {x.city}',
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Fused'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] +
|
||||
[{'uid': f'fiber ({x.from_city} → {x.to_city})-{x.east_cable}',
|
||||
[{'uid': f'fiber ({x.from_city} \u2192 {x.to_city})-{x.east_cable}',
|
||||
'metadata': {'location': midpoint(nodes_by_city[x.from_city],
|
||||
nodes_by_city[x.to_city])},
|
||||
'type': 'Fiber',
|
||||
@@ -167,7 +341,7 @@ def convert_file(input_filename, filter_region=[]):
|
||||
'con_out':x.east_con_out}
|
||||
}
|
||||
for x in links] +
|
||||
[{'uid': f'fiber ({x.to_city} → {x.from_city})-{x.west_cable}',
|
||||
[{'uid': f'fiber ({x.to_city} \u2192 {x.from_city})-{x.west_cable}',
|
||||
'metadata': {'location': midpoint(nodes_by_city[x.from_city],
|
||||
nodes_by_city[x.to_city])},
|
||||
'type': 'Fiber',
|
||||
@@ -179,28 +353,56 @@ def convert_file(input_filename, filter_region=[]):
|
||||
'con_out':x.west_con_out}
|
||||
} # missing ILA construction
|
||||
for x in links] +
|
||||
[{'uid': f'egress edfa in {e.from_city} to {e.to_city}',
|
||||
[{'uid': f'east edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Edfa',
|
||||
'type_variety': e.egress_amp_type,
|
||||
'operational': {'gain_target': e.egress_amp_gain,
|
||||
'tilt_target': e.egress_amp_tilt}
|
||||
}
|
||||
for e in eqpts if e.egress_amp_type.lower() != ''] +
|
||||
[{'uid': f'ingress edfa in {e.from_city} to {e.to_city}',
|
||||
'type_variety': e.east_amp_type,
|
||||
'operational': {'gain_target': e.east_amp_gain,
|
||||
'delta_p': e.east_amp_dp,
|
||||
'tilt_target': e.east_tilt,
|
||||
'out_voa' : e.east_att_out}
|
||||
}
|
||||
for e in eqpts if (e.east_amp_type.lower() != '' and \
|
||||
e.east_amp_type.lower() != 'fused')] +
|
||||
[{'uid': f'west edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Edfa',
|
||||
'type_variety': e.ingress_amp_type,
|
||||
'operational': {'gain_target': e.ingress_amp_gain,
|
||||
'tilt_target': e.ingress_amp_tilt}
|
||||
}
|
||||
for e in eqpts if e.ingress_amp_type.lower() != ''],
|
||||
'type_variety': e.west_amp_type,
|
||||
'operational': {'gain_target': e.west_amp_gain,
|
||||
'delta_p': e.west_amp_dp,
|
||||
'tilt_target': e.west_tilt,
|
||||
'out_voa' : e.west_att_out}
|
||||
}
|
||||
for e in eqpts if (e.west_amp_type.lower() != '' and \
|
||||
e.west_amp_type.lower() != 'fused')] +
|
||||
# fused edfa variety is a hack to indicate that there should not be
|
||||
# booster amplifier out the roadm.
|
||||
# If user specifies ILA in Nodes sheet and fused in Eqpt sheet, then assumes that
|
||||
# this is a fused nodes.
|
||||
[{'uid': f'east edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Fused',
|
||||
'params': {'loss': 0}
|
||||
}
|
||||
for e in eqpts if e.east_amp_type.lower() == 'fused'] +
|
||||
[{'uid': f'west edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Fused',
|
||||
'params': {'loss': 0}
|
||||
}
|
||||
for e in eqpts if e.west_amp_type.lower() == 'fused'],
|
||||
'connections':
|
||||
list(chain.from_iterable([eqpt_connection_by_city(n.city)
|
||||
for n in nodes]))
|
||||
@@ -214,68 +416,106 @@ def convert_file(input_filename, filter_region=[]):
|
||||
for x in nodes_by_city.values() if x.node_type.lower()=='roadm'])))
|
||||
}
|
||||
|
||||
#print(dumps(data, indent=2))
|
||||
# output_json_file_name = input_filename.split(".")[0]+".json"
|
||||
suffix_filename = str(input_filename.suffixes[0])
|
||||
full_input_filename = str(input_filename)
|
||||
split_filename = [full_input_filename[0:len(full_input_filename)-len(suffix_filename)] , suffix_filename[1:]]
|
||||
output_json_file_name = split_filename[0]+'.json'
|
||||
with open(output_json_file_name,'w') as edfa_json_file:
|
||||
edfa_json_file.write(dumps(data, indent=2))
|
||||
with open(output_json_file_name, 'w', encoding='utf-8') as edfa_json_file:
|
||||
edfa_json_file.write(dumps(data, indent=2, ensure_ascii=False))
|
||||
return output_json_file_name
|
||||
|
||||
def parse_excel(input_filename):
|
||||
link_headers = \
|
||||
{ 'Node A': 'from_city',
|
||||
'Node Z': 'to_city',
|
||||
'east':{
|
||||
'Distance (km)': 'east_distance',
|
||||
'Fiber type': 'east_fiber',
|
||||
'lineic att': 'east_lineic',
|
||||
'Con_in': 'east_con_in',
|
||||
'Con_out': 'east_con_out',
|
||||
'PMD': 'east_pmd',
|
||||
'Cable id': 'east_cable'
|
||||
},
|
||||
'west':{
|
||||
'Distance (km)': 'west_distance',
|
||||
'Fiber type': 'west_fiber',
|
||||
'lineic att': 'west_lineic',
|
||||
'Con_in': 'west_con_in',
|
||||
'Con_out': 'west_con_out',
|
||||
'PMD': 'west_pmd',
|
||||
'Cable id': 'west_cable'
|
||||
}
|
||||
}
|
||||
node_headers = \
|
||||
{ 'City': 'city',
|
||||
'State': 'state',
|
||||
'Country': 'country',
|
||||
'Region': 'region',
|
||||
'Latitude': 'latitude',
|
||||
'Longitude': 'longitude',
|
||||
'Type': 'node_type',
|
||||
'Booster_restriction': 'booster_restriction',
|
||||
'Preamp_restriction': 'preamp_restriction'
|
||||
}
|
||||
eqpt_headers = \
|
||||
{ 'Node A': 'from_city',
|
||||
'Node Z': 'to_city',
|
||||
'east':{
|
||||
'amp type': 'east_amp_type',
|
||||
'att_in': 'east_att_in',
|
||||
'amp gain': 'east_amp_gain',
|
||||
'delta p': 'east_amp_dp',
|
||||
'tilt': 'east_tilt',
|
||||
'att_out': 'east_att_out'
|
||||
},
|
||||
'west':{
|
||||
'amp type': 'west_amp_type',
|
||||
'att_in': 'west_att_in',
|
||||
'amp gain': 'west_amp_gain',
|
||||
'delta p': 'west_amp_dp',
|
||||
'tilt': 'west_tilt',
|
||||
'att_out': 'west_att_out'
|
||||
}
|
||||
}
|
||||
|
||||
with open_workbook(input_filename) as wb:
|
||||
nodes_sheet = wb.sheet_by_name('Nodes')
|
||||
links_sheet = wb.sheet_by_name('Links')
|
||||
try:
|
||||
eqpt_sheet = wb.sheet_by_name('Eqpt')
|
||||
except:
|
||||
except Exception:
|
||||
#eqpt_sheet is optional
|
||||
eqpt_sheet = None
|
||||
|
||||
|
||||
# sanity check
|
||||
"""
|
||||
header = [x.value.strip() for x in nodes_sheet.row(4)]
|
||||
expected = ['City', 'State', 'Country', 'Region', 'Latitude', 'Longitude']
|
||||
if header != expected:
|
||||
raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}')
|
||||
"""
|
||||
|
||||
nodes = []
|
||||
for row in all_rows(nodes_sheet, start=5):
|
||||
nodes.append(Node(*(x.value for x in row[0:NODES_COLUMN])))
|
||||
#check input
|
||||
expected_node_types = ('ROADM', 'ILA', 'FUSED')
|
||||
nodes = [n._replace(node_type='ILA')
|
||||
if not (n.node_type in expected_node_types) else n for n in nodes]
|
||||
for node in parse_sheet(nodes_sheet, node_headers, NODES_LINE, NODES_LINE+1, NODES_COLUMN):
|
||||
nodes.append(Node(**node))
|
||||
expected_node_types = {'ROADM', 'ILA', 'FUSED'}
|
||||
for n in nodes:
|
||||
if n.node_type not in expected_node_types:
|
||||
n.node_type = 'ILA'
|
||||
|
||||
# sanity check
|
||||
"""
|
||||
header = [x.value.strip() for x in links_sheet.row(4)]
|
||||
expected = ['Node A', 'Node Z',
|
||||
'Distance (km)', 'Fiber type', 'lineic att', 'Con_in', 'Con_out', 'PMD', 'Cable id',
|
||||
'Distance (km)', 'Fiber type', 'lineic att', 'Con_in', 'Con_out', 'PMD', 'Cable id']
|
||||
if header != expected:
|
||||
raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}')
|
||||
"""
|
||||
links = []
|
||||
for row in all_rows(links_sheet, start=5):
|
||||
links.append(Link(*(x.value for x in row[0:LINKS_COLUMN])))
|
||||
for link in parse_sheet(links_sheet, link_headers, LINKS_LINE, LINKS_LINE+2, LINKS_COLUMN):
|
||||
links.append(Link(**link))
|
||||
#print('\n', [l.__dict__ for l in links])
|
||||
|
||||
eqpts = []
|
||||
if eqpt_sheet != None:
|
||||
for row in all_rows(eqpt_sheet, start=5):
|
||||
eqpts.append(Eqpt(*(x.value for x in row[0:EQPTS_COLUMN])))
|
||||
for eqpt in parse_sheet(eqpt_sheet, eqpt_headers, EQPTS_LINE, EQPTS_LINE+2, EQPTS_COLUMN):
|
||||
eqpts.append(Eqpt(**eqpt))
|
||||
|
||||
# sanity check
|
||||
all_cities = Counter(n.city for n in nodes)
|
||||
if len(all_cities) != len(nodes):
|
||||
ValueError(f'Duplicate city: {all_cities}')
|
||||
if any(ln.from_city not in all_cities or
|
||||
ln.to_city not in all_cities for ln in links):
|
||||
ValueError(f'Bad link.')
|
||||
raise ValueError(f'Duplicate city: {all_cities}')
|
||||
bad_links = []
|
||||
for lnk in links:
|
||||
if lnk.from_city not in all_cities or lnk.to_city not in all_cities:
|
||||
bad_links.append([lnk.from_city, lnk.to_city])
|
||||
if bad_links:
|
||||
raise NetworkTopologyError(f'Bad link(s): {bad_links}.')
|
||||
|
||||
return nodes, links, eqpts
|
||||
|
||||
@@ -283,9 +523,9 @@ def parse_excel(input_filename):
|
||||
def eqpt_connection_by_city(city_name):
|
||||
other_cities = fiber_dest_from_source(city_name)
|
||||
subdata = []
|
||||
if nodes_by_city[city_name].node_type.lower() in ('ila', 'fused'):
|
||||
if nodes_by_city[city_name].node_type.lower() in {'ila', 'fused'}:
|
||||
# Then len(other_cities) == 2
|
||||
direction = ['ingress', 'egress']
|
||||
direction = ['west', 'east']
|
||||
for i in range(2):
|
||||
from_ = fiber_link(other_cities[i], city_name)
|
||||
in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i])
|
||||
@@ -299,7 +539,7 @@ def eqpt_connection_by_city(city_name):
|
||||
subdata += connect_eqpt(from_, in_, to_)
|
||||
|
||||
from_ = fiber_link(other_city, city_name)
|
||||
in_ = eqpt_in_city_to_city(city_name, other_city, "ingress")
|
||||
in_ = eqpt_in_city_to_city(city_name, other_city, "west")
|
||||
to_ = f'roadm {city_name}'
|
||||
subdata += connect_eqpt(from_, in_, to_)
|
||||
return subdata
|
||||
@@ -315,8 +555,8 @@ def connect_eqpt(from_, in_, to_):
|
||||
return connections
|
||||
|
||||
|
||||
def eqpt_in_city_to_city(in_city, to_city, direction='egress'):
|
||||
rev_direction = 'ingress' if direction == 'egress' else 'egress'
|
||||
def eqpt_in_city_to_city(in_city, to_city, direction='east'):
|
||||
rev_direction = 'west' if direction == 'east' else 'east'
|
||||
amp_direction = f'{direction}_amp_type'
|
||||
amp_rev_direction = f'{rev_direction}_amp_type'
|
||||
return_eqpt = ''
|
||||
@@ -352,9 +592,9 @@ def fiber_link(from_city, to_city):
|
||||
link = links_by_city[from_city]
|
||||
l = next(l for l in link if l.from_city in source_dest and l.to_city in source_dest)
|
||||
if l.from_city == from_city:
|
||||
fiber = f'fiber ({l.from_city} → {l.to_city})-{l.east_cable}'
|
||||
fiber = f'fiber ({l.from_city} \u2192 {l.to_city})-{l.east_cable}'
|
||||
else:
|
||||
fiber = f'fiber ({l.to_city} → {l.from_city})-{l.west_cable}'
|
||||
fiber = f'fiber ({l.to_city} \u2192 {l.from_city})-{l.west_cable}'
|
||||
return fiber
|
||||
|
||||
|
||||
@@ -375,9 +615,13 @@ def midpoint(city_a, city_b):
|
||||
|
||||
#output_json_file_name = 'coronet_conus_example.json'
|
||||
#TODO get column size automatically from tupple size
|
||||
NODES_COLUMN = 7
|
||||
|
||||
NODES_COLUMN = 10
|
||||
NODES_LINE = 4
|
||||
LINKS_COLUMN = 16
|
||||
EQPTS_COLUMN = 12
|
||||
LINKS_LINE = 3
|
||||
EQPTS_LINE = 3
|
||||
EQPTS_COLUMN = 14
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls')
|
||||
parser.add_argument('-f', '--filter-region', action='append', default=[])
|
||||
|
||||
@@ -7,25 +7,26 @@ gnpy.core.elements
|
||||
|
||||
This module contains standard network elements.
|
||||
|
||||
A network element is a Python callable. It takes a .info.SpectralInformation
|
||||
A network element is a Python callable. It takes a :class:`.info.SpectralInformation`
|
||||
object and returns a copy with appropriate fields affected. This structure
|
||||
represents spectral information that is "propogated" by this network element.
|
||||
Network elements must have only a local "view" of the network and propogate
|
||||
SpectralInformation using only this information. They should be independent and
|
||||
:class:`.info.SpectralInformation` using only this information. They should be independent and
|
||||
self-contained.
|
||||
|
||||
Network elements MUST implement two attributes .uid and .name representing a
|
||||
unique identifier and a printable name.
|
||||
'''
|
||||
|
||||
from numpy import abs, arange, arcsinh, array, exp
|
||||
from numpy import abs, arange, array, exp, divide, errstate
|
||||
from numpy import interp, log10, mean, pi, polyfit, polyval, sum
|
||||
from scipy.constants import c, h
|
||||
from collections import namedtuple
|
||||
|
||||
from gnpy.core.node import Node
|
||||
from gnpy.core.units import UNITS
|
||||
from gnpy.core.utils import lin2db, db2lin, itufs
|
||||
from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum
|
||||
from gnpy.core.science_utils import propagate_raman_fiber, _psi
|
||||
|
||||
class Transceiver(Node):
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -35,23 +36,50 @@ class Transceiver(Node):
|
||||
self.osnr_nli = None
|
||||
self.snr = None
|
||||
self.passive = False
|
||||
self.baud_rate = None
|
||||
|
||||
def _calc_snr(self, spectral_info):
|
||||
ase = [c.power.ase for c in spectral_info.carriers]
|
||||
nli = [c.power.nli for c in spectral_info.carriers]
|
||||
if min(ase) > 1e-20:
|
||||
self.osnr_ase = [lin2db(c.power.signal/c.power.ase)
|
||||
def _calc_snr(self, spectral_info):
|
||||
with errstate(divide='ignore'):
|
||||
self.baud_rate = [c.baud_rate for c in spectral_info.carriers]
|
||||
ratio_01nm = [lin2db(12.5e9/b_rate) for b_rate in self.baud_rate]
|
||||
#set raw values to record original calculation, before update_snr()
|
||||
self.raw_osnr_ase = [lin2db(divide(c.power.signal, c.power.ase))
|
||||
for c in spectral_info.carriers]
|
||||
self.raw_osnr_ase_01nm = [ase - ratio for ase, ratio
|
||||
in zip(self.raw_osnr_ase, ratio_01nm)]
|
||||
self.raw_osnr_nli = [lin2db(divide(c.power.signal, c.power.nli))
|
||||
for c in spectral_info.carriers]
|
||||
ratio_01nm = [lin2db(12.5e9/c.baud_rate)
|
||||
for c in spectral_info.carriers]
|
||||
self.osnr_ase_01nm = [ase - ratio for ase, ratio
|
||||
in zip(self.osnr_ase, ratio_01nm)]
|
||||
if min(nli) > 1e-20:
|
||||
self.osnr_nli = [lin2db(c.power.signal/c.power.nli)
|
||||
for c in spectral_info.carriers]
|
||||
self.snr = [lin2db(c.power.signal/(c.power.nli+c.power.ase))
|
||||
self.raw_snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase))
|
||||
for c in spectral_info.carriers]
|
||||
self.raw_snr_01nm = [snr - ratio for snr, ratio
|
||||
in zip(self.raw_snr, ratio_01nm)]
|
||||
|
||||
self.osnr_ase = self.raw_osnr_ase
|
||||
self.osnr_ase_01nm = self.raw_osnr_ase_01nm
|
||||
self.osnr_nli = self.raw_osnr_nli
|
||||
self.snr = self.raw_snr
|
||||
self.snr_01nm = self.raw_snr_01nm
|
||||
|
||||
def update_snr(self, *args):
|
||||
"""
|
||||
snr_added in 0.1nm
|
||||
compute SNR penalties such as transponder Tx_osnr or Roadm add_drop_osnr
|
||||
only applied in request.py / propagate on the last Trasceiver node of the path
|
||||
all penalties are added in a single call because to avoid uncontrolled cumul
|
||||
"""
|
||||
#use raw_values so that the added snr penalties are not cumulated
|
||||
snr_added = 0
|
||||
for s in args:
|
||||
snr_added += db2lin(-s)
|
||||
snr_added = -lin2db(snr_added)
|
||||
self.osnr_ase = list(map(lambda x,y:snr_sum(x,y,snr_added),
|
||||
self.raw_osnr_ase, self.baud_rate))
|
||||
self.snr = list(map(lambda x,y:snr_sum(x,y,snr_added),
|
||||
self.raw_snr, self.baud_rate))
|
||||
self.osnr_ase_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),
|
||||
self.raw_osnr_ase_01nm))
|
||||
self.snr_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),
|
||||
self.raw_snr_01nm))
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
@@ -77,38 +105,45 @@ class Transceiver(Node):
|
||||
snr = round(mean(self.snr),2)
|
||||
osnr_ase = round(mean(self.osnr_ase),2)
|
||||
osnr_ase_01nm = round(mean(self.osnr_ase_01nm), 2)
|
||||
snr_01nm = round(mean(self.snr_01nm),2)
|
||||
|
||||
return '\n'.join([f'{type(self).__name__} {self.uid}',
|
||||
|
||||
f' OSNR ASE (1nm): {osnr_ase_01nm:.2f}',
|
||||
f' OSNR ASE (signal bw): {osnr_ase:.2f}',
|
||||
f' SNR total (signal bw): {snr:.2f}'])
|
||||
|
||||
f' OSNR ASE (0.1nm, dB): {osnr_ase_01nm:.2f}',
|
||||
f' OSNR ASE (signal bw, dB): {osnr_ase:.2f}',
|
||||
f' SNR total (signal bw, dB): {snr:.2f}',
|
||||
f' SNR total (0.1nm, dB): {snr_01nm:.2f}'])
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
self._calc_snr(spectral_info)
|
||||
return spectral_info
|
||||
|
||||
RoadmParams = namedtuple('RoadmParams', 'loss')
|
||||
RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr restrictions per_degree_target_pch_out_db')
|
||||
|
||||
class Roadm(Node):
|
||||
def __init__(self, *args, params=None, **kwargs):
|
||||
if params is None:
|
||||
# default loss value if not mentioned in loaded network json
|
||||
params = {'loss':None}
|
||||
def __init__(self, *args, params, **kwargs):
|
||||
if 'per_degree_target_pch_out_db' not in params.keys():
|
||||
params['per_degree_target_pch_out_db'] = []
|
||||
super().__init__(*args, params=RoadmParams(**params), **kwargs)
|
||||
self.loss = self.params.loss
|
||||
self.pch_out = None
|
||||
self.loss = 0 #auto-design interest
|
||||
self.effective_loss = None
|
||||
self.effective_pch_out_db = self.params.target_pch_out_db
|
||||
self.passive = True
|
||||
self.restrictions = self.params.restrictions
|
||||
self.per_degree_target_pch_out_db = self.params.per_degree_target_pch_out_db
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'type' : type(self).__name__,
|
||||
'params' : {'loss' : self.loss},
|
||||
'params' : {
|
||||
'target_pch_out_db' : self.effective_pch_out_db,
|
||||
'restrictions' : self.restrictions,
|
||||
'per_degree_target_pch_out_db': self.per_degree_target_pch_out_db
|
||||
},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
@@ -116,27 +151,45 @@ class Roadm(Node):
|
||||
|
||||
def __str__(self):
|
||||
return '\n'.join([f'{type(self).__name__} {self.uid}',
|
||||
f' loss (dB): {self.loss:.2f}',
|
||||
f' pch out (dBm): {self.pch_out!r}'])
|
||||
f' effective loss (dB): {self.effective_loss:.2f}',
|
||||
f' pch out (dBm): {self.effective_pch_out_db!r}'])
|
||||
|
||||
def propagate(self, *carriers):
|
||||
attenuation = db2lin(self.loss)
|
||||
|
||||
for carrier in carriers:
|
||||
def propagate(self, pref, *carriers, degree):
|
||||
#pin_target and loss are read from eqpt_config.json['Roadm']
|
||||
#all ingress channels in xpress are set to this power level
|
||||
#but add channels are not, so we define an effective loss
|
||||
#in the case of add channels
|
||||
if self.per_degree_target_pch_out_db:
|
||||
# find the target power on this degree
|
||||
try:
|
||||
temp = next(el['target_pch_out_db'] \
|
||||
for el in self.per_degree_target_pch_out_db if el['to_node']==degree)
|
||||
except StopIteration:
|
||||
# if no target power is defined on this degree use the global one
|
||||
temp = self.params.target_pch_out_db
|
||||
else:
|
||||
# if no per degree target power are defined, use the global one
|
||||
temp = self.params.target_pch_out_db
|
||||
self.effective_pch_out_db = min(pref.p_spani, temp)
|
||||
self.effective_loss = pref.p_spani - self.effective_pch_out_db
|
||||
carriers_power = array([c.power.signal +c.power.nli+c.power.ase for c in carriers])
|
||||
carriers_att = list(map(lambda x : lin2db(x*1e3)-self.effective_pch_out_db, carriers_power))
|
||||
exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default = 0)
|
||||
carriers_att = list(map(lambda x: db2lin(x+exceeding_att), carriers_att))
|
||||
for carrier_att, carrier in zip(carriers_att, carriers) :
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal/attenuation,
|
||||
nonlinear_interference=pwr.nli/attenuation,
|
||||
amplified_spontaneous_emission=pwr.ase/attenuation)
|
||||
pwr = pwr._replace( signal = pwr.signal/carrier_att,
|
||||
nli = pwr.nli/carrier_att,
|
||||
ase = pwr.ase/carrier_att)
|
||||
yield carrier._replace(power=pwr)
|
||||
|
||||
def update_pref(self, pref):
|
||||
self.pch_out = round(pref.pi - self.loss, 2)
|
||||
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
|
||||
return pref._replace(p_span0=pref.p_span0, p_spani=self.effective_pch_out_db)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
def __call__(self, spectral_info, degree):
|
||||
carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers, degree=degree))
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
return spectral_info.update(carriers=carriers, pref=pref)
|
||||
return spectral_info._replace(carriers=carriers, pref=pref)
|
||||
|
||||
FusedParams = namedtuple('FusedParams', 'loss')
|
||||
|
||||
@@ -153,6 +206,9 @@ class Fused(Node):
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'type' : type(self).__name__,
|
||||
'params' :{
|
||||
'loss': self.loss
|
||||
},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
@@ -171,17 +227,17 @@ class Fused(Node):
|
||||
for carrier in carriers:
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal/attenuation,
|
||||
nonlinear_interference=pwr.nli/attenuation,
|
||||
amplified_spontaneous_emission=pwr.ase/attenuation)
|
||||
nli=pwr.nli/attenuation,
|
||||
ase=pwr.ase/attenuation)
|
||||
yield carrier._replace(power=pwr)
|
||||
|
||||
def update_pref(self, pref):
|
||||
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
|
||||
return pref._replace(p_span0=pref.p_span0, p_spani=pref.p_spani - self.loss)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
return spectral_info.update(carriers=carriers, pref=pref)
|
||||
return spectral_info._replace(carriers=carriers, pref=pref)
|
||||
|
||||
FiberParams = namedtuple('FiberParams', 'type_variety length loss_coef length_units \
|
||||
att_in con_in con_out dispersion gamma')
|
||||
@@ -210,7 +266,9 @@ class Fiber(Node):
|
||||
self.con_out = self.params.con_out
|
||||
self.dispersion = self.params.dispersion # s/m/m
|
||||
self.gamma = self.params.gamma # 1/W/m
|
||||
self.pch_out = None
|
||||
self.pch_out_db = None
|
||||
self.carriers_in = None
|
||||
self.carriers_out = None
|
||||
# TODO|jla: discuss factor 2 in the linear lineic attenuation
|
||||
|
||||
@property
|
||||
@@ -243,16 +301,17 @@ class Fiber(Node):
|
||||
f' pad att_in (dB): {self.att_in:.2f}',
|
||||
f' total loss (dB): {self.loss:.2f}',
|
||||
f' (includes conn loss (dB) in: {self.con_in:.2f} out: {self.con_out:.2f})',
|
||||
f' (conn loss out includes EOL margin defined in eqpt_config.json)'])
|
||||
f' (conn loss out includes EOL margin defined in eqpt_config.json)',
|
||||
f' pch out (dBm): {self.pch_out_db!r}'])
|
||||
|
||||
@property
|
||||
def fiber_loss(self):
|
||||
# dB fiber loss, not including padding attenuator
|
||||
"""Fiber loss in dB, not including padding attenuator"""
|
||||
return self.loss_coef * self.length + self.con_in + self.con_out
|
||||
|
||||
@property
|
||||
def loss(self):
|
||||
#total loss incluiding padding att_in: useful for polymorphism with roadm loss
|
||||
"""total loss including padding att_in: useful for polymorphism with roadm loss"""
|
||||
return self.loss_coef * self.length + self.con_in + self.con_out + self.att_in
|
||||
|
||||
@property
|
||||
@@ -275,46 +334,46 @@ class Fiber(Node):
|
||||
aleff = 1 / (2 * alpha)
|
||||
return aleff
|
||||
|
||||
def beta2(self, ref_wavelength=None):
|
||||
""" Returns beta2 from dispersion parameter.
|
||||
def carriers(self, loc, attr):
|
||||
"""retrieve carriers information
|
||||
|
||||
:param loc: (in, out) of the class element
|
||||
:param attr: (ase, nli, signal, total) power information
|
||||
"""
|
||||
if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')):
|
||||
yield None
|
||||
return
|
||||
loc_attr = 'carriers_'+loc
|
||||
for c in getattr(self, loc_attr) :
|
||||
if attr == 'total':
|
||||
yield c.power.ase+c.power.nli+c.power.signal
|
||||
else:
|
||||
yield c.power._asdict().get(attr, None)
|
||||
|
||||
def beta2(self, ref_wavelength=1550e-9):
|
||||
"""Returns beta2 from dispersion parameter.
|
||||
Dispersion is entered in ps/nm/km.
|
||||
Disperion can be a numpy array or a single value. If a
|
||||
value ref_wavelength is not entered 1550e-9m will be assumed.
|
||||
ref_wavelength can be a numpy array.
|
||||
Disperion can be a numpy array or a single value.
|
||||
|
||||
:param ref_wavelength: can be a numpy array; default: 1550nm
|
||||
"""
|
||||
# TODO|jla: discuss beta2 as method or attribute
|
||||
wl = 1550e-9 if ref_wavelength is None else ref_wavelength
|
||||
D = abs(self.dispersion)
|
||||
b2 = (wl ** 2) * D / (2 * pi * c) # 10^21 scales [ps^2/km]
|
||||
b2 = (ref_wavelength ** 2) * D / (2 * pi * c) # 10^21 scales [ps^2/km]
|
||||
return b2 # s/Hz/m
|
||||
|
||||
def dbkm_2_lin(self):
|
||||
""" calculates the linear loss coefficient
|
||||
"""
|
||||
# alpha_pcoef is linear loss coefficient in dB/km^-1
|
||||
# alpha_acoef is linear loss field amplitude coefficient in m^-1
|
||||
"""calculates the linear loss coefficient"""
|
||||
# linear loss coefficient in dB/km^-1
|
||||
alpha_pcoef = self.loss_coef
|
||||
# linear loss field amplitude coefficient in m^-1
|
||||
alpha_acoef = alpha_pcoef / (2 * 10 * log10(exp(1)))
|
||||
return alpha_pcoef, alpha_acoef
|
||||
|
||||
def _psi(self, carrier, interfering_carrier):
|
||||
""" Calculates eq. 123 from arXiv:1209.0394.
|
||||
"""
|
||||
if carrier.num_chan == interfering_carrier.num_chan: # SCI
|
||||
psi = arcsinh(0.5 * pi**2 * self.asymptotic_length
|
||||
* abs(self.beta2()) * carrier.baud_rate**2)
|
||||
else: # XCI
|
||||
delta_f = carrier.freq - interfering_carrier.freq
|
||||
psi = arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
|
||||
* carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
|
||||
psi -= arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
|
||||
* carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
|
||||
|
||||
return psi
|
||||
|
||||
def _gn_analytic(self, carrier, *carriers):
|
||||
""" Computes the nonlinear interference power on a single carrier.
|
||||
The method uses eq. 120 from arXiv:1209.0394.
|
||||
"""Computes the nonlinear interference power on a single carrier.
|
||||
The method uses eq. 120 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__.
|
||||
|
||||
:param carrier: the signal under analysis
|
||||
:param carriers: the full WDM comb
|
||||
:return: carrier_nli: the amount of nonlinear interference in W on the under analysis
|
||||
@@ -322,7 +381,7 @@ class Fiber(Node):
|
||||
|
||||
g_nli = 0
|
||||
for interfering_carrier in carriers:
|
||||
psi = self._psi(carrier, interfering_carrier)
|
||||
psi = _psi(carrier, interfering_carrier, beta2=self.beta2(), asymptotic_length=self.asymptotic_length)
|
||||
g_nli += (interfering_carrier.power.signal/interfering_carrier.baud_rate)**2 \
|
||||
* (carrier.power.signal/carrier.baud_rate) * psi
|
||||
|
||||
@@ -341,8 +400,8 @@ class Fiber(Node):
|
||||
for carrier in carriers:
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal/attenuation,
|
||||
nonlinear_interference=pwr.nli/attenuation,
|
||||
amplified_spontaneous_emission=pwr.ase/attenuation)
|
||||
nli=pwr.nli/attenuation,
|
||||
ase=pwr.ase/attenuation)
|
||||
carrier = carrier._replace(power=pwr)
|
||||
chan.append(carrier)
|
||||
|
||||
@@ -354,18 +413,77 @@ class Fiber(Node):
|
||||
pwr = carrier.power
|
||||
carrier_nli = self._gn_analytic(carrier, *carriers)
|
||||
pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation/attenuation,
|
||||
nonlinear_interference=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation,
|
||||
amplified_spontaneous_emission=pwr.ase/self.lin_attenuation/attenuation)
|
||||
nli=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation,
|
||||
ase=pwr.ase/self.lin_attenuation/attenuation)
|
||||
yield carrier._replace(power=pwr)
|
||||
|
||||
def update_pref(self, pref):
|
||||
self.pch_out = round(pref.pi - self.loss, 2)
|
||||
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
|
||||
self.pch_out_db = round(pref.p_spani - self.loss, 2)
|
||||
return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
self.carriers_in = spectral_info.carriers
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
return spectral_info.update(carriers=carriers, pref=pref)
|
||||
self.carriers_out = carriers
|
||||
return spectral_info._replace(carriers=carriers, pref=pref)
|
||||
|
||||
RamanFiberParams = namedtuple('RamanFiberParams', 'type_variety length loss_coef length_units \
|
||||
att_in con_in con_out dispersion gamma raman_efficiency')
|
||||
|
||||
class RamanFiber(Fiber):
|
||||
def __init__(self, *args, params=None, **kwargs):
|
||||
if params is None:
|
||||
params = {}
|
||||
if 'con_in' not in params:
|
||||
# if not defined in the network json connector loss in/out
|
||||
# the None value will be updated in network.py[build_network]
|
||||
# with default values from eqpt_config.json[Spans]
|
||||
params['con_in'] = None
|
||||
params['con_out'] = None
|
||||
if 'att_in' not in params:
|
||||
#fixed attenuator for padding
|
||||
params['att_in'] = 0
|
||||
|
||||
# TODO: can we re-use the Fiber constructor in a better way?
|
||||
Node.__init__(self, *args, params=RamanFiberParams(**params), **kwargs)
|
||||
self.type_variety = self.params.type_variety
|
||||
self.length = self.params.length * UNITS[self.params.length_units] # in m
|
||||
self.loss_coef = self.params.loss_coef * 1e-3 # lineic loss dB/m
|
||||
self.lin_loss_coef = self.params.loss_coef / (20 * log10(exp(1)))
|
||||
self.att_in = self.params.att_in
|
||||
self.con_in = self.params.con_in
|
||||
self.con_out = self.params.con_out
|
||||
self.dispersion = self.params.dispersion # s/m/m
|
||||
self.gamma = self.params.gamma # 1/W/m
|
||||
self.pch_out_db = None
|
||||
self.carriers_in = None
|
||||
self.carriers_out = None
|
||||
# TODO|jla: discuss factor 2 in the linear lineic attenuation
|
||||
|
||||
@property
|
||||
def sim_params(self):
|
||||
return self._sim_params
|
||||
|
||||
@sim_params.setter
|
||||
def sim_params(self, sim_params=None):
|
||||
self._sim_params = sim_params
|
||||
|
||||
def update_pref(self, pref, *carriers):
|
||||
pch_out_db = lin2db(mean([carrier.power.signal for carrier in carriers])) + 30
|
||||
self.pch_out_db = round(pch_out_db, 2)
|
||||
return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
self.carriers_in = spectral_info.carriers
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
pref = self.update_pref(spectral_info.pref, *carriers)
|
||||
self.carriers_out = carriers
|
||||
return spectral_info._replace(carriers=carriers, pref=pref)
|
||||
|
||||
def propagate(self, *carriers):
|
||||
for propagated_carrier in propagate_raman_fiber(self, *carriers):
|
||||
yield propagated_carrier
|
||||
|
||||
class EdfaParams:
|
||||
def __init__(self, **params):
|
||||
@@ -373,16 +491,16 @@ class EdfaParams:
|
||||
if params == {}:
|
||||
self.type_variety = ''
|
||||
self.type_def = ''
|
||||
self.gain_flatmax = 0
|
||||
self.gain_min = 0
|
||||
self.p_max = 0
|
||||
self.nf_model = None
|
||||
self.nf_fit_coeff = None
|
||||
self.nf_ripple = None
|
||||
self.dgt = None
|
||||
self.gain_ripple = None
|
||||
self.out_voa_auto = False
|
||||
self.allowed_for_design = None
|
||||
# self.gain_flatmax = 0
|
||||
# self.gain_min = 0
|
||||
# self.p_max = 0
|
||||
# self.nf_model = None
|
||||
# self.nf_fit_coeff = None
|
||||
# self.nf_ripple = None
|
||||
# self.dgt = None
|
||||
# self.gain_ripple = None
|
||||
# self.out_voa_auto = False
|
||||
# self.allowed_for_design = None
|
||||
|
||||
def update_params(self, kwargs):
|
||||
for k,v in kwargs.items() :
|
||||
@@ -390,22 +508,33 @@ class EdfaParams:
|
||||
if isinstance(v, dict) else v)
|
||||
|
||||
class EdfaOperational:
|
||||
def __init__(self, gain_target, tilt_target, out_voa=None):
|
||||
self.gain_target = gain_target
|
||||
self.tilt_target = tilt_target
|
||||
self.out_voa = out_voa
|
||||
default_values = \
|
||||
{
|
||||
'gain_target': None,
|
||||
'delta_p': None,
|
||||
'out_voa': None,
|
||||
'tilt_target': 0
|
||||
}
|
||||
|
||||
def __init__(self, **operational):
|
||||
self.update_attr(operational)
|
||||
|
||||
def update_attr(self, kwargs):
|
||||
clean_kwargs = {k:v for k,v in kwargs.items() if v !=''}
|
||||
for k,v in self.default_values.items():
|
||||
setattr(self, k, clean_kwargs.get(k,v))
|
||||
|
||||
def __repr__(self):
|
||||
return (f'{type(self).__name__}('
|
||||
f'gain_target={self.gain_target!r}, '
|
||||
f'tilt_target={self.tilt_target!r})')
|
||||
|
||||
class Edfa(Node):
|
||||
def __init__(self, *args, params={}, operational={}, **kwargs):
|
||||
#TBC is this useful? put in comment for now:
|
||||
#if params is None:
|
||||
# params = {}
|
||||
#if operational is None:
|
||||
# operational = {}
|
||||
def __init__(self, *args, params=None, operational=None, **kwargs):
|
||||
if params is None:
|
||||
params = {}
|
||||
if operational is None:
|
||||
operational = {}
|
||||
super().__init__(
|
||||
*args,
|
||||
params=EdfaParams(**params),
|
||||
@@ -420,13 +549,18 @@ class Edfa(Node):
|
||||
self.nf = None # dB edfa nf at operational.gain_target
|
||||
self.gprofile = None
|
||||
self.pin_db = None
|
||||
self.nch = None
|
||||
self.pout_db = None
|
||||
self.dp_db = None #delta P with Pref (power swwep) in power mode
|
||||
self.target_pch_db = None
|
||||
self.effective_pch_db = None
|
||||
self.target_pch_out_db = None
|
||||
self.effective_pch_out_db = None
|
||||
self.passive = False
|
||||
self.effective_gain = self.operational.gain_target
|
||||
self.att_in = None
|
||||
self.carriers_in = None
|
||||
self.carriers_out = None
|
||||
self.effective_gain = self.operational.gain_target
|
||||
self.delta_p = self.operational.delta_p #delta P with Pref (power swwep) in power mode
|
||||
self.tilt_target = self.operational.tilt_target
|
||||
self.out_voa = self.operational.out_voa
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
@@ -434,9 +568,10 @@ class Edfa(Node):
|
||||
'type' : type(self).__name__,
|
||||
'type_variety' : self.params.type_variety,
|
||||
'operational' : {
|
||||
'gain_target' : self.operational.gain_target,
|
||||
'tilt_target' : self.operational.tilt_target,
|
||||
'out_voa' : self.operational.out_voa
|
||||
'gain_target' : self.effective_gain,
|
||||
'delta_p' : self.delta_p,
|
||||
'tilt_target' : self.tilt_target,
|
||||
'out_voa' : self.out_voa
|
||||
},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
@@ -445,7 +580,7 @@ class Edfa(Node):
|
||||
|
||||
def __repr__(self):
|
||||
return (f'{type(self).__name__}(uid={self.uid!r}, '
|
||||
f'type_variety={self.params.type_variety!r}'
|
||||
f'type_variety={self.params.type_variety!r}, '
|
||||
f'interpol_dgt={self.interpol_dgt!r}, '
|
||||
f'interpol_gain_ripple={self.interpol_gain_ripple!r}, '
|
||||
f'interpol_nf_ripple={self.interpol_nf_ripple!r}, '
|
||||
@@ -468,66 +603,128 @@ class Edfa(Node):
|
||||
f' pad att_in (dB): {self.att_in:.2f}',
|
||||
f' Power In (dBm): {self.pin_db:.2f}',
|
||||
f' Power Out (dBm): {self.pout_db:.2f}',
|
||||
f' Delta_P (dB): {self.dp_db!r}',
|
||||
f' target pch (dBm): {self.target_pch_db!r}',
|
||||
f' effective pch (dBm): {self.effective_pch_db!r}',
|
||||
f' output VOA (dB): {self.operational.out_voa:.2f}'])
|
||||
f' Delta_P (dB): {self.delta_p!r}',
|
||||
f' target pch (dBm): {self.target_pch_out_db!r}',
|
||||
f' effective pch (dBm): {self.effective_pch_out_db!r}',
|
||||
f' output VOA (dB): {self.out_voa:.2f}'])
|
||||
|
||||
def carriers(self, loc, attr):
|
||||
"""retrieve carriers information
|
||||
|
||||
:param loc: (in, out) of the class element
|
||||
:param attr: (ase, nli, signal, total) power information
|
||||
"""
|
||||
if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')):
|
||||
yield None
|
||||
return
|
||||
loc_attr = 'carriers_'+loc
|
||||
for c in getattr(self, loc_attr) :
|
||||
if attr == 'total':
|
||||
yield c.power.ase+c.power.nli+c.power.signal
|
||||
else:
|
||||
yield c.power._asdict().get(attr, None)
|
||||
|
||||
def interpol_params(self, frequencies, pin, baud_rates, pref):
|
||||
"""interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from json
|
||||
"""interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from JSON
|
||||
set the edfa class __init__ None parameters :
|
||||
self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple
|
||||
"""
|
||||
# TODO|jla: read amplifier actual frequencies from additional params in json
|
||||
amplifier_freq = itufs(0.05) * 1e12 # Hz
|
||||
amplifier_freq = arrange_frequencies(len(self.params.dgt), self.params.f_min, self.params.f_max) # Hz
|
||||
self.channel_freq = frequencies
|
||||
self.interpol_dgt = interp(self.channel_freq, amplifier_freq, self.params.dgt)
|
||||
|
||||
self.interpol_gain_ripple = interp(self.channel_freq, amplifier_freq, self.params.gain_ripple)
|
||||
self.interpol_nf_ripple =interp(self.channel_freq, amplifier_freq, self.params.nf_ripple)
|
||||
|
||||
self.nch = frequencies.size
|
||||
self.pin_db = lin2db(sum(pin*1e3))
|
||||
"""check power saturation and correct target_gain accordingly:"""
|
||||
|
||||
if self.dp_db is not None:
|
||||
self.target_pch_db = round(self.dp_db + pref.p0, 2)
|
||||
self.effective_gain = self.target_pch_db - pref.pi
|
||||
|
||||
self.effective_gain = min(self.effective_gain, self.params.p_max - self.pin_db)
|
||||
self.effective_pch_db = round(pref.pi + self.effective_gain, 2)
|
||||
"""in power mode: delta_p is defined and can be used to calculate the power target
|
||||
This power target is used calculate the amplifier gain"""
|
||||
if self.delta_p is not None:
|
||||
self.target_pch_out_db = round(self.delta_p + pref.p_span0, 2)
|
||||
self.effective_gain = self.target_pch_out_db - pref.p_spani
|
||||
|
||||
"""check power saturation and correct effective gain & power accordingly:"""
|
||||
self.effective_gain = min(
|
||||
self.effective_gain,
|
||||
self.params.p_max - (pref.p_spani + pref.neq_ch)
|
||||
)
|
||||
#print(self.uid, self.effective_gain, self.operational.gain_target)
|
||||
self.effective_pch_out_db = round(pref.p_spani + self.effective_gain, 2)
|
||||
|
||||
"""check power saturation and correct target_gain accordingly:"""
|
||||
#print(self.uid, self.effective_gain, self.pin_db, pref.p_spani)
|
||||
self.nf = self._calc_nf()
|
||||
self.gprofile = self._gain_profile(pin)
|
||||
|
||||
pout = (pin + self.noise_profile(baud_rates))*db2lin(self.gprofile)
|
||||
self.pout_db = lin2db(sum(pout*1e3))
|
||||
self.operational.gain_target = self.effective_gain
|
||||
# ase & nli are only calculated in signal bandwidth
|
||||
# pout_db is not the absolute full output power (negligible if sufficient channels)
|
||||
|
||||
def _nf(self, type_def, nf_model, nf_fit_coeff, gain_min, gain_flatmax, gain_target):
|
||||
#if hybrid raman, use edfa_gain_flatmax attribute, else use gain_flatmax
|
||||
#gain_flatmax = getattr(params, 'edfa_gain_flatmax', params.gain_flatmax)
|
||||
pad = max(gain_min - gain_target, 0)
|
||||
gain_target += pad
|
||||
dg = max(gain_flatmax - gain_target, 0)
|
||||
if type_def == 'variable_gain':
|
||||
g1a = gain_target - nf_model.delta_p - dg
|
||||
nf_avg = lin2db(db2lin(nf_model.nf1) + db2lin(nf_model.nf2)/db2lin(g1a))
|
||||
elif type_def == 'fixed_gain':
|
||||
nf_avg = nf_model.nf0
|
||||
elif type_def == 'openroadm':
|
||||
pin_ch = self.pin_db - lin2db(self.nch)
|
||||
# model OSNR = f(Pin)
|
||||
nf_avg = pin_ch - polyval(nf_model.nf_coef, pin_ch) + 58
|
||||
elif type_def == 'advanced_model':
|
||||
nf_avg = polyval(nf_fit_coeff, -dg)
|
||||
else:
|
||||
assert False, "Unrecognized amplifier type, this should have been checked by the JSON loader"
|
||||
return nf_avg+pad, pad
|
||||
|
||||
def _calc_nf(self, avg = False):
|
||||
"""nf calculation based on 2 models: self.params.nf_model.enabled from json import:
|
||||
True => 2 stages amp modelling based on precalculated nf1, nf2 and delta_p in build_OA_json
|
||||
False => polynomial fit based on self.params.nf_fit_coeff"""
|
||||
# TODO|jla: TBD alarm rising or input VOA padding in case
|
||||
# gain_min > gain_target TBD:
|
||||
pad = max(self.params.gain_min - self.effective_gain, 0)
|
||||
self.att_in = pad
|
||||
gain_target = self.effective_gain + pad
|
||||
dg = max(self.params.gain_flatmax - gain_target, 0)
|
||||
if self.params.type_def == 'variable_gain':
|
||||
g1a = gain_target - self.params.nf_model.delta_p - dg
|
||||
nf_avg = lin2db(db2lin(self.params.nf_model.nf1) + db2lin(self.params.nf_model.nf2)/db2lin(g1a))
|
||||
elif self.params.type_def == 'fixed_gain':
|
||||
nf_avg = self.params.nf_model.nf0
|
||||
else:
|
||||
nf_avg = polyval(self.params.nf_fit_coeff, -dg)
|
||||
if self.params.type_def == 'dual_stage':
|
||||
g1 = self.params.preamp_gain_flatmax
|
||||
g2 = self.effective_gain - g1
|
||||
nf1_avg, pad = self._nf( self.params.preamp_type_def,
|
||||
self.params.preamp_nf_model,
|
||||
self.params.preamp_nf_fit_coeff,
|
||||
self.params.preamp_gain_min,
|
||||
self.params.preamp_gain_flatmax,
|
||||
g1)
|
||||
#no padding expected for the 1stage because g1 = gain_max
|
||||
nf2_avg, pad = self._nf( self.params.booster_type_def,
|
||||
self.params.booster_nf_model,
|
||||
self.params.booster_nf_fit_coeff,
|
||||
self.params.booster_gain_min,
|
||||
self.params.booster_gain_flatmax,
|
||||
g2)
|
||||
nf_avg = lin2db(db2lin(nf1_avg) + db2lin(nf2_avg-g1))
|
||||
#no padding expected for the 1stage because g1 = gain_max
|
||||
pad = 0
|
||||
else:
|
||||
nf_avg, pad = self._nf( self.params.type_def,
|
||||
self.params.nf_model,
|
||||
self.params.nf_fit_coeff,
|
||||
self.params.gain_min,
|
||||
self.params.gain_flatmax,
|
||||
self.effective_gain)
|
||||
|
||||
self.att_in = pad # not used to attenuate carriers, only used in _repr_ and _str_
|
||||
if avg:
|
||||
return nf_avg + pad
|
||||
return nf_avg
|
||||
else:
|
||||
return self.interpol_nf_ripple + nf_avg + pad # input VOA = 1 for 1 NF degradation
|
||||
return self.interpol_nf_ripple + nf_avg # input VOA = 1 for 1 NF degradation
|
||||
|
||||
def noise_profile(self, df):
|
||||
""" noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
|
||||
"""noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
|
||||
noise is calculated at amplifier input
|
||||
|
||||
:bw: signal bandwidth = baud rate in Hz
|
||||
@@ -617,7 +814,7 @@ class Edfa(Node):
|
||||
|
||||
# Calculate the target slope - currently assumes equal spaced channels
|
||||
# TODO|jla: support arbitrary channel spacing
|
||||
targ_slope = self.operational.tilt_target / (len(nb_channel) - 1)
|
||||
targ_slope = self.tilt_target / (len(nb_channel) - 1)
|
||||
|
||||
# first estimate of DGT scaling
|
||||
if abs(dgt_slope) > 0.001: # check for zero value due to flat dgt
|
||||
@@ -682,7 +879,7 @@ class Edfa(Node):
|
||||
return g1st - voa + array(self.interpol_dgt) * dgts3
|
||||
|
||||
def propagate(self, pref, *carriers):
|
||||
"""add ase noise to the propagating carriers of SpectralInformation"""
|
||||
"""add ASE noise to the propagating carriers of :class:`.info.SpectralInformation`"""
|
||||
pin = array([c.power.signal+c.power.nli+c.power.ase for c in carriers]) # pin in W
|
||||
freq = array([c.frequency for c in carriers])
|
||||
brate = array([c.baud_rate for c in carriers])
|
||||
@@ -691,20 +888,22 @@ class Edfa(Node):
|
||||
|
||||
gains = db2lin(self.gprofile)
|
||||
carrier_ases = self.noise_profile(brate)
|
||||
att = db2lin(self.operational.out_voa)
|
||||
att = db2lin(self.out_voa)
|
||||
|
||||
for gain, carrier_ase, carrier in zip(gains, carrier_ases, carriers):
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal*gain/att,
|
||||
nonlinear_interference=pwr.nli*gain/att,
|
||||
amplified_spontaneous_emission=(pwr.ase+carrier_ase)*gain/att)
|
||||
nli=pwr.nli*gain/att,
|
||||
ase=(pwr.ase+carrier_ase)*gain/att)
|
||||
yield carrier._replace(power=pwr)
|
||||
|
||||
def update_pref(self, pref):
|
||||
return pref._replace(p_span0=pref.p0,
|
||||
p_spani=pref.pi + self.effective_gain - self.operational.out_voa)
|
||||
return pref._replace(p_span0=pref.p_span0,
|
||||
p_spani=pref.p_spani + self.effective_gain - self.out_voa)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
self.carriers_in = spectral_info.carriers
|
||||
carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers))
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
return spectral_info.update(carriers=carriers, pref=pref)
|
||||
self.carriers_out = carriers
|
||||
return spectral_info._replace(carriers=carriers, pref=pref)
|
||||
|
||||
@@ -9,86 +9,207 @@ This module contains functionality for specifying equipment.
|
||||
'''
|
||||
|
||||
from numpy import clip, polyval
|
||||
from sys import exit
|
||||
from operator import itemgetter
|
||||
from math import isclose
|
||||
from pathlib import Path
|
||||
from json import loads
|
||||
from json import load
|
||||
from gnpy.core.utils import lin2db, db2lin, load_json
|
||||
from collections import namedtuple
|
||||
from gnpy.core.elements import Edfa
|
||||
from gnpy.core.exceptions import EquipmentConfigError
|
||||
import time
|
||||
|
||||
Model_vg = namedtuple('Model_vg', 'nf1 nf2 delta_p')
|
||||
Model_fg = namedtuple('Model_fg', 'nf0')
|
||||
Fiber = namedtuple('Fiber', 'type_variety dispersion gamma')
|
||||
Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \
|
||||
max_loss padding EOL con_in con_out')
|
||||
Transceiver = namedtuple('Transceiver', 'type_variety frequency mode')
|
||||
Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pref')
|
||||
SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \
|
||||
power_dbm power_range_db OSNR bit_rate')
|
||||
AmpBase = namedtuple(
|
||||
'AmpBase',
|
||||
'type_variety type_def gain_flatmax gain_min p_max'
|
||||
' nf_model nf_fit_coeff nf_ripple dgt gain_ripple out_voa_auto allowed_for_design')
|
||||
class Amp(AmpBase):
|
||||
def __new__(cls,
|
||||
type_variety, type_def, gain_flatmax, gain_min, p_max, nf_model=None,
|
||||
nf_fit_coeff=None, nf_ripple=None, dgt=None, gain_ripple=None,
|
||||
out_voa_auto=False, allowed_for_design=True):
|
||||
return super().__new__(cls,
|
||||
type_variety, type_def, gain_flatmax, gain_min, p_max,
|
||||
nf_model, nf_fit_coeff, nf_ripple, dgt, gain_ripple,
|
||||
out_voa_auto, allowed_for_design)
|
||||
Model_openroadm = namedtuple('Model_openroadm', 'nf_coef')
|
||||
Model_hybrid = namedtuple('Model_hybrid', 'nf_ram gain_ram edfa_variety')
|
||||
Model_dual_stage = namedtuple('Model_dual_stage', 'preamp_variety booster_variety')
|
||||
|
||||
class common:
|
||||
def update_attr(self, default_values, kwargs, name):
|
||||
clean_kwargs = {k:v for k, v in kwargs.items() if v != ''}
|
||||
for k, v in default_values.items():
|
||||
setattr(self, k, clean_kwargs.get(k, v))
|
||||
if k not in clean_kwargs and name != 'Amp':
|
||||
print(f'\x1b[1;31;40m'+
|
||||
f'\n WARNING missing {k} attribute in eqpt_config.json[{name}]'+
|
||||
f'\n default value is {k} = {v}'+
|
||||
f'\x1b[0m')
|
||||
time.sleep(1)
|
||||
|
||||
class SI(common):
|
||||
default_values =\
|
||||
{
|
||||
"f_min": 191.35e12,
|
||||
"f_max": 196.1e12,
|
||||
"baud_rate": 32e9,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0, 0, 0.5],
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 45,
|
||||
"sys_margins": 0
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'SI')
|
||||
|
||||
class Span(common):
|
||||
default_values = \
|
||||
{
|
||||
'power_mode': True,
|
||||
'delta_power_range_db': None,
|
||||
'max_fiber_lineic_loss_for_raman': 0.25,
|
||||
'target_extended_gain': 2.5,
|
||||
'max_length': 150,
|
||||
'length_units': 'km',
|
||||
'max_loss': None,
|
||||
'padding': 10,
|
||||
'EOL': 0,
|
||||
'con_in': 0,
|
||||
'con_out': 0
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Span')
|
||||
|
||||
class Roadm(common):
|
||||
default_values = \
|
||||
{
|
||||
'target_pch_out_db': -17,
|
||||
'add_drop_osnr': 100,
|
||||
'restrictions': {
|
||||
'preamp_variety_list':[],
|
||||
'booster_variety_list':[]
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Roadm')
|
||||
|
||||
class Transceiver(common):
|
||||
default_values = \
|
||||
{
|
||||
'type_variety': None,
|
||||
'frequency': None,
|
||||
'mode': {}
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Transceiver')
|
||||
|
||||
class Fiber(common):
|
||||
default_values = \
|
||||
{
|
||||
'type_variety': '',
|
||||
'dispersion': None,
|
||||
'gamma': 0
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Fiber')
|
||||
|
||||
class RamanFiber(common):
|
||||
default_values = \
|
||||
{
|
||||
'type_variety': '',
|
||||
'dispersion': None,
|
||||
'gamma': 0,
|
||||
'raman_efficiency': None
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'RamanFiber')
|
||||
for param in ('cr', 'frequency_offset'):
|
||||
if param not in self.raman_efficiency:
|
||||
raise EquipmentConfigError(f'RamanFiber.raman_efficiency: missing "{param}" parameter')
|
||||
if self.raman_efficiency['frequency_offset'] != sorted(self.raman_efficiency['frequency_offset']):
|
||||
raise EquipmentConfigError(f'RamanFiber.raman_efficiency.frequency_offset is not sorted')
|
||||
|
||||
class Amp(common):
|
||||
default_values = \
|
||||
{
|
||||
'f_min': 191.35e12,
|
||||
'f_max': 196.1e12,
|
||||
'type_variety': '',
|
||||
'type_def': '',
|
||||
'gain_flatmax': None,
|
||||
'gain_min': None,
|
||||
'p_max': None,
|
||||
'nf_model': None,
|
||||
'dual_stage_model': None,
|
||||
'nf_fit_coeff': None,
|
||||
'nf_ripple': None,
|
||||
'dgt': None,
|
||||
'gain_ripple': None,
|
||||
'out_voa_auto': False,
|
||||
'allowed_for_design': False,
|
||||
'raman': False
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Amp')
|
||||
|
||||
@classmethod
|
||||
def from_advanced_json(cls, filename, **kwargs):
|
||||
with open(filename) as f:
|
||||
json_data = loads(f.read())
|
||||
return cls(**{**kwargs, **json_data, 'type_def':None, 'nf_model':None})
|
||||
def from_json(cls, filename, **kwargs):
|
||||
config = Path(filename).parent / 'default_edfa_config.json'
|
||||
|
||||
@classmethod
|
||||
def from_default_json(cls, filename, **kwargs):
|
||||
with open(filename) as f:
|
||||
json_data = loads(f.read())
|
||||
type_variety = kwargs['type_variety']
|
||||
type_def = kwargs.get('type_def', 'variable_gain') #default compatibility with older json eqpt files
|
||||
type_def = kwargs.get('type_def', 'variable_gain') # default compatibility with older json eqpt files
|
||||
nf_def = None
|
||||
dual_stage_def = None
|
||||
|
||||
if type_def == 'fixed_gain':
|
||||
try:
|
||||
nf0 = kwargs.pop('nf0')
|
||||
except KeyError: #nf0 is expected for a fixed gain amp
|
||||
print(f'missing nf0 value input for amplifier: {type_variety} in eqpt_config.json')
|
||||
exit()
|
||||
try: #remove all remaining nf inputs
|
||||
del kwargs['nf_min']
|
||||
del kwargs['nf_max']
|
||||
except KeyError: pass #nf_min and nf_max are not needed for fixed gain amp
|
||||
raise EquipmentConfigError(f'missing nf0 value input for amplifier: {type_variety} in equipment config')
|
||||
for k in ('nf_min', 'nf_max'):
|
||||
try:
|
||||
del kwargs[k]
|
||||
except KeyError:
|
||||
pass
|
||||
nf_def = Model_fg(nf0)
|
||||
elif type_def == 'advanced_model':
|
||||
config = Path(filename).parent / kwargs.pop('advanced_config_from_json')
|
||||
elif type_def == 'variable_gain':
|
||||
gain_min, gain_max = kwargs['gain_min'], kwargs['gain_flatmax']
|
||||
try: #nf_min and nf_max are expected for a variable gain amp
|
||||
nf_min = kwargs.pop('nf_min')
|
||||
nf_max = kwargs.pop('nf_max')
|
||||
except KeyError:
|
||||
print(f'missing nf_min/max value input for amplifier: {type_variety} in eqpt_config.json')
|
||||
exit()
|
||||
raise EquipmentConfigError(f'missing nf_min or nf_max value input for amplifier: {type_variety} in equipment config')
|
||||
try: #remove all remaining nf inputs
|
||||
del kwargs['nf0']
|
||||
except KeyError: pass #nf0 is not needed for variable gain amp
|
||||
nf1, nf2, delta_p = nf_model(type_variety, gain_min, gain_max, nf_min, nf_max)
|
||||
nf_def = Model_vg(nf1, nf2, delta_p)
|
||||
return cls(**{**kwargs, **json_data, 'nf_model': nf_def})
|
||||
elif type_def == 'openroadm':
|
||||
try:
|
||||
nf_coef = kwargs.pop('nf_coef')
|
||||
except KeyError: #nf_coef is expected for openroadm amp
|
||||
raise EquipmentConfigError(f'missing nf_coef input for amplifier: {type_variety} in equipment config')
|
||||
nf_def = Model_openroadm(nf_coef)
|
||||
elif type_def == 'dual_stage':
|
||||
try: #nf_ram and gain_ram are expected for a hybrid amp
|
||||
preamp_variety = kwargs.pop('preamp_variety')
|
||||
booster_variety = kwargs.pop('booster_variety')
|
||||
except KeyError:
|
||||
raise EquipmentConfigError(f'missing preamp/booster variety input for amplifier: {type_variety} in equipment config')
|
||||
dual_stage_def = Model_dual_stage(preamp_variety, booster_variety)
|
||||
|
||||
with open(config, encoding='utf-8') as f:
|
||||
json_data = load(f)
|
||||
|
||||
return cls(**{**kwargs, **json_data,
|
||||
'nf_model': nf_def, 'dual_stage_model': dual_stage_def})
|
||||
|
||||
|
||||
def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
|
||||
if nf_min < -10:
|
||||
print(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}')
|
||||
exit()
|
||||
raise EquipmentConfigError(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}')
|
||||
if nf_max < -10:
|
||||
print(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}')
|
||||
exit()
|
||||
raise EquipmentConfigError(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}')
|
||||
|
||||
# NF estimation model based on nf_min and nf_max
|
||||
# delta_p: max power dB difference between first and second stage coils
|
||||
@@ -103,8 +224,7 @@ def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
|
||||
nf1 = lin2db(db2lin(nf_min) - db2lin(nf2)/db2lin(g1a_max))
|
||||
|
||||
if nf1 < 4:
|
||||
print(f'First coil value too low {nf1} for amplifier {type_variety}')
|
||||
exit()
|
||||
raise EquipmentConfigError(f'First coil value too low {nf1} for amplifier {type_variety}')
|
||||
|
||||
# Check 1 dB < delta_p < 6 dB to ensure nf_min and nf_max values make sense.
|
||||
# There shouldn't be high nf differences between the two coils:
|
||||
@@ -115,21 +235,18 @@ def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
|
||||
g1a_max = lin2db(db2lin(nf2) / (db2lin(nf_min) - db2lin(nf1)))
|
||||
delta_p = gain_max - g1a_max
|
||||
g1a_min = gain_min - (gain_max-gain_min) - delta_p
|
||||
if not 1 < delta_p < 6:
|
||||
print(f'Computed \N{greek capital letter delta}P invalid \
|
||||
if not 1 < delta_p < 11:
|
||||
raise EquipmentConfigError(f'Computed \N{greek capital letter delta}P invalid \
|
||||
\n 1st coil vs 2nd coil calculated DeltaP {delta_p:.2f} for \
|
||||
\n amplifier {type_variety} is not valid: revise inputs \
|
||||
\n calculated 1st coil NF = {nf1:.2f}, 2nd coil NF = {nf2:.2f}')
|
||||
exit()
|
||||
# Check calculated values for nf1 and nf2
|
||||
calc_nf_min = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_max))
|
||||
if not isclose(nf_min, calc_nf_min, abs_tol=0.01):
|
||||
print(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}')
|
||||
exit()
|
||||
raise EquipmentConfigError(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}')
|
||||
calc_nf_max = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_min))
|
||||
if not isclose(nf_max, calc_nf_max, abs_tol=0.01):
|
||||
print(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}')
|
||||
exit()
|
||||
raise EquipmentConfigError(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}')
|
||||
|
||||
return nf1, nf2, delta_p
|
||||
|
||||
@@ -137,66 +254,128 @@ def edfa_nf(gain_target, variety_type, equipment):
|
||||
amp_params = equipment['Edfa'][variety_type]
|
||||
amp = Edfa(
|
||||
uid = f'calc_NF',
|
||||
params = amp_params._asdict(),
|
||||
params = amp_params.__dict__,
|
||||
operational = {
|
||||
'gain_target': gain_target,
|
||||
'tilt_target': 0,
|
||||
})
|
||||
'tilt_target': 0
|
||||
}
|
||||
)
|
||||
amp.pin_db = 0
|
||||
amp.nch = 88
|
||||
return amp._calc_nf(True)
|
||||
|
||||
def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=False):
|
||||
"""return the trx and SI parameters from eqpt_config for a given type_variety and mode (ie format)"""
|
||||
trx_params = {}
|
||||
default_si_data = equipment['SI']['default']
|
||||
|
||||
try:
|
||||
trxs = equipment['Transceiver']
|
||||
mode_params = next(mode for trx in trxs \
|
||||
if trx == trx_type_variety \
|
||||
for mode in trxs[trx].mode \
|
||||
if mode['format'] == trx_mode)
|
||||
trx_params = {**mode_params}
|
||||
trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency
|
||||
#if called from path_requests_run.py, trx_mode is filled with None when not specified by user
|
||||
#if called from transmission_main.py, trx_mode is ''
|
||||
if trx_mode is not None:
|
||||
mode_params = next(mode for trx in trxs \
|
||||
if trx == trx_type_variety \
|
||||
for mode in trxs[trx].mode \
|
||||
if mode['format'] == trx_mode)
|
||||
trx_params = {**mode_params}
|
||||
# sanity check: spacing baudrate must be smaller than min spacing
|
||||
if trx_params['baud_rate'] > trx_params['min_spacing'] :
|
||||
raise EquipmentConfigError(f'Inconsistency in equipment library:\n Transpoder "{trx_type_variety}" mode "{trx_params["format"]}" '+\
|
||||
f'has baud rate: {trx_params["baud_rate"]*1e-9} GHz greater than min_spacing {trx_params["min_spacing"]*1e-9}.')
|
||||
else:
|
||||
mode_params = {"format": "undetermined",
|
||||
"baud_rate": None,
|
||||
"OSNR": None,
|
||||
"bit_rate": None,
|
||||
"roll_off": None,
|
||||
"tx_osnr":None,
|
||||
"min_spacing":None,
|
||||
"cost":None}
|
||||
trx_params = {**mode_params}
|
||||
trx_params['f_min'] = equipment['Transceiver'][trx_type_variety].frequency['min']
|
||||
trx_params['f_max'] = equipment['Transceiver'][trx_type_variety].frequency['max']
|
||||
|
||||
# TODO: novel automatic feature maybe unwanted if spacing is specified
|
||||
trx_params['spacing'] = automatic_spacing(trx_params['baud_rate'])
|
||||
# trx_params['spacing'] = automatic_spacing(trx_params['baud_rate'])
|
||||
# temp = trx_params['spacing']
|
||||
# print(f'spacing {temp}')
|
||||
except StopIteration :
|
||||
if error_message:
|
||||
print(f'could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library')
|
||||
print('Computation stopped.')
|
||||
exit()
|
||||
raise EquipmentConfigError(f'Computation stoped: could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library')
|
||||
else:
|
||||
# default transponder charcteristics
|
||||
trx_params['frequency'] = {'min': default_si_data.f_min, 'max': default_si_data.f_max}
|
||||
# mainly used with transmission_main_example.py
|
||||
trx_params['f_min'] = default_si_data.f_min
|
||||
trx_params['f_max'] = default_si_data.f_max
|
||||
trx_params['baud_rate'] = default_si_data.baud_rate
|
||||
trx_params['spacing'] = default_si_data.spacing
|
||||
trx_params['OSNR'] = default_si_data.OSNR
|
||||
trx_params['bit_rate'] = default_si_data.bit_rate
|
||||
trx_params['OSNR'] = None
|
||||
trx_params['bit_rate'] = None
|
||||
trx_params['cost'] = None
|
||||
trx_params['roll_off'] = default_si_data.roll_off
|
||||
trx_params['tx_osnr'] = default_si_data.tx_osnr
|
||||
trx_params['min_spacing'] = None
|
||||
nch = automatic_nch(trx_params['f_min'], trx_params['f_max'], trx_params['spacing'])
|
||||
trx_params['nb_channel'] = nch
|
||||
print(f'There are {nch} channels propagating')
|
||||
|
||||
trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3
|
||||
trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'],
|
||||
trx_params['frequency']['max'],
|
||||
trx_params['spacing'])
|
||||
print('N channels = ', trx_params['nb_channel'])
|
||||
|
||||
return trx_params
|
||||
|
||||
def automatic_spacing(baud_rate):
|
||||
"""return the min possible channel spacing for a given baud rate"""
|
||||
spacing_list = [(38e9,50e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples
|
||||
#[(max_baud_rate, spacing_for_this_baud_rate)]
|
||||
acceptable_spacing_list = list(filter(lambda x : x[0]>baud_rate, spacing_list))
|
||||
if len(acceptable_spacing_list) < 1:
|
||||
#can't find an adequate spacing from the list, so default to:
|
||||
return baud_rate*1.2
|
||||
else:
|
||||
#chose the lowest possible spacing
|
||||
return min(acceptable_spacing_list, key=itemgetter(0))[1]
|
||||
# TODO : this should parametrized in a cfg file
|
||||
# list of possible tuples [(max_baud_rate, spacing_for_this_baud_rate)]
|
||||
spacing_list = [(33e9, 37.5e9), (38e9, 50e9), (50e9, 62.5e9), (67e9, 75e9), (92e9, 100e9)]
|
||||
return min((s[1] for s in spacing_list if s[0] > baud_rate), default=baud_rate*1.2)
|
||||
|
||||
def automatic_nch(f_min, f_max, spacing):
|
||||
return int((f_max - f_min)//spacing)
|
||||
|
||||
def automatic_fmax(f_min, spacing, nch):
|
||||
return f_min + spacing * nch
|
||||
|
||||
def load_equipment(filename):
|
||||
json_data = load_json(filename)
|
||||
return equipment_from_json(json_data, filename)
|
||||
|
||||
def update_trx_osnr(equipment):
|
||||
"""add sys_margins to all Transceivers OSNR values"""
|
||||
for trx in equipment['Transceiver'].values():
|
||||
for m in trx.mode:
|
||||
m['OSNR'] = m['OSNR'] + equipment['SI']['default'].sys_margins
|
||||
return equipment
|
||||
|
||||
def update_dual_stage(equipment):
|
||||
edfa_dict = equipment['Edfa']
|
||||
for edfa in edfa_dict.values():
|
||||
if edfa.type_def == 'dual_stage':
|
||||
edfa_preamp = edfa_dict[edfa.dual_stage_model.preamp_variety]
|
||||
edfa_booster = edfa_dict[edfa.dual_stage_model.booster_variety]
|
||||
for key, value in edfa_preamp.__dict__.items():
|
||||
attr_k = 'preamp_' + key
|
||||
setattr(edfa, attr_k, value)
|
||||
for key, value in edfa_booster.__dict__.items():
|
||||
attr_k = 'booster_' + key
|
||||
setattr(edfa, attr_k, value)
|
||||
edfa.p_max = edfa_booster.p_max
|
||||
edfa.gain_flatmax = edfa_booster.gain_flatmax + edfa_preamp.gain_flatmax
|
||||
if edfa.gain_min < edfa_preamp.gain_min:
|
||||
raise EquipmentConfigError(f'Dual stage {edfa.type_variety} min gain is lower than its preamp min gain')
|
||||
return equipment
|
||||
|
||||
def roadm_restrictions_sanity_check(equipment):
|
||||
""" verifies that booster and preamp restrictions specified in roadm equipment are listed
|
||||
in the edfa.
|
||||
"""
|
||||
restrictions = equipment['Roadm']['default'].restrictions['booster_variety_list'] + \
|
||||
equipment['Roadm']['default'].restrictions['preamp_variety_list']
|
||||
for amp_name in restrictions:
|
||||
if amp_name not in equipment['Edfa']:
|
||||
raise EquipmentConfigError(f'ROADM restriction {amp_name} does not refer to a defined EDFA name')
|
||||
|
||||
def equipment_from_json(json_data, filename):
|
||||
"""build global dictionnary eqpt_library that stores all eqpt characteristics:
|
||||
edfa type type_variety, fiber type_variety
|
||||
@@ -208,17 +387,15 @@ def equipment_from_json(json_data, filename):
|
||||
"""
|
||||
equipment = {}
|
||||
for key, entries in json_data.items():
|
||||
equipment[key] = {}
|
||||
typ = globals()[key]
|
||||
for entry in entries:
|
||||
if key not in equipment:
|
||||
equipment[key] = {}
|
||||
subkey = entry.get('type_variety', 'default')
|
||||
typ = globals()[key]
|
||||
if key == 'Edfa':
|
||||
if 'advanced_config_from_json' in entry:
|
||||
config = Path(filename).parent / entry.pop('advanced_config_from_json')
|
||||
typ = lambda **kws: Amp.from_advanced_json(config, **kws)
|
||||
else:
|
||||
config = Path(filename).parent / 'default_edfa_config.json'
|
||||
typ = lambda **kws: Amp.from_default_json(config, **kws)
|
||||
equipment[key][subkey] = typ(**entry)
|
||||
equipment[key][subkey] = Amp.from_json(filename, **entry)
|
||||
else:
|
||||
equipment[key][subkey] = typ(**entry)
|
||||
equipment = update_trx_osnr(equipment)
|
||||
equipment = update_dual_stage(equipment)
|
||||
roadm_restrictions_sanity_check(equipment)
|
||||
return equipment
|
||||
|
||||
29
gnpy/core/exceptions.py
Normal file
29
gnpy/core/exceptions.py
Normal file
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.core.exceptions
|
||||
====================
|
||||
|
||||
Exceptions thrown by other gnpy modules
|
||||
'''
|
||||
|
||||
|
||||
class ConfigurationError(Exception):
|
||||
'''User-provided configuration contains an error'''
|
||||
|
||||
class EquipmentConfigError(ConfigurationError):
|
||||
'''Incomplete or wrong configuration within the equipment library'''
|
||||
|
||||
class NetworkTopologyError(ConfigurationError):
|
||||
'''Topology of user-provided network is wrong'''
|
||||
|
||||
class ServiceError(Exception):
|
||||
'''Service of user-provided request is wrong'''
|
||||
|
||||
class DisjunctionError(ServiceError):
|
||||
'''Disjunction of user-provided request can not be satisfied'''
|
||||
|
||||
class SpectrumError(Exception):
|
||||
'''Spectrum errors of the program'''
|
||||
|
||||
@@ -5,68 +5,50 @@
|
||||
gnpy.core.info
|
||||
==============
|
||||
|
||||
This module contains classes for modelling SpectralInformation.
|
||||
This module contains classes for modelling :class:`SpectralInformation`.
|
||||
'''
|
||||
|
||||
|
||||
from collections import namedtuple
|
||||
from numpy import array
|
||||
from gnpy.core.utils import lin2db
|
||||
from gnpy.core.utils import lin2db, db2lin
|
||||
from json import loads
|
||||
from gnpy.core.utils import load_json
|
||||
from gnpy.core.equipment import automatic_nch, automatic_spacing
|
||||
|
||||
class ConvenienceAccess:
|
||||
|
||||
def __init_subclass__(cls):
|
||||
for abbrev, field in getattr(cls, '_ABBREVS', {}).items():
|
||||
setattr(cls, abbrev, property(lambda self, f=field: getattr(self, f)))
|
||||
|
||||
def update(self, **kwargs):
|
||||
for abbrev, field in getattr(self, '_ABBREVS', {}).items():
|
||||
if abbrev in kwargs:
|
||||
kwargs[field] = kwargs.pop(abbrev)
|
||||
return self._replace(**kwargs)
|
||||
class Power(namedtuple('Power', 'signal nli ase')):
|
||||
"""carriers power in W"""
|
||||
|
||||
|
||||
class Power(namedtuple('Power', 'signal nonlinear_interference amplified_spontaneous_emission'), ConvenienceAccess):
|
||||
|
||||
_ABBREVS = {'nli': 'nonlinear_interference',
|
||||
'ase': 'amplified_spontaneous_emission',}
|
||||
|
||||
|
||||
class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power'), ConvenienceAccess):
|
||||
|
||||
_ABBREVS = {'channel': 'channel_number',
|
||||
'num_chan': 'channel_number',
|
||||
'ffs': 'frequency',
|
||||
'freq': 'frequency',}
|
||||
|
||||
class Pref(namedtuple('Pref', 'p_span0, p_spani'), ConvenienceAccess):
|
||||
|
||||
_ABBREVS = {'p0' : 'p_span0',
|
||||
'pi' : 'p_spani'}
|
||||
|
||||
class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers'), ConvenienceAccess):
|
||||
|
||||
def __new__(cls, pref=Pref(0, 0), *carriers):
|
||||
return super().__new__(cls, pref, carriers)
|
||||
|
||||
def merge_input_spectral_information(*si):
|
||||
"""mix channel combs of different baud rates and power"""
|
||||
#TODO
|
||||
class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power')):
|
||||
pass
|
||||
|
||||
def create_input_spectral_information(f_min, roll_off, baud_rate, power, spacing, nb_channel):
|
||||
|
||||
class Pref(namedtuple('Pref', 'p_span0, p_spani, neq_ch ')):
|
||||
"""noiseless reference power in dBm:
|
||||
p_span0: inital target carrier power
|
||||
p_spani: carrier power after element i
|
||||
neq_ch: equivalent channel count in dB"""
|
||||
|
||||
|
||||
class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers')):
|
||||
|
||||
def __new__(cls, pref, carriers):
|
||||
return super().__new__(cls, pref, carriers)
|
||||
|
||||
|
||||
def create_input_spectral_information(f_min, f_max, roll_off, baud_rate, power, spacing):
|
||||
# pref in dB : convert power lin into power in dB
|
||||
pref = lin2db(power * 1e3)
|
||||
si = SpectralInformation(pref=Pref(pref, pref))
|
||||
si = si.update(carriers=[
|
||||
nb_channel = automatic_nch(f_min, f_max, spacing)
|
||||
si = SpectralInformation(
|
||||
pref=Pref(pref, pref, lin2db(nb_channel)),
|
||||
carriers=[
|
||||
Channel(f, (f_min+spacing*f),
|
||||
baud_rate, roll_off, Power(power, 0, 0)) for f in range(1,nb_channel+1)
|
||||
])
|
||||
return si
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pref = lin2db(power * 1e3)
|
||||
si = SpectralInformation(
|
||||
@@ -80,11 +62,11 @@ if __name__ == '__main__':
|
||||
si = SpectralInformation()
|
||||
spacing = 0.05 # THz
|
||||
|
||||
si = si.update(carriers=tuple(Channel(f+1, 191.3+spacing*(f+1), 32e9, 0.15, Power(1e-3, f, 1)) for f in range(96)))
|
||||
si = si._replace(carriers=tuple(Channel(f+1, 191.3+spacing*(f+1), 32e9, 0.15, Power(1e-3, f, 1)) for f in range(96)))
|
||||
|
||||
print(f'si = {si}')
|
||||
print(f'si = {si.carriers[0].power.nli}')
|
||||
print(f'si = {si.carriers[20].power.nli}')
|
||||
si2 = si.update(carriers=tuple(c.update(power = c.power.update(nli = c.power.nli * 1e5))
|
||||
si2 = si._replace(carriers=tuple(c._replace(power = c.power._replace(nli = c.power.nli * 1e5))
|
||||
for c in si.carriers))
|
||||
print(f'si2 = {si2}')
|
||||
|
||||
@@ -11,24 +11,27 @@ This module contains functions for constructing networks of network elements.
|
||||
from gnpy.core.convert import convert_file
|
||||
from networkx import DiGraph
|
||||
from numpy import arange
|
||||
from scipy.interpolate import interp1d
|
||||
from logging import getLogger
|
||||
from os import path
|
||||
from operator import itemgetter
|
||||
from operator import itemgetter, attrgetter
|
||||
from gnpy.core import elements
|
||||
from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused
|
||||
from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused, RamanFiber
|
||||
from gnpy.core.equipment import edfa_nf
|
||||
from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError
|
||||
from gnpy.core.units import UNITS
|
||||
from gnpy.core.utils import load_json, save_json, round2float, db2lin, lin2db
|
||||
from sys import exit
|
||||
from gnpy.core.utils import (load_json, save_json, round2float, db2lin,
|
||||
merge_amplifier_restrictions)
|
||||
from gnpy.core.science_utils import SimParams
|
||||
from collections import namedtuple
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
def load_network(filename, equipment):
|
||||
def load_network(filename, equipment, name_matching = False):
|
||||
json_filename = ''
|
||||
if filename.suffix.lower() == '.xls':
|
||||
logger.info('Automatically generating topology JSON file')
|
||||
json_filename = convert_file(filename)
|
||||
json_filename = convert_file(filename, name_matching)
|
||||
elif filename.suffix.lower() == '.json':
|
||||
json_filename = filename
|
||||
else:
|
||||
@@ -51,11 +54,12 @@ def network_from_json(json_data, equipment):
|
||||
variety = el_config.pop('type_variety', 'default')
|
||||
if typ in equipment and variety in equipment[typ]:
|
||||
extra_params = equipment[typ][variety]
|
||||
el_config.setdefault('params', {}).update(extra_params._asdict())
|
||||
elif typ in ['Edfa', 'Fiber']: #catch it now because the code will crash later!
|
||||
print( f'The {typ} of variety type {variety} was not recognized:'
|
||||
temp = el_config.setdefault('params', {})
|
||||
temp = merge_amplifier_restrictions(temp, extra_params.__dict__)
|
||||
el_config['params'] = temp
|
||||
elif typ in ['Edfa', 'Fiber']: # catch it now because the code will crash later!
|
||||
raise ConfigurationError(f'The {typ} of variety type {variety} was not recognized:'
|
||||
'\nplease check it is properly defined in the eqpt_config json file')
|
||||
exit()
|
||||
cls = getattr(elements, typ)
|
||||
el = cls(**el_config)
|
||||
g.add_node(el)
|
||||
@@ -64,7 +68,14 @@ def network_from_json(json_data, equipment):
|
||||
|
||||
for cx in json_data['connections']:
|
||||
from_node, to_node = cx['from_node'], cx['to_node']
|
||||
g.add_edge(nodes[from_node], nodes[to_node])
|
||||
try:
|
||||
if isinstance(nodes[from_node], Fiber):
|
||||
edge_length = nodes[from_node].params.length
|
||||
else:
|
||||
edge_length = 0.01
|
||||
g.add_edge(nodes[from_node], nodes[to_node], weight = edge_length)
|
||||
except KeyError:
|
||||
raise NetworkTopologyError(f'can not find {from_node} or {to_node} defined in {cx}')
|
||||
|
||||
return g
|
||||
|
||||
@@ -81,16 +92,27 @@ def network_to_json(network):
|
||||
data.update(connections)
|
||||
return data
|
||||
|
||||
def select_edfa(gain_target, power_target, equipment):
|
||||
def select_edfa(raman_allowed, gain_target, power_target, equipment, uid, restrictions=None):
|
||||
"""amplifer selection algorithm
|
||||
@Orange Jean-Luc Augé
|
||||
"""
|
||||
Edfa_list = namedtuple('Edfa_list', 'variety power gain nf')
|
||||
TARGET_EXTENDED_GAIN = 2.1
|
||||
#MAX_EXTENDED_GAIN = 5
|
||||
edfa_dict = equipment['Edfa']
|
||||
Edfa_list = namedtuple('Edfa_list', 'variety power gain_min nf')
|
||||
TARGET_EXTENDED_GAIN = equipment['Span']['default'].target_extended_gain
|
||||
|
||||
# for roadm restriction only: create a dict including not allowed for design amps
|
||||
# because main use case is to have specific radm amp which are not allowed for ILA
|
||||
# with the auto design
|
||||
edfa_dict = {name: amp for (name, amp) in equipment['Edfa'].items()
|
||||
if restrictions is None or name in restrictions}
|
||||
|
||||
pin = power_target - gain_target
|
||||
|
||||
# create 2 list of available amplifiers with relevant attributes for their selection
|
||||
|
||||
# edfa list with:
|
||||
# extended gain min allowance of 3dB: could be parametrized, but a bit complex
|
||||
# extended gain max allowance TARGET_EXTENDED_GAIN is coming from eqpt_config.json
|
||||
# power attribut include power AND gain limitations
|
||||
edfa_list = [Edfa_list(
|
||||
variety=edfa_variety,
|
||||
power=min(
|
||||
@@ -100,80 +122,118 @@ def select_edfa(gain_target, power_target, equipment):
|
||||
edfa.p_max
|
||||
)
|
||||
-power_target,
|
||||
gain=edfa.gain_flatmax-gain_target,
|
||||
gain_min=
|
||||
gain_target+3
|
||||
-edfa.gain_min,
|
||||
nf=edfa_nf(gain_target, edfa_variety, equipment)) \
|
||||
for edfa_variety, edfa in edfa_dict.items()
|
||||
if edfa.allowed_for_design]
|
||||
if ((edfa.allowed_for_design or restrictions is not None) and not edfa.raman)]
|
||||
|
||||
acceptable_gain_list = \
|
||||
list(filter(lambda x : x.gain>-TARGET_EXTENDED_GAIN, edfa_list))
|
||||
if len(acceptable_gain_list) < 1:
|
||||
#no amplifier satisfies the required gain, so pick the highest gain:
|
||||
gain_max = max(edfa_list, key=itemgetter(2)).gain
|
||||
#pick up all amplifiers that share this max gain:
|
||||
acceptable_gain_list = \
|
||||
list(filter(lambda x : x.gain-gain_max>-0.1, edfa_list))
|
||||
acceptable_power_list = \
|
||||
list(filter(lambda x : x.power>=0, acceptable_gain_list))
|
||||
#consider a Raman list because of different gain_min requirement:
|
||||
#do not allow extended gain min for Raman
|
||||
raman_list = [Edfa_list(
|
||||
variety=edfa_variety,
|
||||
power=min(
|
||||
pin
|
||||
+edfa.gain_flatmax
|
||||
+TARGET_EXTENDED_GAIN,
|
||||
edfa.p_max
|
||||
)
|
||||
-power_target,
|
||||
gain_min=
|
||||
gain_target
|
||||
-edfa.gain_min,
|
||||
nf=edfa_nf(gain_target, edfa_variety, equipment))
|
||||
for edfa_variety, edfa in edfa_dict.items()
|
||||
if (edfa.allowed_for_design and edfa.raman)] \
|
||||
if raman_allowed else []
|
||||
|
||||
#merge raman and edfa lists
|
||||
amp_list = edfa_list + raman_list
|
||||
|
||||
#filter on min gain limitation:
|
||||
acceptable_gain_min_list = [x for x in amp_list if x.gain_min>0]
|
||||
|
||||
if len(acceptable_gain_min_list) < 1:
|
||||
#do not take this empty list into account for the rest of the code
|
||||
#but issue a warning to the user and do not consider Raman
|
||||
#Raman below min gain should not be allowed because i is meant to be a design requirement
|
||||
#and raman padding at the amplifier input is impossible!
|
||||
|
||||
if len(edfa_list) < 1:
|
||||
raise ConfigurationError(f'auto_design could not find any amplifier \
|
||||
to satisfy min gain requirement in node {uid} \
|
||||
please increase span fiber padding')
|
||||
else:
|
||||
# TODO: convert to logging
|
||||
print(
|
||||
f'\x1b[1;31;40m'\
|
||||
+ f'WARNING: target gain in node {uid} is below all available amplifiers min gain: \
|
||||
amplifier input padding will be assumed, consider increase span fiber padding instead'\
|
||||
+ '\x1b[0m'
|
||||
)
|
||||
acceptable_gain_min_list = edfa_list
|
||||
|
||||
#filter on gain+power limitation:
|
||||
#this list checks both the gain and the power requirement
|
||||
#because of the way .power is calculated in the list
|
||||
acceptable_power_list = [x for x in acceptable_gain_min_list if x.power>0]
|
||||
if len(acceptable_power_list) < 1:
|
||||
#no amplifier satisfies the required power, so pick the highest power:
|
||||
power_max = \
|
||||
max(acceptable_gain_list, key=itemgetter(1)).power
|
||||
#pick up all amplifiers that share this max gain:
|
||||
acceptable_power_list = \
|
||||
list(filter(lambda x : x.power-power_max>-0.1, acceptable_gain_list))
|
||||
#no amplifier satisfies the required power, so pick the highest power(s):
|
||||
power_max = max(acceptable_gain_min_list, key=attrgetter('power')).power
|
||||
#check and pick if other amplifiers may have a similar gain/power
|
||||
#allow a 0.3dB power range
|
||||
#this allows to chose an amplifier with a better NF subsequentely
|
||||
acceptable_power_list = [x for x in acceptable_gain_min_list
|
||||
if x.power-power_max>-0.3]
|
||||
|
||||
|
||||
# gain and power requirements are resolved,
|
||||
# =>chose the amp with the best NF among the acceptable ones:
|
||||
return min(acceptable_power_list, key=itemgetter(3)).variety #filter on NF
|
||||
selected_edfa = min(acceptable_power_list, key=attrgetter('nf')) #filter on NF
|
||||
#check what are the gain and power limitations of this amp
|
||||
power_reduction = round(min(selected_edfa.power, 0),2)
|
||||
if power_reduction < -0.5:
|
||||
print(
|
||||
f'\x1b[1;31;40m'\
|
||||
+ f'WARNING: target gain and power in node {uid}\n \
|
||||
is beyond all available amplifiers capabilities and/or extended_gain_range:\n\
|
||||
a power reduction of {power_reduction} is applied\n'\
|
||||
+ '\x1b[0m'
|
||||
)
|
||||
|
||||
def set_roadm_loss(network, equipment, pref_ch_db):
|
||||
roadms = [roadm for roadm in network if isinstance(roadm, Roadm)]
|
||||
power_mode = equipment['Spans']['default'].power_mode
|
||||
default_roadm_loss = equipment['Roadms']['default'].gain_mode_default_loss
|
||||
pref_roadm_db = equipment['Roadms']['default'].power_mode_pref
|
||||
roadm_loss = pref_ch_db - pref_roadm_db
|
||||
|
||||
for roadm in roadms:
|
||||
if power_mode:
|
||||
roadm.loss = roadm_loss
|
||||
elif roadm.loss == None:
|
||||
roadm.loss = default_roadm_loss
|
||||
return selected_edfa.variety, power_reduction
|
||||
|
||||
def target_power(dp_from_gain, network, node, equipment): #get_fiber_dp
|
||||
def target_power(network, node, equipment): #get_fiber_dp
|
||||
SPAN_LOSS_REF = 20
|
||||
POWER_SLOPE = 0.3
|
||||
power_mode = equipment['Spans']['default'].power_mode
|
||||
dp_range = list(equipment['Spans']['default'].delta_power_range_db)
|
||||
power_mode = equipment['Span']['default'].power_mode
|
||||
dp_range = list(equipment['Span']['default'].delta_power_range_db)
|
||||
node_loss = span_loss(network, node)
|
||||
|
||||
dp_gain_mode = 0
|
||||
try:
|
||||
dp_power_mode = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2])
|
||||
dp_power_mode = max(dp_range[0], dp_power_mode)
|
||||
dp_power_mode = min(dp_range[1], dp_power_mode)
|
||||
dp = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2])
|
||||
dp = max(dp_range[0], dp)
|
||||
dp = min(dp_range[1], dp)
|
||||
except KeyError:
|
||||
print(f'invalid delta_power_range_db definition in eqpt_config[Spans]'
|
||||
raise ConfigurationError(f'invalid delta_power_range_db definition in eqpt_config[Span]'
|
||||
f'delta_power_range_db: [lower_bound, upper_bound, step]')
|
||||
exit()
|
||||
|
||||
if dp_from_gain:
|
||||
dp_power_mode = dp_from_gain
|
||||
dp_gain_mode = dp_from_gain
|
||||
if isinstance(node, Roadm):
|
||||
dp_power_mode = 0
|
||||
|
||||
dp = dp_power_mode if power_mode else dp_gain_mode
|
||||
#print(f'{repr(node)} delta power in:\n{dp}dB')
|
||||
dp = 0
|
||||
|
||||
return dp
|
||||
|
||||
|
||||
def prev_node_generator(network, node):
|
||||
"""fused spans interest:
|
||||
iterate over all predecessors while they are Fused or Fiber type"""
|
||||
prev_node = next(n for n in network.predecessors(node))
|
||||
try:
|
||||
prev_node = next(n for n in network.predecessors(node))
|
||||
except StopIteration:
|
||||
raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology')
|
||||
# yield and re-iterate
|
||||
if isinstance(prev_node, Fused) or isinstance(node, Fused):
|
||||
if isinstance(prev_node, Fused) or isinstance(node, Fused) and not isinstance(prev_node, Roadm):
|
||||
yield prev_node
|
||||
yield from prev_node_generator(network, prev_node)
|
||||
else:
|
||||
@@ -182,9 +242,12 @@ def prev_node_generator(network, node):
|
||||
def next_node_generator(network, node):
|
||||
"""fused spans interest:
|
||||
iterate over all successors while they are Fused or Fiber type"""
|
||||
next_node = next(n for n in network.successors(node))
|
||||
try:
|
||||
next_node = next(n for n in network.successors(node))
|
||||
except StopIteration:
|
||||
raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology')
|
||||
# yield and re-iterate
|
||||
if isinstance(next_node, Fused) or isinstance(node, Fused):
|
||||
if isinstance(next_node, Fused) or isinstance(node, Fused) and not isinstance(next_node, Roadm):
|
||||
yield next_node
|
||||
yield from next_node_generator(network, next_node)
|
||||
else:
|
||||
@@ -226,23 +289,22 @@ def find_last_node(network, node):
|
||||
pass
|
||||
return this_node
|
||||
|
||||
def set_amplifier_voa(amp, pref_total_db, power_mode):
|
||||
VOA_MARGIN = 0
|
||||
if amp.operational.out_voa is None:
|
||||
def set_amplifier_voa(amp, power_target, power_mode):
|
||||
VOA_MARGIN = 1 #do not maximize the VOA optimization
|
||||
if amp.out_voa is None:
|
||||
if power_mode:
|
||||
gain_target = amp.operational.gain_target
|
||||
pout = pref_total_db + amp.dp_db
|
||||
voa = min(amp.params.p_max-pout,
|
||||
amp.params.gain_flatmax-amp.operational.gain_target)
|
||||
voa = round2float(max(voa, 0), 0.5) - VOA_MARGIN if amp.params.out_voa_auto else 0
|
||||
amp.dp_db = amp.dp_db + voa
|
||||
amp.operational.gain_target = amp.operational.gain_target + voa
|
||||
gain_target = amp.effective_gain
|
||||
voa = min(amp.params.p_max-power_target,
|
||||
amp.params.gain_flatmax-amp.effective_gain)
|
||||
voa = max(round2float(max(voa, 0), 0.5) - VOA_MARGIN, 0) if amp.params.out_voa_auto else 0
|
||||
amp.delta_p = amp.delta_p + voa
|
||||
amp.effective_gain = amp.effective_gain + voa
|
||||
else:
|
||||
voa = 0 # no output voa optimization in gain mode
|
||||
amp.operational.out_voa = voa
|
||||
amp.out_voa = voa
|
||||
|
||||
def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
power_mode = equipment['Spans']['default'].power_mode
|
||||
power_mode = equipment['Span']['default'].power_mode
|
||||
next_oms = (n for n in network.successors(roadm) if not isinstance(n, Transceiver))
|
||||
for oms in next_oms:
|
||||
#go through all the OMS departing from the Roadm
|
||||
@@ -253,30 +315,82 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
# node = find_last_node(next_node)
|
||||
# next_node = next(n for n in network.successors(node))
|
||||
# next_node = find_last_node(next_node)
|
||||
prev_dp = 0
|
||||
dp = 0
|
||||
|
||||
if node.per_degree_target_pch_out_db:
|
||||
# find the target power on this degree
|
||||
try:
|
||||
prev_dp = next(el["target_pch_out_db"] for el in \
|
||||
node.per_degree_target_pch_out_db if el["to_node"]==next_node.uid)
|
||||
except StopIteration:
|
||||
# if no target power is defined on this degree use the global one
|
||||
prev_dp = getattr(node.params, 'target_pch_out_db', 0)
|
||||
else:
|
||||
# if no per degree target power is given use the global one
|
||||
prev_dp = getattr(node.params, 'target_pch_out_db', 0)
|
||||
dp = prev_dp
|
||||
prev_voa = 0
|
||||
voa = 0
|
||||
while True:
|
||||
#go through all nodes in the OMS (loop until next Roadm instance)
|
||||
if isinstance(node, Edfa):
|
||||
node_loss = span_loss(network, prev_node)
|
||||
dp_from_gain = prev_dp + node.operational.gain_target - node_loss \
|
||||
if node.operational.gain_target > 0 else None
|
||||
dp = target_power(dp_from_gain, network, next_node, equipment)
|
||||
gain_target = node_loss + dp - prev_dp
|
||||
voa = node.out_voa if node.out_voa else 0
|
||||
if node.delta_p is None:
|
||||
dp = target_power(network, next_node, equipment)
|
||||
else:
|
||||
dp = node.delta_p
|
||||
gain_from_dp = node_loss + dp - prev_dp + prev_voa
|
||||
if node.effective_gain is None or power_mode:
|
||||
gain_target = gain_from_dp
|
||||
else: #gain mode with effective_gain
|
||||
gain_target = node.effective_gain
|
||||
dp = prev_dp - node_loss + gain_target
|
||||
|
||||
if power_mode:
|
||||
node.dp_db = dp
|
||||
node.operational.gain_target = gain_target
|
||||
power_target = pref_total_db + dp
|
||||
|
||||
if node.params.type_variety == '':
|
||||
power_target = pref_total_db + dp
|
||||
edfa_variety = select_edfa(gain_target, power_target, equipment)
|
||||
raman_allowed = False
|
||||
if isinstance(prev_node, Fiber):
|
||||
max_fiber_lineic_loss_for_raman = \
|
||||
equipment['Span']['default'].max_fiber_lineic_loss_for_raman
|
||||
raman_allowed = prev_node.params.loss_coef < max_fiber_lineic_loss_for_raman
|
||||
|
||||
# implementation of restrictions on roadm boosters
|
||||
if isinstance(prev_node,Roadm):
|
||||
if prev_node.restrictions['booster_variety_list']:
|
||||
restrictions = prev_node.restrictions['booster_variety_list']
|
||||
else:
|
||||
restrictions = None
|
||||
elif isinstance(next_node,Roadm):
|
||||
# implementation of restrictions on roadm preamp
|
||||
if next_node.restrictions['preamp_variety_list']:
|
||||
restrictions = next_node.restrictions['preamp_variety_list']
|
||||
else:
|
||||
restrictions = None
|
||||
else:
|
||||
restrictions = None
|
||||
|
||||
if node.params.type_variety == '':
|
||||
edfa_variety, power_reduction = select_edfa(raman_allowed,
|
||||
gain_target, power_target, equipment, node.uid, restrictions)
|
||||
extra_params = equipment['Edfa'][edfa_variety]
|
||||
node.params.update_params(extra_params._asdict())
|
||||
set_amplifier_voa(node, pref_total_db, power_mode)
|
||||
node.params.update_params(extra_params.__dict__)
|
||||
dp += power_reduction
|
||||
gain_target += power_reduction
|
||||
elif node.params.raman and not raman_allowed:
|
||||
print(
|
||||
f'\x1b[1;31;40m'\
|
||||
+ f'WARNING: raman is used in node {node.uid}\n \
|
||||
but fiber lineic loss is above threshold\n'\
|
||||
+ '\x1b[0m'
|
||||
)
|
||||
|
||||
node.delta_p = dp if power_mode else None
|
||||
node.effective_gain = gain_target
|
||||
set_amplifier_voa(node, power_target, power_mode)
|
||||
if isinstance(next_node, Roadm) or isinstance(next_node, Transceiver):
|
||||
break
|
||||
prev_dp = dp
|
||||
prev_voa = voa
|
||||
prev_node = node
|
||||
node = next_node
|
||||
# print(f'{node.uid}')
|
||||
@@ -292,13 +406,25 @@ def add_egress_amplifier(network, node):
|
||||
amp = Edfa(
|
||||
uid = f'Edfa{i}_{node.uid}',
|
||||
params = {},
|
||||
metadata = {
|
||||
'location': {
|
||||
'latitude': (node.lat * 2 + next_node.lat * 2) / 4,
|
||||
'longitude': (node.lng * 2 + next_node.lng * 2) / 4,
|
||||
'city': node.loc.city,
|
||||
'region': node.loc.region,
|
||||
}
|
||||
},
|
||||
operational = {
|
||||
'gain_target': 0,
|
||||
'gain_target': None,
|
||||
'tilt_target': 0,
|
||||
})
|
||||
network.add_node(amp)
|
||||
network.add_edge(node, amp)
|
||||
network.add_edge(amp, next_node)
|
||||
if isinstance(node,Fiber):
|
||||
edgeweight = node.params.length
|
||||
else:
|
||||
edgeweight = 0.01
|
||||
network.add_edge(node, amp, weight = edgeweight)
|
||||
network.add_edge(amp, next_node, weight = 0.01)
|
||||
|
||||
|
||||
def calculate_new_length(fiber_length, bounds, target_length):
|
||||
@@ -334,37 +460,48 @@ def split_fiber(network, fiber, bounds, target_length, equipment):
|
||||
next_node = next(network.successors(fiber))
|
||||
prev_node = next(network.predecessors(fiber))
|
||||
except StopIteration:
|
||||
print(f'{repr(fiber)} is not properly connected, please check network topology')
|
||||
exit()
|
||||
raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology')
|
||||
|
||||
network.remove_edge(fiber, next_node)
|
||||
network.remove_edge(prev_node, fiber)
|
||||
network.remove_node(fiber)
|
||||
# update connector loss parameter with default values
|
||||
|
||||
fiber_params = fiber.params._asdict()
|
||||
fiber_params['length'] = new_length / UNITS[fiber.params.length_units]
|
||||
fiber_params['con_in'] = fiber.con_in
|
||||
fiber_params['con_out'] = fiber.con_out
|
||||
new_spans = [
|
||||
Fiber(
|
||||
uid = f'{fiber.uid}_({span}/{n_spans})',
|
||||
metadata = fiber.metadata,
|
||||
params = fiber_params
|
||||
) for span in range(n_spans)
|
||||
]
|
||||
for new_span in new_spans:
|
||||
new_span.length = new_length
|
||||
network.add_node(new_span)
|
||||
network.add_edge(prev_node, new_span)
|
||||
prev_node = new_span
|
||||
network.add_edge(prev_node, next_node)
|
||||
|
||||
def add_connector_loss(fibers, con_in, con_out, EOL):
|
||||
for fiber in fibers:
|
||||
if fiber.con_in is None: fiber.con_in = con_in
|
||||
if fiber.con_out is None:
|
||||
fiber.con_out = con_out #con_out includes EOL
|
||||
f = interp1d([prev_node.lng, next_node.lng], [prev_node.lat, next_node.lat])
|
||||
xpos = [prev_node.lng + (next_node.lng - prev_node.lng) * (n+1)/(n_spans+1) for n in range(n_spans)]
|
||||
ypos = f(xpos)
|
||||
for span, lng, lat in zip(range(n_spans), xpos, ypos):
|
||||
new_span = Fiber(uid = f'{fiber.uid}_({span+1}/{n_spans})',
|
||||
metadata = {
|
||||
'location': {
|
||||
'latitude': lat,
|
||||
'longitude': lng,
|
||||
'city': fiber.loc.city,
|
||||
'region': fiber.loc.region,
|
||||
}
|
||||
},
|
||||
params = fiber_params)
|
||||
if isinstance(prev_node,Fiber):
|
||||
edgeweight = prev_node.params.length
|
||||
else:
|
||||
fiber.con_out = fiber.con_out+EOL
|
||||
edgeweight = 0.01
|
||||
network.add_edge(prev_node, new_span, weight = edgeweight)
|
||||
prev_node = new_span
|
||||
if isinstance(prev_node,Fiber):
|
||||
edgeweight = prev_node.params.length
|
||||
else:
|
||||
edgeweight = 0.01
|
||||
network.add_edge(prev_node, next_node, weight = edgeweight)
|
||||
|
||||
def add_connector_loss(network, fibers, default_con_in, default_con_out, EOL):
|
||||
for fiber in fibers:
|
||||
if fiber.con_in is None: fiber.con_in = default_con_in
|
||||
if fiber.con_out is None: fiber.con_out = default_con_out
|
||||
next_node = next(n for n in network.successors(fiber))
|
||||
if not isinstance(next_node, Fused):
|
||||
fiber.con_out += EOL
|
||||
|
||||
def add_fiber_padding(network, fibers, padding):
|
||||
"""last_fibers = (fiber for n in network.nodes()
|
||||
@@ -373,30 +510,35 @@ def add_fiber_padding(network, fibers, padding):
|
||||
if isinstance(fiber, Fiber))"""
|
||||
for fiber in fibers:
|
||||
this_span_loss = span_loss(network, fiber)
|
||||
next_node = next(network.successors(fiber))
|
||||
try:
|
||||
next_node = next(network.successors(fiber))
|
||||
except StopIteration:
|
||||
raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology')
|
||||
if this_span_loss < padding and not (isinstance(next_node, Fused)):
|
||||
#add a padding att_in at the input of the 1st fiber:
|
||||
#address the case when several fibers are spliced together
|
||||
first_fiber = find_first_node(network, fiber)
|
||||
if first_fiber.att_in is None:
|
||||
first_fiber.att_in = padding - this_span_loss
|
||||
else :
|
||||
first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
|
||||
# in order to support no booster , fused might be placed
|
||||
# just after a roadm: need to check that first_fiber is really a fiber
|
||||
if isinstance(first_fiber,Fiber):
|
||||
if first_fiber.att_in is None:
|
||||
first_fiber.att_in = padding - this_span_loss
|
||||
else:
|
||||
first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
|
||||
|
||||
def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
default_span_data = equipment['Spans']['default']
|
||||
default_span_data = equipment['Span']['default']
|
||||
max_length = int(default_span_data.max_length * UNITS[default_span_data.length_units])
|
||||
min_length = max(int(default_span_data.padding/0.2*1e3),50_000)
|
||||
bounds = range(min_length, max_length)
|
||||
target_length = max(min_length, 90_000)
|
||||
con_in = default_span_data.con_in
|
||||
con_out = default_span_data.con_out + default_span_data.EOL
|
||||
default_con_in = default_span_data.con_in
|
||||
default_con_out = default_span_data.con_out
|
||||
padding = default_span_data.padding
|
||||
|
||||
#set raodm loss for gain_mode before to build network
|
||||
set_roadm_loss(network, equipment, pref_ch_db)
|
||||
#set roadm loss for gain_mode before to build network
|
||||
fibers = [f for f in network.nodes() if isinstance(f, Fiber)]
|
||||
add_connector_loss(fibers, con_in, con_out, default_span_data.EOL)
|
||||
add_connector_loss(network, fibers, default_con_in, default_con_out, default_span_data.EOL)
|
||||
add_fiber_padding(network, fibers, padding)
|
||||
# don't group split fiber and add amp in the same loop
|
||||
# =>for code clarity (at the expense of speed):
|
||||
@@ -405,6 +547,7 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
|
||||
amplified_nodes = [n for n in network.nodes()
|
||||
if isinstance(n, Fiber) or isinstance(n, Roadm)]
|
||||
|
||||
for node in amplified_nodes:
|
||||
add_egress_amplifier(network, node)
|
||||
|
||||
@@ -418,3 +561,11 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
for t in trx:
|
||||
set_egress_amplifier(network, t, equipment, pref_total_db)
|
||||
|
||||
def load_sim_params(filename):
|
||||
sim_params = load_json(filename)
|
||||
return SimParams(params=sim_params)
|
||||
|
||||
def configure_network(network, sim_params):
|
||||
for node in network.nodes:
|
||||
if isinstance(node, RamanFiber):
|
||||
node.sim_params = sim_params
|
||||
|
||||
@@ -8,13 +8,13 @@ gnpy.core.node
|
||||
This module contains the base class for a network element.
|
||||
|
||||
Strictly, a network element is any callable which accepts an immutable
|
||||
.info.SpectralInformation object and returns a .info.SpectralInformation object
|
||||
(a copy.)
|
||||
:class:`.info.SpectralInformation` object and returns an :class:`.info.SpectralInformation` object
|
||||
(a copy).
|
||||
|
||||
Network elements MUST implement two attributes .uid and .name representing a
|
||||
unique identifier and a printable name.
|
||||
|
||||
This base class provides a mode convenient way to define a network element
|
||||
This base class provides a more convenient way to define a network element
|
||||
via subclassing.
|
||||
'''
|
||||
|
||||
@@ -26,17 +26,19 @@ class Location(namedtuple('Location', 'latitude longitude city region')):
|
||||
return super().__new__(cls, latitude, longitude, city, region)
|
||||
|
||||
class Node:
|
||||
def __init__(self, uid, name=None, params=None, metadata={'location':{}}, operational=None):
|
||||
def __init__(self, uid, name=None, params=None, metadata=None, operational=None):
|
||||
if name is None:
|
||||
name = uid
|
||||
self.uid, self.name = uid, name
|
||||
if metadata is None:
|
||||
metadata = {'location': {}}
|
||||
if metadata and not isinstance(metadata.get('location'), Location):
|
||||
metadata['location'] = Location(**metadata.pop('location', {}))
|
||||
self.params, self.metadata, self.operational = params, metadata, operational
|
||||
|
||||
@property
|
||||
def coords(self):
|
||||
return tuple(self.lng, self.lat)
|
||||
return self.lng, self.lat
|
||||
|
||||
@property
|
||||
def location(self):
|
||||
|
||||
1258
gnpy/core/request.py
1258
gnpy/core/request.py
File diff suppressed because it is too large
Load Diff
820
gnpy/core/science_utils.py
Normal file
820
gnpy/core/science_utils.py
Normal file
@@ -0,0 +1,820 @@
|
||||
import numpy as np
|
||||
from operator import attrgetter
|
||||
from collections import namedtuple
|
||||
from logging import getLogger
|
||||
import scipy.constants as ph
|
||||
from scipy.integrate import solve_bvp
|
||||
from scipy.integrate import cumtrapz
|
||||
from scipy.interpolate import interp1d
|
||||
from scipy.optimize import OptimizeResult
|
||||
|
||||
from gnpy.core.utils import db2lin
|
||||
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
class RamanParams():
|
||||
def __init__(self, params):
|
||||
self._flag_raman = params['flag_raman']
|
||||
self._space_resolution = params['space_resolution']
|
||||
self._tolerance = params['tolerance']
|
||||
|
||||
@property
|
||||
def flag_raman(self):
|
||||
return self._flag_raman
|
||||
|
||||
@property
|
||||
def space_resolution(self):
|
||||
return self._space_resolution
|
||||
|
||||
@property
|
||||
def tolerance(self):
|
||||
return self._tolerance
|
||||
|
||||
class NLIParams():
|
||||
def __init__(self, params):
|
||||
self._nli_method_name = params['nli_method_name']
|
||||
self._wdm_grid_size = params['wdm_grid_size']
|
||||
self._dispersion_tolerance = params['dispersion_tolerance']
|
||||
self._phase_shift_tollerance = params['phase_shift_tollerance']
|
||||
self._f_cut_resolution = None
|
||||
self._f_pump_resolution = None
|
||||
|
||||
@property
|
||||
def nli_method_name(self):
|
||||
return self._nli_method_name
|
||||
|
||||
@property
|
||||
def wdm_grid_size(self):
|
||||
return self._wdm_grid_size
|
||||
|
||||
@property
|
||||
def dispersion_tolerance(self):
|
||||
return self._dispersion_tolerance
|
||||
|
||||
@property
|
||||
def phase_shift_tollerance(self):
|
||||
return self._phase_shift_tollerance
|
||||
|
||||
@property
|
||||
def f_cut_resolution(self):
|
||||
return self._f_cut_resolution
|
||||
|
||||
@f_cut_resolution.setter
|
||||
def f_cut_resolution(self, f_cut_resolution):
|
||||
self._f_cut_resolution = f_cut_resolution
|
||||
|
||||
@property
|
||||
def f_pump_resolution(self):
|
||||
return self._f_pump_resolution
|
||||
|
||||
@f_pump_resolution.setter
|
||||
def f_pump_resolution(self, f_pump_resolution):
|
||||
self._f_pump_resolution = f_pump_resolution
|
||||
|
||||
class SimParams():
|
||||
def __init__(self, params):
|
||||
self._raman_computed_channels = params['raman_computed_channels']
|
||||
self._raman_params = RamanParams(params=params['raman_parameters'])
|
||||
self._nli_params = NLIParams(params=params['nli_parameters'])
|
||||
|
||||
@property
|
||||
def raman_computed_channels(self):
|
||||
return self._raman_computed_channels
|
||||
|
||||
@property
|
||||
def raman_params(self):
|
||||
return self._raman_params
|
||||
|
||||
@property
|
||||
def nli_params(self):
|
||||
return self._nli_params
|
||||
|
||||
class FiberParams():
|
||||
def __init__(self, fiber):
|
||||
self._loss_coef = 2 * fiber.dbkm_2_lin()[1]
|
||||
self._length = fiber.length
|
||||
self._gamma = fiber.gamma
|
||||
self._beta2 = fiber.beta2()
|
||||
self._beta3 = fiber.beta3 if hasattr(fiber, 'beta3') else 0
|
||||
self._f_ref_beta = fiber.f_ref_beta if hasattr(fiber, 'f_ref_beta') else 0
|
||||
self._raman_efficiency = fiber.params.raman_efficiency
|
||||
self._temperature = fiber.operational['temperature']
|
||||
|
||||
@property
|
||||
def loss_coef(self):
|
||||
return self._loss_coef
|
||||
|
||||
@property
|
||||
def length(self):
|
||||
return self._length
|
||||
|
||||
@property
|
||||
def gamma(self):
|
||||
return self._gamma
|
||||
|
||||
@property
|
||||
def beta2(self):
|
||||
return self._beta2
|
||||
|
||||
@property
|
||||
def beta3(self):
|
||||
return self._beta3
|
||||
|
||||
@property
|
||||
def f_ref_beta(self):
|
||||
return self._f_ref_beta
|
||||
|
||||
@property
|
||||
def raman_efficiency(self):
|
||||
return self._raman_efficiency
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
return self._temperature
|
||||
|
||||
def alpha0(self, f_ref=193.5e12):
|
||||
""" It returns the zero element of the series expansion of attenuation coefficient alpha(f) in the
|
||||
reference frequency f_ref
|
||||
|
||||
:param f_ref: reference frequency of series expansion [Hz]
|
||||
:return: alpha0: power attenuation coefficient in f_ref [Neper/m]
|
||||
"""
|
||||
if not hasattr(self.loss_coef, 'alpha_power'):
|
||||
alpha0 = self.loss_coef
|
||||
else:
|
||||
alpha_interp = interp1d(self.loss_coef['frequency'],
|
||||
self.loss_coef['alpha_power'])
|
||||
alpha0 = alpha_interp(f_ref)
|
||||
return alpha0
|
||||
|
||||
pump = namedtuple('RamanPump', 'power frequency propagation_direction')
|
||||
|
||||
def propagate_raman_fiber(fiber, *carriers):
|
||||
sim_params = fiber.sim_params
|
||||
raman_params = fiber.sim_params.raman_params
|
||||
nli_params = fiber.sim_params.nli_params
|
||||
# apply input attenuation to carriers
|
||||
attenuation_in = db2lin(fiber.con_in + fiber.att_in)
|
||||
chan = []
|
||||
for carrier in carriers:
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal / attenuation_in,
|
||||
nli=pwr.nli / attenuation_in,
|
||||
ase=pwr.ase / attenuation_in)
|
||||
carrier = carrier._replace(power=pwr)
|
||||
chan.append(carrier)
|
||||
carriers = tuple(f for f in chan)
|
||||
fiber_params = FiberParams(fiber)
|
||||
|
||||
# evaluate fiber attenuation involving also SRS if required by sim_params
|
||||
if 'raman_pumps' in fiber.operational:
|
||||
raman_pumps = tuple(pump(p['power'], p['frequency'], p['propagation_direction'])
|
||||
for p in fiber.operational['raman_pumps'])
|
||||
else:
|
||||
raman_pumps = None
|
||||
raman_solver = RamanSolver(raman_params=raman_params, fiber_params=fiber_params)
|
||||
stimulated_raman_scattering = raman_solver.stimulated_raman_scattering(carriers=carriers,
|
||||
raman_pumps=raman_pumps)
|
||||
fiber_attenuation = (stimulated_raman_scattering.rho[:, -1])**-2
|
||||
if not raman_params.flag_raman:
|
||||
fiber_attenuation = tuple(fiber.lin_attenuation for _ in carriers)
|
||||
|
||||
# evaluate Raman ASE noise if required by sim_params and if raman pumps are present
|
||||
if raman_params.flag_raman and raman_pumps:
|
||||
raman_ase = raman_solver.spontaneous_raman_scattering.power[:, -1]
|
||||
else:
|
||||
raman_ase = tuple(0 for _ in carriers)
|
||||
|
||||
# evaluate nli and propagate in fiber
|
||||
attenuation_out = db2lin(fiber.con_out)
|
||||
nli_solver = NliSolver(nli_params=nli_params, fiber_params=fiber_params)
|
||||
nli_solver.stimulated_raman_scattering = stimulated_raman_scattering
|
||||
|
||||
nli_frequencies = []
|
||||
computed_nli = []
|
||||
for carrier in (c for c in carriers if c.channel_number in sim_params.raman_computed_channels):
|
||||
resolution_param = frequency_resolution(carrier, carriers, sim_params, fiber_params)
|
||||
f_cut_resolution, f_pump_resolution, _, _ = resolution_param
|
||||
nli_params.f_cut_resolution = f_cut_resolution
|
||||
nli_params.f_pump_resolution = f_pump_resolution
|
||||
nli_frequencies.append(carrier.frequency)
|
||||
computed_nli.append(nli_solver.compute_nli(carrier, *carriers))
|
||||
|
||||
new_carriers = []
|
||||
for carrier, attenuation, rmn_ase in zip(carriers, fiber_attenuation, raman_ase):
|
||||
carrier_nli = np.interp(carrier.frequency, nli_frequencies, computed_nli)
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal/attenuation/attenuation_out,
|
||||
nli=(pwr.nli+carrier_nli)/attenuation/attenuation_out,
|
||||
ase=((pwr.ase/attenuation)+rmn_ase)/attenuation_out)
|
||||
new_carriers.append(carrier._replace(power=pwr))
|
||||
return new_carriers
|
||||
|
||||
def frequency_resolution(carrier, carriers, sim_params, fiber_params):
|
||||
def _get_freq_res_k_phi(delta_count, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol):
|
||||
res_phi = _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol)
|
||||
res_k = _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol)
|
||||
res_dict = {'res_phi': res_phi, 'res_k': res_k}
|
||||
method = min(res_dict, key=res_dict.get)
|
||||
return res_dict[method], method, res_dict
|
||||
|
||||
def _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol):
|
||||
return k_tol * abs(alpha0) / abs(beta2) / (1 + delta_count) / (4 * np.pi ** 2 * grid_size)
|
||||
|
||||
def _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol):
|
||||
return phi_tol / abs(beta2) / (1 + delta_count) / delta_z / (4 * np.pi ** 2 * grid_size)
|
||||
|
||||
grid_size = sim_params.nli_params.wdm_grid_size
|
||||
delta_z = sim_params.raman_params.space_resolution
|
||||
alpha0 = fiber_params.alpha0()
|
||||
beta2 = fiber_params.beta2
|
||||
k_tol = sim_params.nli_params.dispersion_tolerance
|
||||
phi_tol = sim_params.nli_params.phase_shift_tollerance
|
||||
f_pump_resolution, method_f_pump, res_dict_pump = \
|
||||
_get_freq_res_k_phi(0, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol)
|
||||
f_cut_resolution = {}
|
||||
method_f_cut = {}
|
||||
res_dict_cut = {}
|
||||
for cut_carrier in carriers:
|
||||
delta_number = cut_carrier.channel_number - carrier.channel_number
|
||||
delta_count = abs(delta_number)
|
||||
f_res, method, res_dict = \
|
||||
_get_freq_res_k_phi(delta_count, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol)
|
||||
f_cut_resolution[f'delta_{delta_number}'] = f_res
|
||||
method_f_cut[delta_number] = method
|
||||
res_dict_cut[delta_number] = res_dict
|
||||
return [f_cut_resolution, f_pump_resolution, (method_f_cut, method_f_pump), (res_dict_cut, res_dict_pump)]
|
||||
|
||||
def raised_cosine_comb(f, *carriers):
|
||||
""" Returns an array storing the PSD of a WDM comb of raised cosine shaped
|
||||
channels at the input frequencies defined in array f
|
||||
:param f: numpy array of frequencies in Hz
|
||||
:param carriers: namedtuple describing the WDM comb
|
||||
:return: PSD of the WDM comb evaluated over f
|
||||
"""
|
||||
psd = np.zeros(np.shape(f))
|
||||
for carrier in carriers:
|
||||
f_nch = carrier.frequency
|
||||
g_ch = carrier.power.signal / carrier.baud_rate
|
||||
ts = 1 / carrier.baud_rate
|
||||
passband = (1 - carrier.roll_off) / (2 / carrier.baud_rate)
|
||||
stopband = (1 + carrier.roll_off) / (2 / carrier.baud_rate)
|
||||
ff = np.abs(f - f_nch)
|
||||
tf = ff - passband
|
||||
if carrier.roll_off == 0:
|
||||
psd = np.where(tf <= 0, g_ch, 0.) + psd
|
||||
else:
|
||||
psd = g_ch * (np.where(tf <= 0, 1., 0.) + 1 / 2 * (1 + np.cos(np.pi * ts / carrier.roll_off * tf)) *
|
||||
np.where(tf > 0, 1., 0.) * np.where(np.abs(ff) <= stopband, 1., 0.)) + psd
|
||||
return psd
|
||||
|
||||
class RamanSolver:
|
||||
def __init__(self, raman_params=None, fiber_params=None):
|
||||
""" Initialize the fiber object with its physical parameters
|
||||
:param length: fiber length in m.
|
||||
:param alphap: fiber power attenuation coefficient vs frequency in 1/m. numpy array
|
||||
:param freq_alpha: frequency axis of alphap in Hz. numpy array
|
||||
:param cr_raman: Raman efficiency vs frequency offset in 1/W/m. numpy array
|
||||
:param freq_cr: reference frequency offset axis for cr_raman. numpy array
|
||||
:param raman_params: namedtuple containing the solver parameters (optional).
|
||||
"""
|
||||
self.fiber_params = fiber_params
|
||||
self.raman_params = raman_params
|
||||
self._carriers = None
|
||||
self._stimulated_raman_scattering = None
|
||||
self._spontaneous_raman_scattering = None
|
||||
|
||||
@property
|
||||
def fiber_params(self):
|
||||
return self._fiber_params
|
||||
|
||||
@fiber_params.setter
|
||||
def fiber_params(self, fiber_params):
|
||||
self._stimulated_raman_scattering = None
|
||||
self._fiber_params = fiber_params
|
||||
|
||||
@property
|
||||
def carriers(self):
|
||||
return self._carriers
|
||||
|
||||
@carriers.setter
|
||||
def carriers(self, carriers):
|
||||
"""
|
||||
:param carriers: tuple of namedtuples containing information about carriers
|
||||
:return:
|
||||
"""
|
||||
self._carriers = carriers
|
||||
self._stimulated_raman_scattering = None
|
||||
|
||||
@property
|
||||
def raman_pumps(self):
|
||||
return self._raman_pumps
|
||||
|
||||
@raman_pumps.setter
|
||||
def raman_pumps(self, raman_pumps):
|
||||
self._raman_pumps = raman_pumps
|
||||
self._stimulated_raman_scattering = None
|
||||
|
||||
@property
|
||||
def raman_params(self):
|
||||
return self._raman_params
|
||||
|
||||
@raman_params.setter
|
||||
def raman_params(self, raman_params):
|
||||
"""
|
||||
:param raman_params: namedtuple containing the solver parameters (optional).
|
||||
:return:
|
||||
"""
|
||||
self._raman_params = raman_params
|
||||
self._stimulated_raman_scattering = None
|
||||
self._spontaneous_raman_scattering = None
|
||||
|
||||
@property
|
||||
def spontaneous_raman_scattering(self):
|
||||
if self._spontaneous_raman_scattering is None:
|
||||
# SET STUFF
|
||||
loss_coef = self.fiber_params.loss_coef
|
||||
raman_efficiency = self.fiber_params.raman_efficiency
|
||||
temperature = self.fiber_params.temperature
|
||||
carriers = self.carriers
|
||||
raman_pumps = self.raman_pumps
|
||||
|
||||
logger.debug('Start computing fiber Spontaneous Raman Scattering')
|
||||
power_spectrum, freq_array, prop_direct, bn_array = self._compute_power_spectrum(carriers, raman_pumps)
|
||||
|
||||
if not hasattr(loss_coef, 'alpha_power'):
|
||||
alphap_fiber = loss_coef * np.ones(freq_array.shape)
|
||||
else:
|
||||
interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power'])
|
||||
alphap_fiber = interp_alphap(freq_array)
|
||||
|
||||
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
|
||||
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
|
||||
cr = interp_cr(freq_diff)
|
||||
|
||||
# z propagation axis
|
||||
z_array = self._stimulated_raman_scattering.z
|
||||
ase_bc = np.zeros(freq_array.shape)
|
||||
|
||||
# calculate ase power
|
||||
spontaneous_raman_scattering = self._int_spontaneous_raman(z_array, self._stimulated_raman_scattering.power,
|
||||
alphap_fiber, freq_array, cr, freq_diff, ase_bc,
|
||||
bn_array, temperature)
|
||||
|
||||
setattr(spontaneous_raman_scattering, 'frequency', freq_array)
|
||||
setattr(spontaneous_raman_scattering, 'z', z_array)
|
||||
setattr(spontaneous_raman_scattering, 'power', spontaneous_raman_scattering.x)
|
||||
delattr(spontaneous_raman_scattering, 'x')
|
||||
|
||||
logger.debug(spontaneous_raman_scattering.message)
|
||||
|
||||
self._spontaneous_raman_scattering = spontaneous_raman_scattering
|
||||
|
||||
return self._spontaneous_raman_scattering
|
||||
|
||||
@staticmethod
|
||||
def _compute_power_spectrum(carriers, raman_pumps=None):
|
||||
"""
|
||||
Rearrangement of spectral and Raman pump information to make them compatible with Raman solver
|
||||
:param carriers: a tuple of namedtuples describing the transmitted channels
|
||||
:param raman_pumps: a namedtuple describing the Raman pumps
|
||||
:return:
|
||||
"""
|
||||
|
||||
# Signal power spectrum
|
||||
pow_array = np.array([])
|
||||
f_array = np.array([])
|
||||
noise_bandwidth_array = np.array([])
|
||||
for carrier in sorted(carriers, key=attrgetter('frequency')):
|
||||
f_array = np.append(f_array, carrier.frequency)
|
||||
pow_array = np.append(pow_array, carrier.power.signal)
|
||||
ref_bw = carrier.baud_rate
|
||||
noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw)
|
||||
|
||||
propagation_direction = np.ones(len(f_array))
|
||||
|
||||
# Raman pump power spectrum
|
||||
if raman_pumps:
|
||||
for pump in raman_pumps:
|
||||
pow_array = np.append(pow_array, pump.power)
|
||||
f_array = np.append(f_array, pump.frequency)
|
||||
direction = +1 if pump.propagation_direction.lower() == 'coprop' else -1
|
||||
propagation_direction = np.append(propagation_direction, direction)
|
||||
noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw)
|
||||
|
||||
# Final sorting
|
||||
ind = np.argsort(f_array)
|
||||
f_array = f_array[ind]
|
||||
pow_array = pow_array[ind]
|
||||
propagation_direction = propagation_direction[ind]
|
||||
|
||||
return pow_array, f_array, propagation_direction, noise_bandwidth_array
|
||||
|
||||
def _int_spontaneous_raman(self, z_array, raman_matrix, alphap_fiber, freq_array, cr_raman_matrix, freq_diff, ase_bc, bn_array, temperature):
|
||||
spontaneous_raman_scattering = OptimizeResult()
|
||||
|
||||
dx = self.raman_params.space_resolution
|
||||
h = ph.value('Planck constant')
|
||||
kb = ph.value('Boltzmann constant')
|
||||
|
||||
power_ase = np.nan * np.ones(raman_matrix.shape)
|
||||
int_pump = cumtrapz(raman_matrix, z_array, dx=dx, axis=1, initial=0)
|
||||
|
||||
for f_ind, f_ase in enumerate(freq_array):
|
||||
cr_raman = cr_raman_matrix[f_ind, :]
|
||||
vibrational_loss = f_ase / freq_array[:f_ind]
|
||||
eta = 1/(np.exp((h*freq_diff[f_ind, f_ind+1:])/(kb*temperature)) - 1)
|
||||
|
||||
int_fiber_loss = -alphap_fiber[f_ind] * z_array
|
||||
int_raman_loss = np.sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(), axis=0)
|
||||
int_raman_gain = np.sum((cr_raman[f_ind + 1:] * int_pump[f_ind + 1:, :].transpose()).transpose(), axis=0)
|
||||
|
||||
int_gain_loss = int_fiber_loss + int_raman_gain + int_raman_loss
|
||||
|
||||
new_ase = np.sum((cr_raman[f_ind+1:] * (1 + eta) * raman_matrix[f_ind+1:, :].transpose()).transpose() * h * f_ase * bn_array[f_ind], axis=0)
|
||||
|
||||
bc_evolution = ase_bc[f_ind] * np.exp(int_gain_loss)
|
||||
ase_evolution = np.exp(int_gain_loss) * cumtrapz(new_ase*np.exp(-int_gain_loss), z_array, dx=dx, initial=0)
|
||||
|
||||
power_ase[f_ind, :] = bc_evolution + ase_evolution
|
||||
|
||||
spontaneous_raman_scattering.x = 2 * power_ase
|
||||
spontaneous_raman_scattering.success = True
|
||||
spontaneous_raman_scattering.message = "Spontaneous Raman Scattering evaluated successfully"
|
||||
|
||||
return spontaneous_raman_scattering
|
||||
|
||||
def stimulated_raman_scattering(self, carriers, raman_pumps=None):
|
||||
""" Returns stimulated Raman scattering solution including
|
||||
fiber gain/loss profile.
|
||||
:return: self._stimulated_raman_scattering: the SRS problem solution.
|
||||
scipy.interpolate.PPoly instance
|
||||
"""
|
||||
|
||||
if self._stimulated_raman_scattering is None:
|
||||
# fiber parameters
|
||||
fiber_length = self.fiber_params.length
|
||||
loss_coef = self.fiber_params.loss_coef
|
||||
if self.raman_params.flag_raman:
|
||||
raman_efficiency = self.fiber_params.raman_efficiency
|
||||
else:
|
||||
raman_efficiency = self.fiber_params.raman_efficiency
|
||||
raman_efficiency['cr'] = np.array(raman_efficiency['cr']) * 0
|
||||
# raman solver parameters
|
||||
z_resolution = self.raman_params.space_resolution
|
||||
tolerance = self.raman_params.tolerance
|
||||
|
||||
logger.debug('Start computing fiber Stimulated Raman Scattering')
|
||||
|
||||
power_spectrum, freq_array, prop_direct, _ = self._compute_power_spectrum(carriers, raman_pumps)
|
||||
|
||||
if not hasattr(loss_coef, 'alpha_power'):
|
||||
alphap_fiber = loss_coef * np.ones(freq_array.shape)
|
||||
else:
|
||||
interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power'])
|
||||
alphap_fiber = interp_alphap(freq_array)
|
||||
|
||||
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
|
||||
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
|
||||
cr = interp_cr(freq_diff)
|
||||
|
||||
# z propagation axis
|
||||
z = np.arange(0, fiber_length+1, z_resolution)
|
||||
|
||||
ode_function = lambda z, p: self._ode_stimulated_raman(z, p, alphap_fiber, freq_array, cr, prop_direct)
|
||||
boundary_residual = lambda ya, yb: self._residuals_stimulated_raman(ya, yb, power_spectrum, prop_direct)
|
||||
initial_guess_conditions = self._initial_guess_stimulated_raman(z, power_spectrum, alphap_fiber, prop_direct)
|
||||
|
||||
# ODE SOLVER
|
||||
stimulated_raman_scattering = solve_bvp(ode_function, boundary_residual, z, initial_guess_conditions, tol=tolerance)
|
||||
|
||||
rho = (stimulated_raman_scattering.y.transpose() / power_spectrum).transpose()
|
||||
rho = np.sqrt(rho) # From power attenuation to field attenuation
|
||||
setattr(stimulated_raman_scattering, 'frequency', freq_array)
|
||||
setattr(stimulated_raman_scattering, 'z', stimulated_raman_scattering.x)
|
||||
setattr(stimulated_raman_scattering, 'rho', rho)
|
||||
setattr(stimulated_raman_scattering, 'power', stimulated_raman_scattering.y)
|
||||
delattr(stimulated_raman_scattering, 'x')
|
||||
delattr(stimulated_raman_scattering, 'y')
|
||||
|
||||
self.carriers = carriers
|
||||
self.raman_pumps = raman_pumps
|
||||
self._stimulated_raman_scattering = stimulated_raman_scattering
|
||||
|
||||
return self._stimulated_raman_scattering
|
||||
|
||||
def _residuals_stimulated_raman(self, ya, yb, power_spectrum, prop_direct):
|
||||
|
||||
computed_boundary_value = np.zeros(ya.size)
|
||||
|
||||
for index, direction in enumerate(prop_direct):
|
||||
if direction == +1:
|
||||
computed_boundary_value[index] = ya[index]
|
||||
else:
|
||||
computed_boundary_value[index] = yb[index]
|
||||
|
||||
return power_spectrum - computed_boundary_value
|
||||
|
||||
def _initial_guess_stimulated_raman(self, z, power_spectrum, alphap_fiber, prop_direct):
|
||||
""" Computes the initial guess knowing the boundary conditions
|
||||
:param z: patial axis [m]. numpy array
|
||||
:param power_spectrum: power in each frequency slice [W]. Frequency axis is defined by freq_array. numpy array
|
||||
:param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array
|
||||
:param prop_direct: indicates the propagation direction of each power slice in power_spectrum:
|
||||
+1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array
|
||||
:return: power_guess: guess on the initial conditions [W]. The first ndarray index identifies the frequency slice,
|
||||
the second ndarray index identifies the step in z. ndarray
|
||||
"""
|
||||
|
||||
power_guess = np.empty((power_spectrum.size, z.size))
|
||||
for f_index, power_slice in enumerate(power_spectrum):
|
||||
if prop_direct[f_index] == +1:
|
||||
power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z) * power_slice
|
||||
else:
|
||||
power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z[::-1]) * power_slice
|
||||
|
||||
return power_guess
|
||||
|
||||
def _ode_stimulated_raman(self, z, power_spectrum, alphap_fiber, freq_array, cr_raman_matrix, prop_direct):
|
||||
""" Aim of ode_raman is to implement the set of ordinary differential equations (ODEs) describing the Raman effect.
|
||||
:param z: spatial axis (unused).
|
||||
:param power_spectrum: power in each frequency slice [W]. Frequency axis is defined by freq_array. numpy array. Size n
|
||||
:param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array. Size n
|
||||
:param freq_array: reference frequency axis [Hz]. numpy array. Size n
|
||||
:param cr_raman: Cr(f) Raman gain efficiency variation in frequency [1/W/m]. Frequency defined by freq_array. numpy ndarray. Size nxn
|
||||
:param prop_direct: indicates the propagation direction of each power slice in power_spectrum:
|
||||
+1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array. Size n
|
||||
:return: dP/dz: the power variation in dz [W/m]. numpy array. Size n
|
||||
"""
|
||||
|
||||
dpdz = np.nan * np.ones(power_spectrum.shape)
|
||||
for f_ind, power in enumerate(power_spectrum):
|
||||
cr_raman = cr_raman_matrix[f_ind, :]
|
||||
vibrational_loss = freq_array[f_ind] / freq_array[:f_ind]
|
||||
|
||||
for z_ind, power_sample in enumerate(power):
|
||||
raman_gain = np.sum(cr_raman[f_ind+1:] * power_spectrum[f_ind+1:, z_ind])
|
||||
raman_loss = np.sum(vibrational_loss * cr_raman[:f_ind] * power_spectrum[:f_ind, z_ind])
|
||||
|
||||
dpdz_element = prop_direct[f_ind] * (-alphap_fiber[f_ind] + raman_gain - raman_loss) * power_sample
|
||||
dpdz[f_ind][z_ind] = dpdz_element
|
||||
|
||||
return np.vstack(dpdz)
|
||||
|
||||
class NliSolver:
|
||||
""" This class implements the NLI models.
|
||||
Model and method can be specified in `self.nli_params.method`.
|
||||
List of implemented methods:
|
||||
'gn_model_analytic': brute force triple integral solution
|
||||
'ggn_spectrally_separated_xpm_spm': XPM plus SPM
|
||||
"""
|
||||
|
||||
def __init__(self, nli_params=None, fiber_params=None):
|
||||
""" Initialize the fiber object with its physical parameters
|
||||
"""
|
||||
self.fiber_params = fiber_params
|
||||
self.nli_params = nli_params
|
||||
self.stimulated_raman_scattering = None
|
||||
|
||||
@property
|
||||
def fiber_params(self):
|
||||
return self._fiber_params
|
||||
|
||||
@fiber_params.setter
|
||||
def fiber_params(self, fiber_params):
|
||||
self._fiber_params = fiber_params
|
||||
|
||||
@property
|
||||
def stimulated_raman_scattering(self):
|
||||
return self._stimulated_raman_scattering
|
||||
|
||||
@stimulated_raman_scattering.setter
|
||||
def stimulated_raman_scattering(self, stimulated_raman_scattering):
|
||||
self._stimulated_raman_scattering = stimulated_raman_scattering
|
||||
|
||||
@property
|
||||
def nli_params(self):
|
||||
return self._nli_params
|
||||
|
||||
@nli_params.setter
|
||||
def nli_params(self, nli_params):
|
||||
"""
|
||||
:param model_params: namedtuple containing the parameters used to compute the NLI.
|
||||
"""
|
||||
self._nli_params = nli_params
|
||||
|
||||
def compute_nli(self, carrier, *carriers):
|
||||
""" Compute NLI power generated by the WDM comb `*carriers` on the channel under test `carrier`
|
||||
at the end of the fiber span.
|
||||
"""
|
||||
if 'gn_model_analytic' == self.nli_params.nli_method_name.lower():
|
||||
carrier_nli = self._gn_analytic(carrier, *carriers)
|
||||
elif 'ggn_spectrally_separated' in self.nli_params.nli_method_name.lower():
|
||||
eta_matrix = self._compute_eta_matrix(carrier, *carriers)
|
||||
carrier_nli = self._carrier_nli_from_eta_matrix(eta_matrix, carrier, *carriers)
|
||||
else:
|
||||
raise ValueError(f'Method {self.nli_params.method_nli} not implemented.')
|
||||
|
||||
return carrier_nli
|
||||
|
||||
@staticmethod
|
||||
def _carrier_nli_from_eta_matrix(eta_matrix, carrier, *carriers):
|
||||
carrier_nli = 0
|
||||
for pump_carrier_1 in carriers:
|
||||
for pump_carrier_2 in carriers:
|
||||
carrier_nli += eta_matrix[pump_carrier_1.channel_number-1, pump_carrier_2.channel_number-1] * \
|
||||
pump_carrier_1.power.signal * pump_carrier_2.power.signal
|
||||
carrier_nli *= carrier.power.signal
|
||||
|
||||
return carrier_nli
|
||||
|
||||
def _compute_eta_matrix(self, carrier_cut, *carriers):
|
||||
cut_index = carrier_cut.channel_number - 1
|
||||
# Matrix initialization
|
||||
matrix_size = max(carriers, key=lambda x: getattr(x, 'channel_number')).channel_number
|
||||
eta_matrix = np.zeros(shape=(matrix_size, matrix_size))
|
||||
|
||||
# SPM
|
||||
logger.debug(f'Start computing SPM on channel #{carrier_cut.channel_number}')
|
||||
# SPM GGN
|
||||
if 'ggn' in self.nli_params.nli_method_name.lower():
|
||||
partial_nli = self._generalized_spectrally_separated_spm(carrier_cut)
|
||||
# SPM GN
|
||||
elif 'gn' in self.nli_params.nli_method_name.lower():
|
||||
partial_nli = self._gn_analytic(carrier_cut, *[carrier_cut])
|
||||
eta_matrix[cut_index, cut_index] = partial_nli / (carrier_cut.power.signal**3)
|
||||
|
||||
# XPM
|
||||
for pump_carrier in carriers:
|
||||
pump_index = pump_carrier.channel_number - 1
|
||||
if not (cut_index == pump_index):
|
||||
logger.debug(f'Start computing XPM on channel #{carrier_cut.channel_number} '
|
||||
f'from channel #{pump_carrier.channel_number}')
|
||||
# XPM GGN
|
||||
if 'ggn' in self.nli_params.nli_method_name.lower():
|
||||
partial_nli = self._generalized_spectrally_separated_xpm(carrier_cut, pump_carrier)
|
||||
# XPM GGN
|
||||
elif 'gn' in self.nli_params.nli_method_name.lower():
|
||||
partial_nli = self._gn_analytic(carrier_cut, *[pump_carrier])
|
||||
eta_matrix[pump_index, pump_index] = partial_nli /\
|
||||
(carrier_cut.power.signal * pump_carrier.power.signal**2)
|
||||
return eta_matrix
|
||||
|
||||
# Methods for computing GN-model
|
||||
def _gn_analytic(self, carrier, *carriers):
|
||||
""" Computes the nonlinear interference power on a single carrier.
|
||||
The method uses eq. 120 from arXiv:1209.0394.
|
||||
:param carrier: the signal under analysis
|
||||
:param carriers: the full WDM comb
|
||||
:return: carrier_nli: the amount of nonlinear interference in W on the carrier under analysis
|
||||
"""
|
||||
alpha = self.fiber_params.alpha0() / 2
|
||||
beta2 = self.fiber_params.beta2
|
||||
gamma = self.fiber_params.gamma
|
||||
length = self.fiber_params.length
|
||||
effective_length = (1 - np.exp(-2 * alpha * length)) / (2 * alpha)
|
||||
asymptotic_length = 1 / (2 * alpha)
|
||||
|
||||
g_nli = 0
|
||||
for interfering_carrier in carriers:
|
||||
g_interfearing = interfering_carrier.power.signal / interfering_carrier.baud_rate
|
||||
g_signal = carrier.power.signal / carrier.baud_rate
|
||||
g_nli += g_interfearing**2 * g_signal \
|
||||
* _psi(carrier, interfering_carrier, beta2=self.fiber_params.beta2, asymptotic_length=1/self.fiber_params.alpha0())
|
||||
g_nli *= (16.0 / 27.0) * (gamma * effective_length)**2 /\
|
||||
(2 * np.pi * abs(beta2) * asymptotic_length)
|
||||
carrier_nli = carrier.baud_rate * g_nli
|
||||
return carrier_nli
|
||||
|
||||
# Methods for computing the GGN-model
|
||||
def _generalized_spectrally_separated_spm(self, carrier):
|
||||
f_cut_resolution = self.nli_params.f_cut_resolution['delta_0']
|
||||
f_eval = carrier.frequency
|
||||
g_cut = (carrier.power.signal / carrier.baud_rate)
|
||||
|
||||
spm_nli = carrier.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_cut**3 * \
|
||||
self._generalized_psi(carrier, carrier, f_eval, f_cut_resolution, f_cut_resolution)
|
||||
return spm_nli
|
||||
|
||||
def _generalized_spectrally_separated_xpm(self, carrier_cut, pump_carrier):
|
||||
delta_index = pump_carrier.channel_number - carrier_cut.channel_number
|
||||
f_cut_resolution = self.nli_params.f_cut_resolution[f'delta_{delta_index}']
|
||||
f_pump_resolution = self.nli_params.f_pump_resolution
|
||||
f_eval = carrier_cut.frequency
|
||||
g_pump = (pump_carrier.power.signal / pump_carrier.baud_rate)
|
||||
g_cut = (carrier_cut.power.signal / carrier_cut.baud_rate)
|
||||
frequency_offset_threshold = self._frequency_offset_threshold(pump_carrier.baud_rate)
|
||||
if abs(carrier_cut.frequency - pump_carrier.frequency) <= frequency_offset_threshold:
|
||||
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \
|
||||
2 * self._generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution)
|
||||
else:
|
||||
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \
|
||||
2 * self._fast_generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution)
|
||||
return xpm_nli
|
||||
|
||||
def _fast_generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution):
|
||||
""" It computes the generalized psi function similarly to the one used in the GN model
|
||||
:return: generalized_psi
|
||||
"""
|
||||
# Fiber parameters
|
||||
alpha0 = self.fiber_params.alpha0(f_eval)
|
||||
beta2 = self.fiber_params.beta2
|
||||
beta3 = self.fiber_params.beta3
|
||||
f_ref_beta = self.fiber_params.f_ref_beta
|
||||
z = self.stimulated_raman_scattering.z
|
||||
frequency_rho = self.stimulated_raman_scattering.frequency
|
||||
rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
|
||||
if len(frequency_rho) == 1:
|
||||
rho_function = lambda f: rho_norm[0, :]
|
||||
else:
|
||||
rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate')
|
||||
rho_norm_pump = rho_function(pump_carrier.frequency)
|
||||
|
||||
f1_array = np.array([pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
|
||||
pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2)])
|
||||
f2_array = np.arange(carrier_cut.frequency,
|
||||
carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
|
||||
f_cut_resolution) # Only positive f2 is used since integrand_f2 is symmetric
|
||||
|
||||
integrand_f1 = np.zeros(len(f1_array))
|
||||
for f1_index, f1 in enumerate(f1_array):
|
||||
delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
|
||||
(beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
|
||||
integrand_f2 = self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0)
|
||||
integrand_f1[f1_index] = 2 * np.trapz(integrand_f2, f2_array) # 2x since integrand_f2 is symmetric in f2
|
||||
generalized_psi = 0.5 * sum(integrand_f1) * pump_carrier.baud_rate
|
||||
return generalized_psi
|
||||
|
||||
def _generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution):
|
||||
""" It computes the generalized psi function similarly to the one used in the GN model
|
||||
:return: generalized_psi
|
||||
"""
|
||||
# Fiber parameters
|
||||
alpha0 = self.fiber_params.alpha0(f_eval)
|
||||
beta2 = self.fiber_params.beta2
|
||||
beta3 = self.fiber_params.beta3
|
||||
f_ref_beta = self.fiber_params.f_ref_beta
|
||||
z = self.stimulated_raman_scattering.z
|
||||
frequency_rho = self.stimulated_raman_scattering.frequency
|
||||
rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
|
||||
if len(frequency_rho) == 1:
|
||||
rho_function = lambda f: rho_norm[0, :]
|
||||
else:
|
||||
rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate')
|
||||
rho_norm_pump = rho_function(pump_carrier.frequency)
|
||||
|
||||
f1_array = np.arange(pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
|
||||
pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
|
||||
f_pump_resolution)
|
||||
f2_array = np.arange(carrier_cut.frequency - (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
|
||||
carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
|
||||
f_cut_resolution)
|
||||
psd1 = raised_cosine_comb(f1_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal)
|
||||
|
||||
integrand_f1 = np.zeros(len(f1_array))
|
||||
for f1_index, (f1, psd1_sample) in enumerate(zip(f1_array, psd1)):
|
||||
f3_array = f1 + f2_array - f_eval
|
||||
psd2 = raised_cosine_comb(f2_array, carrier_cut) * (carrier_cut.baud_rate / carrier_cut.power.signal)
|
||||
psd3 = raised_cosine_comb(f3_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal)
|
||||
ggg = psd1_sample * psd2 * psd3
|
||||
|
||||
delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
|
||||
(beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
|
||||
|
||||
integrand_f2 = ggg * self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0)
|
||||
integrand_f1[f1_index] = np.trapz(integrand_f2, f2_array)
|
||||
generalized_psi = np.trapz(integrand_f1, f1_array)
|
||||
return generalized_psi
|
||||
|
||||
@staticmethod
|
||||
def _generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0):
|
||||
w = 1j * delta_beta - alpha0
|
||||
generalized_rho_nli = (rho_norm_pump[-1]**2 * np.exp(w * z[-1]) - rho_norm_pump[0]**2 * np.exp(w * z[0])) / w
|
||||
for z_ind in range(0, len(z) - 1):
|
||||
derivative_rho = (rho_norm_pump[z_ind + 1]**2 - rho_norm_pump[z_ind]**2) / (z[z_ind + 1] - z[z_ind])
|
||||
generalized_rho_nli -= derivative_rho * (np.exp(w * z[z_ind + 1]) - np.exp(w * z[z_ind])) / (w**2)
|
||||
generalized_rho_nli = np.abs(generalized_rho_nli)**2
|
||||
return generalized_rho_nli
|
||||
|
||||
def _frequency_offset_threshold(self, symbol_rate):
|
||||
k_ref = 5
|
||||
beta2_ref = 21.3e-27
|
||||
delta_f_ref = 50e9
|
||||
rs_ref = 32e9
|
||||
freq_offset_th = ((k_ref * delta_f_ref) * rs_ref * beta2_ref) / (self.fiber_params.beta2 * symbol_rate)
|
||||
return freq_offset_th
|
||||
|
||||
def _psi(carrier, interfering_carrier, beta2, asymptotic_length):
|
||||
"""Calculates eq. 123 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__"""
|
||||
|
||||
if carrier.channel_number == interfering_carrier.channel_number: # SCI, SPM
|
||||
psi = np.arcsinh(0.5 * np.pi**2 * asymptotic_length * abs(beta2) * carrier.baud_rate**2)
|
||||
else: # XCI, XPM
|
||||
delta_f = carrier.frequency - interfering_carrier.frequency
|
||||
psi = np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) *
|
||||
carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
|
||||
psi -= np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) *
|
||||
carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
|
||||
return psi
|
||||
@@ -22,8 +22,9 @@ from json import dumps
|
||||
from pathlib import Path
|
||||
from gnpy.core.equipment import load_equipment
|
||||
from gnpy.core.utils import db2lin, lin2db
|
||||
from gnpy.core.exceptions import ServiceError
|
||||
|
||||
SERVICES_COLUMN = 11
|
||||
SERVICES_COLUMN = 12
|
||||
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
|
||||
|
||||
all_rows = lambda sheet, start=0: (sheet.row(x) for x in range(start, sheet.nrows))
|
||||
@@ -31,9 +32,9 @@ logger = getLogger(__name__)
|
||||
|
||||
# Type for input data
|
||||
class Request(namedtuple('Request', 'request_id source destination trx_type mode \
|
||||
spacing power nb_channel disjoint_from nodes_list is_loose')):
|
||||
def __new__(cls, request_id, source, destination, trx_type, mode , spacing , power , nb_channel , disjoint_from ='' , nodes_list = None, is_loose = ''):
|
||||
return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose)
|
||||
spacing power nb_channel disjoint_from nodes_list is_loose path_bandwidth')):
|
||||
def __new__(cls, request_id, source, destination, trx_type, mode=None , spacing= None , power = None, nb_channel = None , disjoint_from ='' , nodes_list = None, is_loose = '', path_bandwidth = None):
|
||||
return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose, path_bandwidth)
|
||||
|
||||
# Type for output data: // from dutc
|
||||
class Element:
|
||||
@@ -43,147 +44,189 @@ class Element:
|
||||
return hash((type(self), self.uid))
|
||||
|
||||
class Request_element(Element):
|
||||
def __init__(self,Request,eqpt_filename):
|
||||
def __init__(self, Request, eqpt_filename, bidir):
|
||||
# request_id is str
|
||||
# excel has automatic number formatting that adds .0 on integer values
|
||||
# the next lines recover the pure int value, assuming this .0 is unwanted
|
||||
if not isinstance(Request.request_id,str):
|
||||
value = str(int(Request.request_id))
|
||||
if value.endswith('.0'):
|
||||
value = value[:-2]
|
||||
self.request_id = value
|
||||
else:
|
||||
self.request_id = Request.request_id
|
||||
self.source = Request.source
|
||||
self.destination = Request.destination
|
||||
self.request_id = correct_xlrd_int_to_str_reading(Request.request_id)
|
||||
self.source = f'trx {Request.source}'
|
||||
self.destination = f'trx {Request.destination}'
|
||||
# TODO: the automatic naming generated by excel parser requires that source and dest name
|
||||
# be a string starting with 'trx' : this is manually added here.
|
||||
self.srctpid = f'trx {Request.source}'
|
||||
self.dsttpid = f'trx {Request.destination}'
|
||||
self.bidir = bidir
|
||||
# test that trx_type belongs to eqpt_config.json
|
||||
# if not replace it with a default
|
||||
equipment = load_equipment(eqpt_filename)
|
||||
try :
|
||||
if equipment['Transceiver'][Request.trx_type]:
|
||||
self.trx_type = Request.trx_type
|
||||
if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]:
|
||||
self.trx_type = correct_xlrd_int_to_str_reading(Request.trx_type)
|
||||
if Request.mode is not None :
|
||||
Requestmode = correct_xlrd_int_to_str_reading(Request.mode)
|
||||
if [mode for mode in equipment['Transceiver'][Request.trx_type].mode if mode['format'] == Requestmode]:
|
||||
self.mode = Requestmode
|
||||
else :
|
||||
msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Requestmode}\' in eqpt library \nComputation stopped.'
|
||||
#print(msg)
|
||||
logger.critical(msg)
|
||||
exit(1)
|
||||
else:
|
||||
Requestmode = None
|
||||
self.mode = Request.mode
|
||||
except KeyError:
|
||||
msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.'
|
||||
msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Request.mode}\' in eqpt library \nComputation stopped.'
|
||||
#print(msg)
|
||||
logger.critical(msg)
|
||||
exit()
|
||||
raise ServiceError(msg)
|
||||
# excel input are in GHz and dBm
|
||||
self.spacing = Request.spacing * 1e9
|
||||
self.power = db2lin(Request.power) * 1e-3
|
||||
self.nb_channel = int(Request.nb_channel)
|
||||
if not isinstance(Request.disjoint_from,str):
|
||||
value = str(int(Request.disjoint_from))
|
||||
if value.endswith('.0'):
|
||||
value = value[:-2]
|
||||
if Request.spacing is not None:
|
||||
self.spacing = Request.spacing * 1e9
|
||||
else:
|
||||
value = Request.disjoint_from
|
||||
self.disjoint_from = [n for n in value.split()]
|
||||
msg = f'Request {self.request_id} missing spacing: spacing is mandatory.\ncomputation stopped'
|
||||
logger.critical(msg)
|
||||
raise ServiceError(msg)
|
||||
if Request.power is not None:
|
||||
self.power = db2lin(Request.power) * 1e-3
|
||||
else:
|
||||
self.power = None
|
||||
if Request.nb_channel is not None :
|
||||
self.nb_channel = int(Request.nb_channel)
|
||||
else:
|
||||
self.nb_channel = None
|
||||
|
||||
value = correct_xlrd_int_to_str_reading(Request.disjoint_from)
|
||||
self.disjoint_from = [n for n in value.split(' | ') if value]
|
||||
self.nodes_list = []
|
||||
if Request.nodes_list :
|
||||
self.nodes_list = Request.nodes_list.split(' | ')
|
||||
|
||||
# cleaning the list of nodes to remove source and destination
|
||||
# (because the remaining of the program assumes that the nodes list are nodes
|
||||
# on the path and should not include source and destination)
|
||||
try :
|
||||
self.nodes_list.remove(self.source)
|
||||
msg = f'{self.source} removed from explicit path node-list'
|
||||
logger.info(msg)
|
||||
# print(msg)
|
||||
except ValueError:
|
||||
msg = f'{self.source} already removed from explicit path node-list'
|
||||
logger.info(msg)
|
||||
# print(msg)
|
||||
|
||||
try :
|
||||
self.nodes_list.remove(self.destination)
|
||||
msg = f'{self.destination} removed from explicit path node-list'
|
||||
logger.info(msg)
|
||||
# print(msg)
|
||||
except ValueError:
|
||||
msg = f'{self.destination} already removed from explicit path node-list'
|
||||
logger.info(msg)
|
||||
# print(msg)
|
||||
|
||||
self.loose = 'loose'
|
||||
# the excel parser applies the same hop-type to all nodes in the route nodes_list.
|
||||
# user can change this per node in the generated json
|
||||
self.loose = 'LOOSE'
|
||||
if Request.is_loose == 'no' :
|
||||
self.loose = 'strict'
|
||||
self.loose = 'STRICT'
|
||||
self.path_bandwidth = None
|
||||
if Request.path_bandwidth is not None:
|
||||
self.path_bandwidth = Request.path_bandwidth * 1e9
|
||||
else:
|
||||
self.path_bandwidth = 0
|
||||
|
||||
uid = property(lambda self: repr(self))
|
||||
@property
|
||||
def pathrequest(self):
|
||||
return {
|
||||
# Default assumption for bidir is False
|
||||
req_dictionnary = {
|
||||
'request-id':self.request_id,
|
||||
'source': self.source,
|
||||
'destination': self.destination,
|
||||
'src-tp-id': self.srctpid,
|
||||
'dst-tp-id': self.dsttpid,
|
||||
'bidirectional': self.bidir,
|
||||
'path-constraints':{
|
||||
'te-bandwidth': {
|
||||
'technology': 'flexi-grid',
|
||||
'trx_type' : self.trx_type,
|
||||
'trx_mode' : self.mode,
|
||||
'effective-freq-slot':[{'n': 'null','m': 'null'}] ,
|
||||
'effective-freq-slot':[{'N': 'null', 'M': 'null'}],
|
||||
'spacing' : self.spacing,
|
||||
'max-nb-of-channel' : self.nb_channel,
|
||||
'output-power' : self.power
|
||||
}
|
||||
},
|
||||
'optimizations': {
|
||||
'explicit-route-include-objects': [
|
||||
{
|
||||
'index': self.nodes_list.index(node),
|
||||
'unnumbered-hop':{
|
||||
'node-id': f'{node}',
|
||||
'link-tp-id': 'link-tp-id is not used',
|
||||
'hop-type': 'loose',
|
||||
'direction': 'direction is not used'
|
||||
},
|
||||
'label-hop':{
|
||||
'te-label': {
|
||||
'generic': 'generic is not used',
|
||||
'direction': 'direction is not used'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.nodes_list:
|
||||
req_dictionnary['explicit-route-objects'] = {}
|
||||
temp = {'route-object-include-exclude' : [
|
||||
{'explicit-route-usage': 'route-include-ero',
|
||||
'index': self.nodes_list.index(node),
|
||||
'num-unnum-hop': {
|
||||
'node-id': f'{node}',
|
||||
'link-tp-id': 'link-tp-id is not used',
|
||||
'hop-type': f'{self.loose}',
|
||||
}
|
||||
}
|
||||
for node in self.nodes_list
|
||||
]
|
||||
|
||||
}
|
||||
}
|
||||
for node in self.nodes_list]
|
||||
}
|
||||
req_dictionnary['explicit-route-objects'] = temp
|
||||
if self.path_bandwidth is not None:
|
||||
req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth
|
||||
|
||||
return req_dictionnary
|
||||
@property
|
||||
def pathsync(self):
|
||||
if self.disjoint_from :
|
||||
return {'synchonization-id':self.request_id,
|
||||
return {'synchronization-id':self.request_id,
|
||||
'svec': {
|
||||
'relaxable' : 'False',
|
||||
'link-diverse': 'True',
|
||||
'node-diverse': 'True',
|
||||
'relaxable' : 'false',
|
||||
'disjointness': 'node link',
|
||||
'request-id-number': [self.request_id]+ [n for n in self.disjoint_from]
|
||||
}
|
||||
}
|
||||
else:
|
||||
return None
|
||||
# TO-DO: avoid multiple entries with same synchronisation vectors
|
||||
@property
|
||||
def json(self):
|
||||
return self.pathrequest , self.pathsync
|
||||
|
||||
def convert_service_sheet(input_filename, eqpt_filename, output_filename='', filter_region=[]):
|
||||
def convert_service_sheet(input_filename, eqpt_filename, output_filename='', bidir=False, filter_region=None):
|
||||
""" converts a service sheet into a json structure
|
||||
"""
|
||||
if filter_region is None:
|
||||
filter_region = []
|
||||
service = parse_excel(input_filename)
|
||||
req = [Request_element(n,eqpt_filename) for n in service]
|
||||
req = [Request_element(n, eqpt_filename, bidir) for n in service]
|
||||
# dumps the output into a json file with name
|
||||
# split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]]
|
||||
if output_filename=='':
|
||||
output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json'
|
||||
# for debug
|
||||
# print(json_filename)
|
||||
data = {
|
||||
'path-request': [n.json[0] for n in req],
|
||||
'synchronisation': [n.json[1] for n in req
|
||||
if n.json[1] is not None]
|
||||
}
|
||||
with open(output_filename, 'w') as f:
|
||||
f.write(dumps(data, indent=2))
|
||||
# if there is no sync vector , do not write any synchronization
|
||||
synchro = [n.json[1] for n in req if n.json[1] is not None]
|
||||
if synchro:
|
||||
data = {
|
||||
'path-request': [n.json[0] for n in req],
|
||||
'synchronization': synchro
|
||||
}
|
||||
else:
|
||||
data = {
|
||||
'path-request': [n.json[0] for n in req]
|
||||
}
|
||||
with open(output_filename, 'w', encoding='utf-8') as f:
|
||||
f.write(dumps(data, indent=2, ensure_ascii=False))
|
||||
return data
|
||||
|
||||
def correct_xlrd_int_to_str_reading(v) :
|
||||
if not isinstance(v,str):
|
||||
value = str(int(v))
|
||||
if value.endswith('.0'):
|
||||
value = value[:-2]
|
||||
else:
|
||||
value = v
|
||||
return value
|
||||
|
||||
# to be used from dutc
|
||||
def parse_row(row, fieldnames):
|
||||
return {f: r.value for f, r in zip(fieldnames, row[0:SERVICES_COLUMN])
|
||||
@@ -197,20 +240,29 @@ def parse_excel(input_filename):
|
||||
return services
|
||||
|
||||
def parse_service_sheet(service_sheet):
|
||||
logger.info(f'Validating headers on {service_sheet.name!r}')
|
||||
header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN]]
|
||||
expected = ['route id', 'Source', 'Destination', 'TRX type', \
|
||||
'Mode', 'System: spacing', 'System: input power (dBm)', 'System: nb of channels',\
|
||||
'routing: disjoint from', 'routing: path', 'routing: is loose?']
|
||||
if header != expected:
|
||||
msg = f'Malformed header on Service sheet: {header} != {expected}'
|
||||
logger.critical(msg)
|
||||
raise ValueError(msg)
|
||||
""" reads each column according to authorized fieldnames. order is not important.
|
||||
"""
|
||||
logger.info(f'Validating headers on {service_sheet.name!r}')
|
||||
# add a test on field to enable the '' field case that arises when columns on the
|
||||
# right hand side are used as comments or drawing in the excel sheet
|
||||
header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN]
|
||||
if len(x.value.strip()) > 0]
|
||||
|
||||
service_fieldnames = 'request_id source destination trx_type mode spacing power nb_channel disjoint_from nodes_list is_loose'.split()
|
||||
# Important Note: it reads all colum on each row so that
|
||||
# it is not possible to write annotation in the excel sheet
|
||||
# outside the SERVICES_COLUMN ... TO BE IMPROVED
|
||||
# request_id should be unique for disjunction constraints (not used yet)
|
||||
for row in all_rows(service_sheet, start=5):
|
||||
yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames))
|
||||
# create a service_fieldname independant from the excel column order
|
||||
# to be compatible with any version of the sheet
|
||||
# the following dictionnary records the excel field names and the corresponding parameter's name
|
||||
|
||||
authorized_fieldnames = {
|
||||
'route id':'request_id', 'Source':'source', 'Destination':'destination', \
|
||||
'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \
|
||||
'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\
|
||||
'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\
|
||||
'routing: is loose?':'is_loose', 'path bandwidth':'path_bandwidth'}
|
||||
try:
|
||||
service_fieldnames = [authorized_fieldnames[e] for e in header]
|
||||
except KeyError:
|
||||
msg = f'Malformed header on Service sheet: {header} field not in {authorized_fieldnames}'
|
||||
logger.critical(msg)
|
||||
raise ValueError(msg)
|
||||
for row in all_rows(service_sheet, start=5):
|
||||
yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames))
|
||||
|
||||
386
gnpy/core/spectrum_assignment.py
Normal file
386
gnpy/core/spectrum_assignment.py
Normal file
@@ -0,0 +1,386 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
gnpy.core.spectrum_assignment
|
||||
=============================
|
||||
|
||||
This module contains the Oms and Bitmap classes and the different method to
|
||||
select and assign spectrum. Spectrum_selection function identifies the free
|
||||
slots and select_candidate selects the candidate spectrum according to
|
||||
strategy: for example first fit
|
||||
oms records its elements, and elements are updated with an oms to have
|
||||
element/oms correspondace
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
from logging import getLogger
|
||||
from math import ceil
|
||||
from gnpy.core.elements import Roadm, Transceiver
|
||||
from gnpy.core.exceptions import SpectrumError
|
||||
|
||||
LOGGER = getLogger(__name__)
|
||||
|
||||
class Bitmap:
|
||||
""" records the spectrum occupation
|
||||
"""
|
||||
def __init__(self, f_min, f_max, grid, guardband=0.15e12, bitmap=None):
|
||||
# n is the min index including guardband. Guardband is require to be sure
|
||||
# that a channel can be assigned with center frequency fmin (means that its
|
||||
# slot occupation goes below freq_index_min
|
||||
n_min = frequency_to_n(f_min-guardband, grid)
|
||||
n_max = frequency_to_n(f_max+guardband, grid) - 1
|
||||
self.n_min = n_min
|
||||
self.n_max = n_max
|
||||
self.freq_index_min = frequency_to_n(f_min)
|
||||
self.freq_index_max = frequency_to_n(f_max)
|
||||
self.freq_index = list(range(n_min, n_max+1))
|
||||
if bitmap is None:
|
||||
self.bitmap = [1] * (n_max-n_min+1)
|
||||
elif len(bitmap) == len(self.freq_index):
|
||||
self.bitmap = bitmap
|
||||
else:
|
||||
raise SpectrumError(f'bitmap is not consistant with f_min{f_min} - n: {n_min} and f_max{f_max}- n :{n_max}')
|
||||
|
||||
def getn(self, i):
|
||||
""" converts the n (itu grid) into a local index
|
||||
"""
|
||||
return self.freq_index[i]
|
||||
def geti(self, nvalue):
|
||||
""" converts the local index into n (itu grid)
|
||||
"""
|
||||
return self.freq_index.index(nvalue)
|
||||
def insert_left(self, newbitmap):
|
||||
""" insert bitmap on the left to align oms bitmaps if their start frequencies are different
|
||||
"""
|
||||
self.bitmap = newbitmap + self.bitmap
|
||||
temp = list(range(self.n_min-len(newbitmap), self.n_min))
|
||||
self.freq_index = temp + self.freq_index
|
||||
self.n_min = self.freq_index[0]
|
||||
def insert_right(self, newbitmap):
|
||||
""" insert bitmap on the right to align oms bitmaps if their stop frequencies are different
|
||||
"""
|
||||
self.bitmap = self.bitmap + newbitmap
|
||||
self.freq_index = self.freq_index + list(range(self.n_max, self.n_max+len(newbitmap)))
|
||||
self.n_max = self.freq_index[-1]
|
||||
|
||||
# +'grid available_slots f_min f_max services_list')
|
||||
OMSParams = namedtuple('OMSParams', 'oms_id el_id_list el_list')
|
||||
|
||||
class OMS:
|
||||
""" OMS class is the logical container that represent a link between two adjacent ROADMs and
|
||||
records the crossed elements and the occupied spectrum
|
||||
"""
|
||||
def __init__(self, *args, **params):
|
||||
params = OMSParams(**params)
|
||||
self.oms_id = params.oms_id
|
||||
self.el_id_list = params.el_id_list
|
||||
self.el_list = params.el_list
|
||||
self.spectrum_bitmap = []
|
||||
self.nb_channels = 0
|
||||
self.service_list = []
|
||||
# TODO
|
||||
def __str__(self):
|
||||
return '\n\t'.join([f'{type(self).__name__} {self.oms_id}',
|
||||
f'{self.el_id_list[0]} - {self.el_id_list[-1]}'])
|
||||
def __repr__(self):
|
||||
return '\n\t'.join([f'{type(self).__name__} {self.oms_id}',
|
||||
f'{self.el_id_list[0]} - {self.el_id_list[-1]}', '\n'])
|
||||
|
||||
def add_element(self, elem):
|
||||
""" records oms elements
|
||||
"""
|
||||
self.el_id_list.append(elem.uid)
|
||||
self.el_list.append(elem)
|
||||
|
||||
def update_spectrum(self, f_min, f_max, guardband=0.15e12, existing_spectrum=None,
|
||||
grid=0.00625e12):
|
||||
""" frequencies expressed in Hz
|
||||
"""
|
||||
if existing_spectrum is None:
|
||||
# add some 150 GHz margin to enable a center channel on f_min
|
||||
# use ITU-T G694.1
|
||||
# Flexible DWDM grid definition
|
||||
# For the flexible DWDM grid, the allowed frequency slots have a nominal
|
||||
# central frequency (in THz) defined by:
|
||||
# 193.1 + n × 0.00625 where n is a positive or negative integer including 0
|
||||
# and 0.00625 is the nominal central frequency granularity in THz
|
||||
# and a slot width defined by:
|
||||
# 12.5 × m where m is a positive integer and 12.5 is the slot width granularity in
|
||||
# GHz.
|
||||
# Any combination of frequency slots is allowed as long as no two frequency
|
||||
# slots overlap.
|
||||
|
||||
# TODO : add explaination on that / parametrize ....
|
||||
self.spectrum_bitmap = Bitmap(f_min, f_max, grid, guardband)
|
||||
# print(len(self.spectrum_bitmap.bitmap))
|
||||
|
||||
def assign_spectrum(self, nvalue, mvalue):
|
||||
""" change oms spectrum to mark spectrum assigned
|
||||
"""
|
||||
if (nvalue is None or mvalue is None or isinstance(nvalue, float)
|
||||
or isinstance(mvalue, float) or mvalue == 0):
|
||||
raise SpectrumError('could not assign None values')
|
||||
startn, stopn = mvalue_to_slots(nvalue, mvalue)
|
||||
# print(f'startn stop n {startn} , {stopn}')
|
||||
# assumes that guardbands are sufficient to ensure that assigning a center channel
|
||||
# at fmin or fmax is OK is startn > self.spectrum_bitmap.n_min
|
||||
if (nvalue <= self.spectrum_bitmap.freq_index_max and
|
||||
nvalue >= self.spectrum_bitmap.freq_index_min and
|
||||
stopn <= self.spectrum_bitmap.n_max and
|
||||
startn > self.spectrum_bitmap.n_min):
|
||||
# verification that both length are identical
|
||||
self.spectrum_bitmap.bitmap[self.spectrum_bitmap.geti(startn):self.spectrum_bitmap.geti(stopn)+1] = [0] * (stopn-startn+1)
|
||||
return True
|
||||
else:
|
||||
msg = f'Could not assign n {nvalue}, m {mvalue} values:' +\
|
||||
f' one or several slots are not available'
|
||||
LOGGER.info(msg)
|
||||
return False
|
||||
|
||||
def add_service(self, service_id, nb_wl):
|
||||
""" record service and mark spectrum as occupied
|
||||
"""
|
||||
self.service_list.append(service_id)
|
||||
self.nb_channels += nb_wl
|
||||
|
||||
def frequency_to_n(freq, grid=0.00625e12):
|
||||
""" converts frequency into the n value (ITU grid)
|
||||
"""
|
||||
return (int)((freq-193.1e12)/grid)
|
||||
|
||||
def nvalue_to_frequency(nvalue, grid=0.00625e12):
|
||||
""" converts n value into a frequency
|
||||
"""
|
||||
return 193.1e12 + nvalue * grid
|
||||
|
||||
def mvalue_to_slots(nvalue, mvalue):
|
||||
""" convert center n an m into start and stop n
|
||||
"""
|
||||
startn = nvalue - mvalue
|
||||
stopn = nvalue + mvalue -1
|
||||
return startn, stopn
|
||||
|
||||
def slots_to_m(startn, stopn):
|
||||
""" converts the start and stop n values to the center n and m value
|
||||
"""
|
||||
nvalue = (int)((startn+stopn+1)/2)
|
||||
mvalue = (int)((stopn-startn+1)/2)
|
||||
return nvalue, mvalue
|
||||
|
||||
def m_to_freq(nvalue, mvalue, grid=0.00625e12):
|
||||
""" converts m into frequency range
|
||||
"""
|
||||
startn, stopn = mvalue_to_slots(nvalue, mvalue)
|
||||
fstart = nvalue_to_frequency(startn, grid)
|
||||
fstop = nvalue_to_frequency(stopn+1, grid)
|
||||
return fstart, fstop
|
||||
|
||||
def align_grids(oms_list):
|
||||
""" used to apply same grid to all oms : same starting n, stop n and slot size
|
||||
out of grid slots are set to 0
|
||||
"""
|
||||
n_min = min([o.spectrum_bitmap.n_min for o in oms_list])
|
||||
n_max = max([o.spectrum_bitmap.n_max for o in oms_list])
|
||||
for this_o in oms_list:
|
||||
if (this_o.spectrum_bitmap.n_min - n_min) > 0:
|
||||
this_o.spectrum_bitmap.insert_left([0] * (this_o.spectrum_bitmap.n_min - n_min))
|
||||
if (n_max - this_o.spectrum_bitmap.n_max) > 0:
|
||||
this_o.spectrum_bitmap.insert_right([0] * (n_max - this_o.spectrum_bitmap.n_max))
|
||||
return oms_list
|
||||
|
||||
def build_oms_list(network, equipment):
|
||||
""" initialization of OMS list in the network
|
||||
an oms is build reading all intermediate nodes between two adjacent ROADMs
|
||||
each element within the list is being added an oms and oms_id to record the
|
||||
oms it belongs to.
|
||||
the function supports different spectrum width and supposes that the whole network
|
||||
works with the min range among OMSs
|
||||
"""
|
||||
oms_id = 0
|
||||
oms_list = []
|
||||
for node in [n for n in network.nodes() if isinstance(n, Roadm)]:
|
||||
for edge in network.edges([node]):
|
||||
if not isinstance(edge[1], Transceiver):
|
||||
nd_in = edge[0] # nd_in is a Roadm
|
||||
try:
|
||||
nd_in.oms_list.append(oms_id)
|
||||
except AttributeError:
|
||||
nd_in.oms_list = []
|
||||
nd_in.oms_list.append(oms_id)
|
||||
nd_out = edge[1]
|
||||
|
||||
params = {}
|
||||
params['oms_id'] = oms_id
|
||||
params['el_id_list'] = []
|
||||
params['el_list'] = []
|
||||
oms = OMS(**params)
|
||||
oms.add_element(nd_in)
|
||||
while not isinstance(nd_out, Roadm):
|
||||
oms.add_element(nd_out)
|
||||
# add an oms_id in the element
|
||||
nd_out.oms_id = oms_id
|
||||
nd_out.oms = oms
|
||||
n_temp = nd_out
|
||||
nd_out = next(n[1] for n in network.edges([n_temp]) if n[1].uid != nd_in.uid)
|
||||
nd_in = n_temp
|
||||
|
||||
oms.add_element(nd_out)
|
||||
# nd_out is a Roadm
|
||||
try:
|
||||
nd_out.oms_list.append(oms_id)
|
||||
except AttributeError:
|
||||
nd_out.oms_list = []
|
||||
nd_out.oms_list.append(oms_id)
|
||||
|
||||
oms.update_spectrum(equipment['SI']['default'].f_min,
|
||||
equipment['SI']['default'].f_max, grid=0.00625e12)
|
||||
# oms.assign_spectrum(13,7) gives back (193137500000000.0, 193225000000000.0)
|
||||
# as in the example in the standard
|
||||
# oms.assign_spectrum(13,7)
|
||||
|
||||
oms_list.append(oms)
|
||||
oms_id += 1
|
||||
oms_list = align_grids(oms_list)
|
||||
reversed_oms(oms_list)
|
||||
return oms_list
|
||||
|
||||
def reversed_oms(oms_list):
|
||||
""" identifies reversed OMS
|
||||
only applicable for non parallel OMS
|
||||
"""
|
||||
for oms in oms_list:
|
||||
has_reversed = False
|
||||
for this_o in oms_list:
|
||||
if (oms.el_id_list[0] == this_o.el_id_list[-1] and
|
||||
oms.el_id_list[-1] == this_o.el_id_list[0]):
|
||||
oms.reversed_oms = this_o
|
||||
has_reversed = True
|
||||
break
|
||||
if not has_reversed:
|
||||
oms.reversed_oms = None
|
||||
|
||||
|
||||
def bitmap_sum(band1, band2):
|
||||
""" a functions that marks occupied bitmap by 0 if the slot is occupied in band1 or in band2
|
||||
"""
|
||||
res = []
|
||||
for i, elem in enumerate(band1):
|
||||
if band2[i] * elem == 0:
|
||||
res.append(0)
|
||||
else:
|
||||
res.append(1)
|
||||
return res
|
||||
|
||||
def spectrum_selection(pth, oms_list, requested_m, requested_n=None):
|
||||
""" collects spectrum availability and call the select_candidate function
|
||||
# step 1 collects pth spectrum availability
|
||||
# step 2 if n is not None try to assign the spectrum
|
||||
# if the spectrum is not available then sends back an "error"
|
||||
# if n is None selects candidate spectrum
|
||||
# select spectrum that fits the policy ( first fit, random, ABP...)
|
||||
# step3 returns the selection
|
||||
"""
|
||||
|
||||
# use indexes instead of ITU-T n values
|
||||
path_oms = []
|
||||
for elem in pth:
|
||||
if not isinstance(elem, Roadm) and not isinstance(elem, Transceiver):
|
||||
# only edfa, fused and fibers have oms_id attribute
|
||||
path_oms.append(elem.oms_id)
|
||||
# remove duplicate oms_id, order is not important
|
||||
path_oms = list(set(path_oms))
|
||||
# assuming all oms have same freq index
|
||||
if not path_oms:
|
||||
candidate = (None, None, None)
|
||||
return candidate, path_oms
|
||||
freq_index = oms_list[path_oms[0]].spectrum_bitmap.freq_index
|
||||
freq_index_min = oms_list[path_oms[0]].spectrum_bitmap.freq_index_min
|
||||
freq_index_max = oms_list[path_oms[0]].spectrum_bitmap.freq_index_max
|
||||
|
||||
freq_availability = oms_list[path_oms[0]].spectrum_bitmap.bitmap
|
||||
for oms in path_oms[1:]:
|
||||
freq_availability = bitmap_sum(oms_list[oms].spectrum_bitmap.bitmap, freq_availability)
|
||||
if requested_n is None:
|
||||
# avoid slots reserved on the edge 0.15e-12 on both sides -> 24
|
||||
candidates = [(freq_index[i]+requested_m, freq_index[i], freq_index[i]+2*requested_m-1)
|
||||
for i in range(len(freq_availability))
|
||||
if freq_availability[i:i+2*requested_m] == [1] * (2*requested_m)
|
||||
and freq_index[i] >= freq_index_min
|
||||
and freq_index[i+2*requested_m-1] <= freq_index_max]
|
||||
|
||||
candidate = select_candidate(candidates, policy='first_fit')
|
||||
else:
|
||||
i = oms_list[path_oms[0]].spectrum_bitmap.geti(requested_n)
|
||||
# print(f'N {requested_n} i {i}')
|
||||
# print(freq_availability[i-m:i+m] )
|
||||
# print(freq_index[i-m:i+m])
|
||||
if (freq_availability[i-requested_m:i+requested_m] == [1] * (2*requested_m) and
|
||||
freq_index[i-requested_m] >= freq_index_min
|
||||
and freq_index[i+requested_m-1] <= freq_index_max):
|
||||
# candidate is the triplet center_n, startn and stopn
|
||||
candidate = (requested_n, requested_n-requested_m, requested_n+requested_m-1)
|
||||
else:
|
||||
candidate = (None, None, None)
|
||||
# print("coucou11")
|
||||
# print(candidate)
|
||||
# print(freq_availability[321:321+2*m])
|
||||
# a = [i+321 for i in range(2*m)]
|
||||
# print(a)
|
||||
# print(candidate)
|
||||
return candidate, path_oms
|
||||
|
||||
def select_candidate(candidates, policy):
|
||||
""" selects a candidate among all available spectrum
|
||||
"""
|
||||
if policy == 'first_fit':
|
||||
if candidates:
|
||||
return candidates[0]
|
||||
else:
|
||||
return (None, None, None)
|
||||
else:
|
||||
raise ServiceError('Only first_fit spectrum assignment policy is implemented.')
|
||||
|
||||
def pth_assign_spectrum(pths, rqs, oms_list, rpths):
|
||||
""" basic first fit assignment
|
||||
if reversed path are provided, means that occupation is bidir
|
||||
"""
|
||||
for i, pth in enumerate(pths):
|
||||
# computes the number of channels required
|
||||
try:
|
||||
if rqs[i].blocking_reason:
|
||||
rqs[i].blocked = True
|
||||
rqs[i].N = 0
|
||||
rqs[i].M = 0
|
||||
except AttributeError:
|
||||
nb_wl = ceil(rqs[i].path_bandwidth / rqs[i].bit_rate)
|
||||
# computes the total nb of slots according to requested spacing
|
||||
# TODO : express superchannels
|
||||
# assumes that all channels must be grouped
|
||||
# TODO : enables non contiguous reservation in case of blocking
|
||||
requested_m = ceil(rqs[i].spacing / 0.0125e12) * nb_wl
|
||||
# concatenate all path and reversed path elements to derive slots availability
|
||||
(center_n, startn, stopn), path_oms = spectrum_selection(pth + rpths[i], oms_list, requested_m,
|
||||
requested_n=None)
|
||||
# checks that requested_m is fitting startm and stopm
|
||||
# if not None, center_n and start, stop frequencies are applicable to all oms of pth
|
||||
# checks that spectrum is not None else indicate blocking reason
|
||||
if center_n is not None:
|
||||
# checks that requested_m is fitting startm and stopm
|
||||
if 2 * requested_m > (stopn - startn + 1):
|
||||
msg = f'candidate: {(center_n, startn, stopn)} is not consistant ' +\
|
||||
f'with {requested_m}'
|
||||
LOGGER.critical(msg)
|
||||
raise ValueError(msg)
|
||||
|
||||
for oms_elem in path_oms:
|
||||
oms_list[oms_elem].assign_spectrum(center_n, requested_m)
|
||||
oms_list[oms_elem].add_service(rqs[i].request_id, nb_wl)
|
||||
rqs[i].blocked = False
|
||||
rqs[i].N = center_n
|
||||
rqs[i].M = requested_m
|
||||
else:
|
||||
rqs[i].blocked = True
|
||||
rqs[i].N = 0
|
||||
rqs[i].M = 0
|
||||
rqs[i].blocking_reason = 'NO_SPECTRUM'
|
||||
@@ -11,21 +11,21 @@ This module contains utility functions that are used with gnpy.
|
||||
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
from csv import writer
|
||||
import numpy as np
|
||||
from numpy import pi, cos, sqrt, log10
|
||||
from scipy import constants
|
||||
|
||||
|
||||
def load_json(filename):
|
||||
with open(filename, 'r') as f:
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
return data
|
||||
|
||||
|
||||
def save_json(obj, filename):
|
||||
with open(filename, 'w') as f:
|
||||
json.dump(obj, f, indent=2)
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
json.dump(obj, f, indent=2, ensure_ascii=False)
|
||||
|
||||
def write_csv(obj, filename):
|
||||
"""
|
||||
@@ -55,7 +55,7 @@ def write_csv(obj, filename):
|
||||
result_category 2
|
||||
...
|
||||
"""
|
||||
with open(filename, 'w') as f:
|
||||
with open(filename, 'w', encoding='utf-8') as f:
|
||||
w = writer(f)
|
||||
for data_key, data_list in obj.items():
|
||||
#main header
|
||||
@@ -73,21 +73,19 @@ def c():
|
||||
return constants.c
|
||||
|
||||
|
||||
def itufs(spacing, startf=191.35, stopf=196.10):
|
||||
"""Creates an array of frequencies whose default range is
|
||||
191.35-196.10 THz
|
||||
def arrange_frequencies(length, start, stop):
|
||||
"""Create an array of frequencies
|
||||
|
||||
:param spacing: Frequency spacing in THz
|
||||
:param starf: Start frequency in THz
|
||||
:param stopf: Stop frequency in THz
|
||||
:type spacing: float
|
||||
:type startf: float
|
||||
:type stopf: float
|
||||
:return an array of frequnecies determined by the spacing parameter
|
||||
:param length: number of elements
|
||||
:param star: Start frequency in THz
|
||||
:param stop: Stop frequency in THz
|
||||
:type length: integer
|
||||
:type start: float
|
||||
:type stop: float
|
||||
:return an array of frequencies determined by the spacing parameter
|
||||
:rtype: numpy.ndarray
|
||||
"""
|
||||
return np.arange(startf, stopf + spacing / 2, spacing)
|
||||
|
||||
return np.linspace(start, stop, length)
|
||||
|
||||
def h():
|
||||
"""
|
||||
@@ -120,6 +118,10 @@ def freq2wavelength(value):
|
||||
"""
|
||||
return c() / value
|
||||
|
||||
def snr_sum(snr, bw, snr_added, bw_added=12.5e9):
|
||||
snr_added = snr_added - lin2db(bw/bw_added)
|
||||
snr = -lin2db(db2lin(-snr)+db2lin(-snr_added))
|
||||
return snr
|
||||
|
||||
def deltawl2deltaf(delta_wl, wavelength):
|
||||
""" deltawl2deltaf(delta_wl, wavelength):
|
||||
@@ -181,3 +183,43 @@ def rrc(ffs, baud_rate, alpha):
|
||||
p_inds = np.where(np.logical_and(np.abs(ffs) > 0, np.abs(ffs) < l_lim))
|
||||
hf[p_inds] = 1
|
||||
return sqrt(hf)
|
||||
|
||||
def merge_amplifier_restrictions(dict1, dict2):
|
||||
"""Updates contents of dicts recursively
|
||||
|
||||
>>> d1 = {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}}
|
||||
>>> d2 = {'params': {'target_pch_out_db': -20}}
|
||||
>>> merge_amplifier_restrictions(d1, d2)
|
||||
{'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}, 'target_pch_out_db': -20}}
|
||||
|
||||
>>> d3 = {'params': {'restrictions': {'preamp_variety_list': ['foo'], 'booster_variety_list': ['bar']}}}
|
||||
>>> merge_amplifier_restrictions(d1, d3)
|
||||
{'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}}
|
||||
"""
|
||||
|
||||
copy_dict1 = dict1.copy()
|
||||
for key in dict2:
|
||||
if key in dict1:
|
||||
if isinstance(dict1[key], dict):
|
||||
copy_dict1[key] = merge_amplifier_restrictions(copy_dict1[key], dict2[key])
|
||||
else:
|
||||
copy_dict1[key] = dict2[key]
|
||||
return copy_dict1
|
||||
|
||||
def silent_remove(this_list, elem):
|
||||
"""Remove matching elements from a list without raising ValueError
|
||||
|
||||
>>> li = [0, 1]
|
||||
>>> li = silent_remove(li, 1)
|
||||
>>> li
|
||||
[0]
|
||||
>>> li = silent_remove(li, 1)
|
||||
>>> li
|
||||
[0]
|
||||
"""
|
||||
|
||||
try:
|
||||
this_list.remove(elem)
|
||||
except ValueError:
|
||||
pass
|
||||
return this_list
|
||||
|
||||
@@ -23,7 +23,7 @@ type’][‘subtype’]=object**
|
||||
Every equipment type is defined in JSON root with according name and
|
||||
array of parameters as value.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: none
|
||||
|
||||
{"Edfa": [...],
|
||||
"Fiber": [...]
|
||||
@@ -40,26 +40,33 @@ object contains **"type_variety":”type name”** name:value combination,
|
||||
if only one subtype exists **"type_variety"** name is not mandatory and
|
||||
it will be marked with **”default”** value.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json
|
||||
|
||||
{"Edfa": [{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"p_max": 23,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10
|
||||
"nf_max": 10,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_low_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16,
|
||||
"gain_min": 8,
|
||||
"p_max": 21,
|
||||
"nf_min": 7,
|
||||
"nf_max": 11
|
||||
"p_max": 23,
|
||||
"nf_min": 6.5,
|
||||
"nf_max": 11,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
}
|
||||
],
|
||||
"Fiber": [{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127
|
||||
}
|
||||
@@ -74,66 +81,167 @@ it will be marked with **”default”** value.
|
||||
1.2.1. EDFA element
|
||||
*******************
|
||||
|
||||
Two types of EDFA definition are possible. Description JSON file
|
||||
Four types of EDFA definition are possible. Description JSON file
|
||||
location is in **transmission_main_example.py** folder:
|
||||
|
||||
- Advanced – with JSON file describing gain/noise figure tilt and
|
||||
gain/noise figure ripple. **"advanced_config_from_json"** value
|
||||
contains filename.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json-object
|
||||
|
||||
"Edfa":[{
|
||||
"type_variety": "high_detail_model_example",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json"
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
]
|
||||
|
||||
- Default – with JSON file describing gain figure tilt and gain/noise
|
||||
- Variable gain – with JSON file describing gain figure tilt and gain/noise
|
||||
figure ripple. **”default_edfa_config.json”** as source file.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json-object
|
||||
|
||||
"Edfa":[{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"p_max": 23,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10
|
||||
"nf_max": 10,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
}
|
||||
]
|
||||
|
||||
- Fixed gain – with JSON file describing gain figure tilt and gain/noise
|
||||
figure ripple. **”default_edfa_config.json”** as source file.
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"Edfa":[{
|
||||
"type_variety": "std_fixed_gain",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21,
|
||||
"gain_min": 20,
|
||||
"p_max": 21,
|
||||
"nf0": 5.5,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
]
|
||||
|
||||
- openroadm – with JSON file describing gain figure tilt and gain/noise
|
||||
figure ripple. **”default_edfa_config.json”** as source file.
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"Edfa":[{
|
||||
"type_variety": "low_noise",
|
||||
"type_def": "openroadm",
|
||||
"gain_flatmax": 27,
|
||||
"gain_min": 12,
|
||||
"p_max": 22,
|
||||
"nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62],
|
||||
"allowed_for_design": false
|
||||
}
|
||||
]
|
||||
|
||||
1.2.2. Fiber element
|
||||
********************
|
||||
|
||||
Fiber element with its parameters:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json-object
|
||||
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127
|
||||
}
|
||||
]
|
||||
|
||||
RamanFiber element
|
||||
******************
|
||||
|
||||
A special variant of the regular ``Fiber`` where the simulation engine accounts for the Raman effect.
|
||||
The newly added parameters are nested in the ``raman_efficiency`` dictionary.
|
||||
Its shape corresponds to typical properties of silica.
|
||||
More details are available from :cite:`curri_merit_2016`.
|
||||
|
||||
The ``cr`` property is the normailzed Raman efficiency, so it is is (almost) independent of the fiber type, while the coefficient actually giving Raman gain is g_R=C_R/Aeff.
|
||||
|
||||
The ``frequency_offset`` represents the spectral difference between the pumping photon and the one receiving energy.
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"RamanFiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127,
|
||||
"raman_efficiency": {
|
||||
"cr":[
|
||||
0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119,
|
||||
0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469,
|
||||
0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826,
|
||||
0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05,
|
||||
7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05,
|
||||
2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05,
|
||||
2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05,
|
||||
1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05,
|
||||
1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07,
|
||||
2E-07, 1E-07
|
||||
],
|
||||
"frequency_offset":[
|
||||
0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12,
|
||||
7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12,
|
||||
13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12,
|
||||
17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12,
|
||||
22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12,
|
||||
28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12,
|
||||
35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12,
|
||||
41.5e12, 42e12
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
1.2.3 Roadm element
|
||||
*******************
|
||||
|
||||
Roadm element with its parameters:
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"Roadms":[{
|
||||
"gain_mode_default_loss": 20,
|
||||
"power_mode_pout_target": -20,
|
||||
"add_drop_osnr": 38
|
||||
}
|
||||
]
|
||||
|
||||
1.2.3. Spans element
|
||||
********************
|
||||
|
||||
Spans element with its parameters:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json-object
|
||||
|
||||
"Spans":[{
|
||||
"power_mode":true,
|
||||
"delta_power_range_db": [0,0,0.5],
|
||||
"max_length": 150,
|
||||
"length_units": "km",
|
||||
"max_loss": 28,
|
||||
"padding": 10,
|
||||
"EOL": 1,
|
||||
"con_loss": 0.5
|
||||
"EOL": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
}
|
||||
]
|
||||
|
||||
@@ -143,15 +251,18 @@ Spans element with its parameters:
|
||||
|
||||
Spectral information with its parameters:
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json-object
|
||||
|
||||
"SI":[{
|
||||
"f_min": 191.3e12,
|
||||
"Nch": 80,
|
||||
"baud_rate": 32e9,
|
||||
"spacing": 75e9,
|
||||
"f_max":195.1e12,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0,0,0.5],
|
||||
"roll_off": 0.15,
|
||||
"power": 1.2589e-3
|
||||
"tx_osnr": 40,
|
||||
"sys_margins": 0
|
||||
}
|
||||
]
|
||||
|
||||
@@ -162,7 +273,9 @@ Spectral information with its parameters:
|
||||
Transceiver element with its parameters. **”mode”** can contain multiple
|
||||
Transceiver operation formats.
|
||||
|
||||
.. code-block::
|
||||
Note that ``OSNR`` parameter refers to the receiver's minimal OSNR threshold for a given mode.
|
||||
|
||||
.. code-block:: json-object
|
||||
|
||||
"Transceiver":[{
|
||||
"frequency":{
|
||||
@@ -171,16 +284,24 @@ Transceiver operation formats.
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "PS_SP64_1",
|
||||
"baudrate": 32e9,
|
||||
"OSNR": 9,
|
||||
"bit_rate": 100e9
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 37.5e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "PS_SP64_2",
|
||||
"baudrate": 66e9,
|
||||
"OSNR": 10,
|
||||
"bit_rate": 200e9
|
||||
"format": "mode 2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 15,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -215,7 +336,7 @@ Network description JSON file root consist of three unordered parts:
|
||||
|
||||
- connections – contains array of unidirectional connection objects
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: none
|
||||
|
||||
{"network_name": "Example Network",
|
||||
"elements": [{...},
|
||||
@@ -244,10 +365,10 @@ obligatory.
|
||||
|
||||
Transceiver element with its parameters.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json
|
||||
|
||||
{"uid": "trx Site_A",
|
||||
“metadata": {
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Site_A",
|
||||
"region": "",
|
||||
@@ -266,7 +387,7 @@ Transceiver element with its parameters.
|
||||
ROADM element with its parameters. **“params”** is optional, if not used
|
||||
default loss value of 20dB is used.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json
|
||||
|
||||
{"uid": "roadm Site_A",
|
||||
"metadata": {
|
||||
@@ -290,12 +411,12 @@ default loss value of 20dB is used.
|
||||
Fused element with its parameters. **“params”** is optional, if not used
|
||||
default loss value of 1dB is used.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json
|
||||
|
||||
{"uid": "ingress fused spans in Site_B",
|
||||
"metadata": {
|
||||
"location": {
|
||||
“city": "Site_B",
|
||||
"city": "Site_B",
|
||||
"region": "",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
@@ -313,7 +434,7 @@ default loss value of 1dB is used.
|
||||
|
||||
Fiber element with its parameters.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json
|
||||
|
||||
{"uid": "fiber (Site_A \\u2192 Site_B)",
|
||||
"metadata": {
|
||||
@@ -333,13 +454,56 @@ Fiber element with its parameters.
|
||||
}
|
||||
}
|
||||
|
||||
2.2.5. RamanFiber element
|
||||
*************************
|
||||
|
||||
2.2.5. EDFA element
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"uid": "Span1",
|
||||
"type": "RamanFiber",
|
||||
"type_variety": "SSMF",
|
||||
"operational": {
|
||||
"temperature": 283,
|
||||
"raman_pumps": [
|
||||
{
|
||||
"power": 200e-3,
|
||||
"frequency": 205e12,
|
||||
"propagation_direction": "counterprop"
|
||||
},
|
||||
{
|
||||
"power": 206e-3,
|
||||
"frequency": 201e12,
|
||||
"propagation_direction": "counterprop"
|
||||
}
|
||||
]
|
||||
},
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2.2.6. EDFA element
|
||||
********************
|
||||
|
||||
EDFA element with its parameters.
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json
|
||||
|
||||
{"uid": "Edfa1",
|
||||
"type": "Edfa",
|
||||
@@ -365,8 +529,31 @@ Each unidirectional connection object in connections array consist of
|
||||
two unordered **”from_node”** and **”to_node”** name pair with values
|
||||
corresponding to element **”uid”**
|
||||
|
||||
.. code-block::
|
||||
.. code-block:: json
|
||||
|
||||
{"from_node": "roadm Site_C",
|
||||
"to_node": "trx Site_C"
|
||||
}
|
||||
|
||||
************************
|
||||
3. Simulation Parameters
|
||||
************************
|
||||
|
||||
Additional details of the simulation are controlled via ``sim_params.json``:
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"raman_computed_channels": [1, 18, 37, 56, 75],
|
||||
"raman_parameters": {
|
||||
"flag_raman": true,
|
||||
"space_resolution": 10e3,
|
||||
"tolerance": 1e-8
|
||||
},
|
||||
"nli_parameters": {
|
||||
"nli_method_name": "ggn_spectrally_separated",
|
||||
"wdm_grid_size": 50e9,
|
||||
"dispersion_tolerance": 1,
|
||||
"phase_shift_tollerance": 0.1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,62 @@
|
||||
{
|
||||
"paths": [
|
||||
"response": [
|
||||
{
|
||||
"path": {
|
||||
"path-id": null,
|
||||
"path-properties": {
|
||||
"path-metric": [
|
||||
{
|
||||
"metric-type": null,
|
||||
"accumulative-value": null
|
||||
}
|
||||
],
|
||||
"path-srlgs": {
|
||||
"usage": "not used yet",
|
||||
"values": ["not used yet"]
|
||||
"response-id": null,
|
||||
"path-properties": {
|
||||
"path-metric": [
|
||||
{
|
||||
"metric-type": "SNR@bandwidth",
|
||||
"accumulative-value": null
|
||||
},
|
||||
"path-route-objects": [
|
||||
{
|
||||
"path-route-object": {
|
||||
"index": null,
|
||||
"unnumbered-hop": {
|
||||
"node-id": null,
|
||||
"link-tp-id": null,
|
||||
"hop-type": null,
|
||||
"direction": "not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "not used yet",
|
||||
"direction": "not used yet"
|
||||
}
|
||||
}
|
||||
{
|
||||
"metric-type": "SNR@0.1nm",
|
||||
"accumulative-value": null
|
||||
},
|
||||
{
|
||||
"metric-type": "OSNR@bandwidth",
|
||||
"accumulative-value": null
|
||||
},
|
||||
{
|
||||
"metric-type": "OSNR@0.1nm",
|
||||
"accumulative-value": null
|
||||
},
|
||||
{
|
||||
"metric-type": "reference_power",
|
||||
"accumulative-value": null
|
||||
},
|
||||
{
|
||||
"metric-type": "path_bandwidth",
|
||||
"accumulative-value": null
|
||||
}
|
||||
],
|
||||
"path-route-objects": [
|
||||
{
|
||||
"path-route-object": {
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": null,
|
||||
"link-tp-id": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"path-route-object": {
|
||||
"index": 1,
|
||||
"transponder": {
|
||||
"transponder-type": null,
|
||||
"transponder-mode": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"path-route-object": {
|
||||
"index": 2,
|
||||
"num-unnum-hop": {
|
||||
"node-id": null,
|
||||
"link-tp-id": null
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -1,2 +1,2 @@
|
||||
[pytest]
|
||||
addopts = -p no:warnings
|
||||
addopts = --doctest-modules
|
||||
|
||||
@@ -1,40 +1,14 @@
|
||||
alabaster==0.7.12
|
||||
atomicwrites==1.2.1
|
||||
attrs==18.2.0
|
||||
Babel==2.6.0
|
||||
certifi==2018.10.15
|
||||
chardet==3.0.4
|
||||
cycler==0.10.0
|
||||
decorator==4.3.0
|
||||
docutils==0.14
|
||||
idna==2.7
|
||||
imagesize==1.1.0
|
||||
Jinja2==2.10
|
||||
kiwisolver==1.0.1
|
||||
latexcodec==1.0.5
|
||||
MarkupSafe==1.0
|
||||
matplotlib==3.0.0
|
||||
more-itertools==4.3.0
|
||||
networkx==2.2
|
||||
numpy==1.15.2
|
||||
oset==0.1.3
|
||||
packaging==18.0
|
||||
pluggy==0.7.1
|
||||
py==1.7.0
|
||||
pybtex==0.21
|
||||
pybtex-docutils==0.2.1
|
||||
Pygments==2.2.0
|
||||
pyparsing==2.2.2
|
||||
pytest==3.8.2
|
||||
python-dateutil==2.7.3
|
||||
pytz==2018.5
|
||||
PyYAML==3.13
|
||||
requests==2.19.1
|
||||
scipy==1.1.0
|
||||
six==1.11.0
|
||||
snowballstemmer==1.2.1
|
||||
Sphinx==1.8.1
|
||||
sphinxcontrib-bibtex==0.4.0
|
||||
sphinxcontrib-websupport==1.1.0
|
||||
urllib3==1.23
|
||||
xlrd==1.1.0
|
||||
alabaster>=0.7.12,<1
|
||||
docutils==0.15.2
|
||||
flask==1.0.2
|
||||
flask-restful==0.3.7
|
||||
matplotlib>=3.1.0,<4
|
||||
networkx>=2.3,<3
|
||||
numpy>=1.16.1,<2
|
||||
pandas==0.24.2
|
||||
Pygments>=2.4.2,<3
|
||||
pytest>=4.0.0,<5
|
||||
scipy>=1.3.0,<2
|
||||
Sphinx>=2.1.1,<3
|
||||
sphinxcontrib-bibtex>=0.4.2,<1
|
||||
xlrd>=1.2.0,<2
|
||||
|
||||
@@ -6,54 +6,72 @@
|
||||
"destination": null,
|
||||
"src-tp-id": null,
|
||||
"dst-tp-id": null,
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": null,
|
||||
"index": null,
|
||||
"num-unnum-hop": {
|
||||
"node-id": null,
|
||||
"link-tp-id": null,
|
||||
"hop-type": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": null,
|
||||
"index": null,
|
||||
"label-hop": {
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": null,
|
||||
"index": null,
|
||||
"transponder": {
|
||||
"transponder-type": null,
|
||||
"transponder-mode": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": null,
|
||||
"index": null,
|
||||
"regenerator": {
|
||||
"regenerator-id": null,
|
||||
"transponder-type": null,
|
||||
"transponder-mode": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": null,
|
||||
"trx_mode": null,
|
||||
"trx_type": "name of the tsp type_variety as listed in the library",
|
||||
"trx_mode": "optional, name of the mode as listed in the tsp type_variety",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": null,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": {
|
||||
"route-object-include-object": [
|
||||
{
|
||||
"index": null,
|
||||
"unnumbered-hop": {
|
||||
"node-id": null,
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": null,
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
"spacing": mandatory decimal Hz,
|
||||
"max-nb-of-channel": optional integer,
|
||||
"output-power": optional decimal W,
|
||||
"path_bandwidth": optional bit/s
|
||||
}
|
||||
}
|
||||
}],
|
||||
"synchronization": [
|
||||
}
|
||||
],
|
||||
"synchronization": [ list of disjunctions, optional
|
||||
{
|
||||
"synchronization-id": null,
|
||||
"synchronization-id": "3",
|
||||
"svec": {
|
||||
"relaxable": "True",
|
||||
"link-diverse": "False",
|
||||
"node-diverse": "False",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
null ]
|
||||
null, null ]
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
4
setup.py
4
setup.py
@@ -11,13 +11,13 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
|
||||
|
||||
setup(
|
||||
name='gnpy',
|
||||
version='0.1.3',
|
||||
version='2.1',
|
||||
description='route planning and optimization tool for mesh optical networks',
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/x-rst; charset=UTF-8',
|
||||
url='https://github.com/Telecominfraproject/gnpy',
|
||||
author='Telecom Infra Project',
|
||||
author_email='james.powell@telecominfraproject.com',
|
||||
author_email='jan.kundrat@telecominfraproject.com',
|
||||
classifiers=[
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Intended Audience :: Developers',
|
||||
|
||||
@@ -205,6 +205,36 @@
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Att_B",
|
||||
"type": "Fused",
|
||||
"params":{
|
||||
"loss":16
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0,
|
||||
"city": "Corlay",
|
||||
"region": "RLD"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Att_F",
|
||||
"type": "Fused",
|
||||
"params":{
|
||||
"loss":16
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0,
|
||||
"city": "Corlay",
|
||||
"region": "RLD"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
@@ -247,6 +277,10 @@
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa5",
|
||||
"to_node": "Att_F"
|
||||
},
|
||||
{
|
||||
"from_node": "Att_F",
|
||||
"to_node": "trx F"
|
||||
},
|
||||
{
|
||||
@@ -255,6 +289,10 @@
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa1",
|
||||
"to_node": "Att_B"
|
||||
},
|
||||
{
|
||||
"from_node": "Att_B",
|
||||
"to_node": "trx B"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -77,8 +77,11 @@ def compare_networks(expected, actual):
|
||||
def compare_services(expected, actual):
|
||||
requests = compare(expected['path-request'], actual['path-request'],
|
||||
key=lambda el: el['request-id'])
|
||||
synchronizations = compare(expected['synchronisation'], actual['synchronisation'],
|
||||
key=lambda el: el['synchonization-id'])
|
||||
synchronizations = compare(expected['path-request'], expected['path-request'],
|
||||
key=lambda el: el['request-id'])
|
||||
if 'synchronization' in expected.keys():
|
||||
synchronizations = compare(expected['synchronization'], actual['synchronization'],
|
||||
key=lambda el: el['synchronization-id'])
|
||||
return ServicesResults(requests, synchronizations)
|
||||
|
||||
def compare_paths(expected_output, actual_output):
|
||||
@@ -105,16 +108,16 @@ def encode_sets(obj):
|
||||
if __name__ == '__main__':
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.expected_output) as f:
|
||||
with open(args.expected_output, encoding='utf-8') as f:
|
||||
expected = load(f)
|
||||
|
||||
with open(args.actual_output) as f:
|
||||
with open(args.actual_output, encoding='utf-8') as f:
|
||||
actual = load(f)
|
||||
|
||||
result = COMPARISONS[args.comparison](expected, actual)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, 'w') as f:
|
||||
dump(result, f, default=encode_sets, indent=2)
|
||||
with open(args.output, 'w', encoding='utf-8') as f:
|
||||
dump(result, f, default=encode_sets, indent=2, ensure_ascii=False)
|
||||
else:
|
||||
print(str(result))
|
||||
|
||||
Binary file not shown.
196470
tests/data/CORONET_Global_Topology_auto_design_expected.json
Normal file
196470
tests/data/CORONET_Global_Topology_auto_design_expected.json
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,11 @@
|
||||
{ "Edfa":[{
|
||||
"type_variety": "CienaDB_medium_gain",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25,
|
||||
"gain_min": 15,
|
||||
"p_max": 21,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
@@ -14,6 +16,7 @@
|
||||
"p_max": 21,
|
||||
"nf_min": 6,
|
||||
"nf_max": 10,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
@@ -24,6 +27,7 @@
|
||||
"p_max": 21,
|
||||
"nf_min": 7,
|
||||
"nf_max": 11,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
@@ -34,6 +38,7 @@
|
||||
"p_max": 21,
|
||||
"nf_min": 5.8,
|
||||
"nf_max": 10,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
@@ -44,7 +49,16 @@
|
||||
"p_max": 21,
|
||||
"nf0": 5,
|
||||
"allowed_for_design": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type_variety": "std_booster",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21,
|
||||
"gain_min": 20,
|
||||
"p_max": 21,
|
||||
"nf0": 5,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
],
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
@@ -52,9 +66,11 @@
|
||||
"gamma": 0.00127
|
||||
}
|
||||
],
|
||||
"Spans":[{
|
||||
"power_mode": true,
|
||||
"delta_power_range_db": [0,0,1],
|
||||
"Span":[{
|
||||
"power_mode":true,
|
||||
"delta_power_range_db": [0,0,0.5],
|
||||
"max_fiber_lineic_loss_for_raman": 0.25,
|
||||
"target_extended_gain": 2.5,
|
||||
"max_length": 150,
|
||||
"length_units": "km",
|
||||
"max_loss": 28,
|
||||
@@ -64,9 +80,13 @@
|
||||
"con_out": 0
|
||||
}
|
||||
],
|
||||
"Roadms":[{
|
||||
"gain_mode_default_loss": 20,
|
||||
"power_mode_pref": -20
|
||||
"Roadm":[{
|
||||
"target_pch_out_db": -20,
|
||||
"add_drop_osnr": 38,
|
||||
"restrictions": {
|
||||
"preamp_variety_list":[],
|
||||
"booster_variety_list":[]
|
||||
}
|
||||
}],
|
||||
"SI":[{
|
||||
"f_min": 191.3e12,
|
||||
@@ -74,10 +94,10 @@
|
||||
"baud_rate": 32e9,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0,0.5,0.5],
|
||||
"power_range_db": [0,0,0.5],
|
||||
"roll_off": 0.15,
|
||||
"OSNR": 15,
|
||||
"bit_rate":100e9
|
||||
"tx_osnr": 100,
|
||||
"sys_margins": 0
|
||||
}],
|
||||
"Transceiver":[
|
||||
{
|
||||
@@ -92,15 +112,41 @@
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 100,
|
||||
"min_spacing": 50e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "PS_SP64_2",
|
||||
"baud_rate": 64e9,
|
||||
"OSNR": 15,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
}
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 100,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 100,
|
||||
"min_spacing": 50e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "mode 2",
|
||||
"baud_rate": 64e9,
|
||||
"OSNR": 15,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 100,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -115,7 +161,69 @@
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 19,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 100,
|
||||
"min_spacing": 50e9,
|
||||
"cost":1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type_variety": "Voyager",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 12,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 45,
|
||||
"min_spacing": 50e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "mode 3",
|
||||
"baud_rate": 44e9,
|
||||
"OSNR": 18,
|
||||
"bit_rate": 300e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 45,
|
||||
"min_spacing": 62.5e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "mode 2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 21,
|
||||
"bit_rate": 400e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 45,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "mode 2 - fake",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 21,
|
||||
"bit_rate": 400e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 45,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
},
|
||||
{
|
||||
"format": "mode 4",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 16,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 45,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,694 +0,0 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Corlay)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Loudeac)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F01",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Brest_KLA)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Lannion_CAS)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Corlay)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F01",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Brest_KLA \u2192 Morlaix)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lannion_CAS to Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lannion_CAS to Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lannion_CAS to Stbrieuc",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Corlay to Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 0,
|
||||
"longitude": 0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "egress edfa in Lannion_CAS to Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lannion_CAS to Corlay",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "egress edfa in Lannion_CAS to Stbrieuc"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lannion_CAS to Stbrieuc",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "egress edfa in Lannion_CAS to Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lannion_CAS to Morlaix",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-",
|
||||
"to_node": "ingress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Loudeac)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Corlay)-",
|
||||
"to_node": "egress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Loudeac)-",
|
||||
"to_node": "ingress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-",
|
||||
"to_node": "egress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Corlay)-"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F01"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F01",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F01"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F01",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-",
|
||||
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-",
|
||||
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lannion_CAS",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "trx Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lorient_KMA",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "trx Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Vannes_KBE",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "trx Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Rennes_STA",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "trx Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Brest_KLA",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "trx Brest_KLA"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "BREST_KLA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx BREST_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronisation": [
|
||||
{
|
||||
"synchonization-id": "0",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"0",
|
||||
"1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "1",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
97
tests/data/expected_results_science_utils.csv
Normal file
97
tests/data/expected_results_science_utils.csv
Normal file
@@ -0,0 +1,97 @@
|
||||
,signal,ase,nli
|
||||
0,0.0002869472910750076,3.829243751386179e-08,2.157043502374111e-07
|
||||
1,0.000284426444181902,3.8108068606265256e-08,2.1799950841472648e-07
|
||||
2,0.0002819286625240274,3.7925434667811625e-08,2.2023841125044652e-07
|
||||
3,0.0002794537215642205,3.774451238936698e-08,2.224218994135113e-07
|
||||
4,0.0002756243295734432,3.739256063612741e-08,2.2343448272114653e-07
|
||||
5,0.0002718482755003954,3.7044477620123535e-08,2.2437826192962217e-07
|
||||
6,0.0002681247979313455,3.6700201831013766e-08,2.2525495466695055e-07
|
||||
7,0.0002644507001383656,3.635953568122817e-08,2.2606415187870565e-07
|
||||
8,0.0002608253488031495,3.602242321653821e-08,2.268074852150968e-07
|
||||
9,0.00025690468888571607,3.564391587795796e-08,2.2718285844824803e-07
|
||||
10,0.0002530414048173237,3.5269661038482016e-08,2.2749429758476786e-07
|
||||
11,0.0002492279873568786,3.4899736994459975e-08,2.277374766526846e-07
|
||||
12,0.0002454639458992114,3.4534068616323406e-08,2.2791414400784552e-07
|
||||
13,0.00024174879168999762,3.417258192135115e-08,2.280260208417629e-07
|
||||
14,0.00023798746912556782,3.3802278288721e-08,2.2798420759779948e-07
|
||||
15,0.00023427697848575827,3.3436265380528345e-08,2.2788101592690985e-07
|
||||
16,0.00023061678363205047,3.30744682841412e-08,2.2771816297652923e-07
|
||||
17,0.00022700656967542085,3.271682680678683e-08,2.2749755602884014e-07
|
||||
18,0.0002234457948096593,3.236326805537296e-08,2.236182244259085e-07
|
||||
19,0.0002195336193536736,3.195819496314336e-08,2.193976173454328e-07
|
||||
20,0.00021568313139087874,3.155821230359698e-08,2.1524945887103656e-07
|
||||
21,0.00021189361260563733,3.116322489050993e-08,2.1117277567390236e-07
|
||||
22,0.00020816423698459606,3.0773141693336075e-08,2.0716649124094935e-07
|
||||
23,0.0002044941867087381,3.038787321635763e-08,2.032295417993187e-07
|
||||
24,0.00020116081520673765,3.00440338127331e-08,1.9963693210324778e-07
|
||||
25,0.00019787569461895006,2.9704199888387147e-08,1.9610141536963145e-07
|
||||
26,0.00019463824873065924,2.9368302916351224e-08,1.9262221997372471e-07
|
||||
27,0.0001914486066928752,2.903632427420397e-08,1.8919927457565086e-07
|
||||
28,0.00018830616497930887,2.870819640079397e-08,1.858317840670677e-07
|
||||
29,0.00018521032563368435,2.838385281897912e-08,1.8251896218718178e-07
|
||||
30,0.00018216049720979434,2.8063228018898468e-08,1.7926003240909075e-07
|
||||
31,0.0001791561867005718,2.7746255438682553e-08,1.76054318231933e-07
|
||||
32,0.00017619680881744213,2.7432871709278503e-08,1.7290105534292413e-07
|
||||
33,0.00017328178390236163,2.7123014438128492e-08,1.6979948820364567e-07
|
||||
34,0.00017049664136784971,2.6828118382010868e-08,1.668331233176527e-07
|
||||
35,0.0001677518922618999,2.6536524600591003e-08,1.639139770351797e-07
|
||||
36,0.00016504703499520338,2.6248178236430935e-08,1.6104139135571758e-07
|
||||
37,0.0001623826677977635,2.596311344676757e-08,1.579538179464147e-07
|
||||
38,0.0001597582427278653,2.5681275450827438e-08,1.549209871570718e-07
|
||||
39,0.0001571732182028194,2.5402610321183817e-08,1.5194201541886346e-07
|
||||
40,0.00015462705891566638,2.512706495768609e-08,1.490160317195833e-07
|
||||
41,0.00015212101646392648,2.4854546722771583e-08,1.4614388817377845e-07
|
||||
42,0.00014965447757986727,2.4585006051161647e-08,1.4332463586636234e-07
|
||||
43,0.00014722683809507942,2.4318394065447274e-08,1.4055734193947907e-07
|
||||
44,0.0001447164668892396,2.4034548127308286e-08,1.3772590008270512e-07
|
||||
45,0.00014224784112375704,2.3753926686114635e-08,1.3494914625939818e-07
|
||||
46,0.00013982028367499942,2.3476475779461364e-08,1.3222606385780792e-07
|
||||
47,0.00013743418748445304,2.3202244204140228e-08,1.2955665313419502e-07
|
||||
48,0.00013508884015386575,2.2931178307200807e-08,1.269398709602497e-07
|
||||
49,0.00013278354172499636,2.2663225269637508e-08,1.243746944213211e-07
|
||||
50,0.0001305176041972383,2.2398333101097452e-08,1.2186012017916144e-07
|
||||
51,0.00012829168984639723,2.2136419884279648e-08,1.1939640981690787e-07
|
||||
52,0.00012610506317956035,2.1877436733290284e-08,1.169825203056231e-07
|
||||
53,0.000123957002859191,2.1621335420785434e-08,1.1461743054419468e-07
|
||||
54,0.00012180241033649304,2.1360152817604167e-08,1.1225922783038433e-07
|
||||
55,0.00011968650905779935,2.1101906890578305e-08,1.0994951537259513e-07
|
||||
56,0.000117608577762061,2.0846548870078847e-08,1.0757395097864581e-07
|
||||
57,0.00011556891128259058,2.0594151467353748e-08,1.0524972555992308e-07
|
||||
58,0.00011356676177301841,2.0344667169015006e-08,1.0297570549831857e-07
|
||||
59,0.00011160139690545192,2.00980493433389e-08,1.0075078305548045e-07
|
||||
60,0.00010967209909252646,1.985425227516509e-08,9.857387536569511e-08
|
||||
61,0.00010777915187087522,1.9613208260272527e-08,9.644480679616336e-08
|
||||
62,0.00010592181397175155,1.937487453011716e-08,9.436248424611683e-08
|
||||
63,0.00010409936038610526,1.913920913597429e-08,9.23258408012148e-08
|
||||
64,0.00010246447558375888,1.8936226281729442e-08,9.046927135291653e-08
|
||||
65,0.00010085803630104006,1.87354387522902e-08,8.865067925960373e-08
|
||||
66,9.927950010553608e-05,1.853681852284204e-08,8.686925127146881e-08
|
||||
67,9.772837346090978e-05,1.834034443508121e-08,8.512422533827548e-08
|
||||
68,9.620413430112097e-05,1.8145990199784238e-08,8.341482250639003e-08
|
||||
69,9.470627135913274e-05,1.795373041706864e-08,8.174028142913882e-08
|
||||
70,9.323428359797426e-05,1.776354066998682e-08,8.009985766376296e-08
|
||||
71,9.178813743816942e-05,1.7575386852678668e-08,7.849321446941785e-08
|
||||
72,9.03673300948529e-05,1.7389247191220127e-08,7.691961625609547e-08
|
||||
73,8.897136946427622e-05,1.7205101122769978e-08,7.537834446342857e-08
|
||||
74,8.760740745800998e-05,1.7025337039390582e-08,7.387513417420477e-08
|
||||
75,8.626710469266086e-05,1.684760610568072e-08,7.274492099363918e-08
|
||||
76,8.495000573672162e-05,1.6671894857242002e-08,7.163427447510873e-08
|
||||
77,8.365569697520994e-05,1.649819993412593e-08,7.054284583689279e-08
|
||||
78,8.238374036674246e-05,1.6326513144182658e-08,6.947026569965565e-08
|
||||
79,8.113370706498376e-05,1.6156829499842502e-08,6.841617243780552e-08
|
||||
80,7.990517700269747e-05,1.5989147949913657e-08,6.738021182874466e-08
|
||||
81,7.86978423091888e-05,1.5823469853370494e-08,6.636212425984957e-08
|
||||
82,7.751129541079691e-05,1.5659805288834794e-08,6.536156604375694e-08
|
||||
83,7.634513730458643e-05,1.549817228640182e-08,6.4378200720386e-08
|
||||
84,7.530262080974352e-05,1.5364274253504764e-08,6.349909645089537e-08
|
||||
85,7.427675504203847e-05,1.523236211656126e-08,6.263403294276386e-08
|
||||
86,7.326723873728748e-05,1.5102509684796054e-08,6.17827561543225e-08
|
||||
87,7.227232864621635e-05,1.497407531211962e-08,6.094379608688325e-08
|
||||
88,7.129179755315639e-05,1.4847053209180731e-08,6.011696114034632e-08
|
||||
89,7.032542203609286e-05,1.4721438007057792e-08,5.930206291361871e-08
|
||||
90,6.937298231674387e-05,1.4597224779058979e-08,5.8498916078193026e-08
|
||||
91,6.843339696762452e-05,1.4474430063551042e-08,5.7706608718023995e-08
|
||||
92,6.750649045006184e-05,1.435304906112738e-08,5.692499280974924e-08
|
||||
93,6.659208967850971e-05,1.4233077472549144e-08,5.615392239861094e-08
|
||||
94,6.554258932109723e-05,1.4075047005202515e-08,5.5268928972034715e-08
|
||||
95,6.450957734109015e-05,1.3918652473373596e-08,5.439783940505763e-08
|
||||
|
Binary file not shown.
@@ -1,804 +0,0 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx toto",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "toto",
|
||||
"region": "",
|
||||
"latitude": 6.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx tata",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "tata",
|
||||
"region": "",
|
||||
"latitude": 7.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm toto",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "toto",
|
||||
"region": "",
|
||||
"latitude": 6.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm tata",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "tata",
|
||||
"region": "",
|
||||
"latitude": 7.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (toto \u2192 tata)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (tata \u2192 toto)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lannion_CAS to Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "egress edfa in Lorient_KMA to Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "test",
|
||||
"operational": {
|
||||
"gain_target": 0,
|
||||
"tilt_target": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "egress edfa in Lannion_CAS to Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lannion_CAS to Corlay",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"to_node": "ingress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Loudeac)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"to_node": "egress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"to_node": "ingress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"to_node": "egress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Corlay)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "egress edfa in Lorient_KMA to Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "egress edfa in Lorient_KMA to Loudeac",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"to_node": "ingress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"to_node": "egress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm toto",
|
||||
"to_node": "fiber (toto \u2192 tata)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (tata \u2192 toto)-",
|
||||
"to_node": "roadm toto"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm tata",
|
||||
"to_node": "fiber (tata \u2192 toto)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (toto \u2192 tata)-",
|
||||
"to_node": "roadm tata"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lannion_CAS",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "trx Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lorient_KMA",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "trx Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Vannes_KBE",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "trx Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Rennes_STA",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "trx Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Brest_KLA",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "trx Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx toto",
|
||||
"to_node": "roadm toto"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm toto",
|
||||
"to_node": "trx toto"
|
||||
},
|
||||
{
|
||||
"from_node": "trx tata",
|
||||
"to_node": "roadm tata"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm tata",
|
||||
"to_node": "trx tata"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "Brest_KLA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Brest_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
{
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lannion_CAS",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lorient_KMA",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "Lannion_CAS",
|
||||
"destination": "Rennes_STA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Rennes_STA",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "4",
|
||||
"source": "Rennes_STA",
|
||||
"destination": "Lannion_CAS",
|
||||
"src-tp-id": "trx Rennes_STA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 64,
|
||||
"output-power": 0.0019952623149688794
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "5",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Lannion_CAS",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
{
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "toto",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronisation": [
|
||||
{
|
||||
"synchonization-id": "0",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"0",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "3",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"3",
|
||||
"4"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "5",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"5",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,762 +0,0 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx toto",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "toto",
|
||||
"region": "",
|
||||
"latitude": 6.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "trx tata",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "tata",
|
||||
"region": "",
|
||||
"latitude": 7.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Transceiver"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lannion_CAS",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lannion_CAS",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Lorient_KMA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Vannes_KBE",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 4.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Rennes_STA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Rennes_STA",
|
||||
"region": "RLD",
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm Brest_KLA",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Brest_KLA",
|
||||
"region": "RLD",
|
||||
"latitude": 4.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm toto",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "toto",
|
||||
"region": "",
|
||||
"latitude": 6.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "roadm tata",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "tata",
|
||||
"region": "",
|
||||
"latitude": 7.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Roadm"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "ingress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Corlay",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Corlay",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Loudeac",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Loudeac",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "egress fused spans in Morlaix",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Morlaix",
|
||||
"region": "RLD",
|
||||
"latitude": 3.0,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fused"
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (toto \u2192 tata)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 0.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 20.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 2.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.5
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 65.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 40.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 3.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 35.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (tata \u2192 toto)-",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.5,
|
||||
"longitude": 0.0
|
||||
}
|
||||
},
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
"con_out": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061",
|
||||
"to_node": "ingress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Loudeac)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Corlay)-F010",
|
||||
"to_node": "egress fused spans in Corlay"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Corlay",
|
||||
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Corlay \u2192 Loudeac)-F010",
|
||||
"to_node": "ingress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
|
||||
"to_node": "egress fused spans in Loudeac"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Loudeac",
|
||||
"to_node": "fiber (Loudeac \u2192 Corlay)-F010"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
|
||||
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
|
||||
"to_node": "ingress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "ingress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060",
|
||||
"to_node": "egress fused spans in Morlaix"
|
||||
},
|
||||
{
|
||||
"from_node": "egress fused spans in Morlaix",
|
||||
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm toto",
|
||||
"to_node": "fiber (toto \u2192 tata)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (tata \u2192 toto)-",
|
||||
"to_node": "roadm toto"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm tata",
|
||||
"to_node": "fiber (tata \u2192 toto)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (toto \u2192 tata)-",
|
||||
"to_node": "roadm tata"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lannion_CAS",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lannion_CAS",
|
||||
"to_node": "trx Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Lorient_KMA",
|
||||
"to_node": "roadm Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Lorient_KMA",
|
||||
"to_node": "trx Lorient_KMA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Vannes_KBE",
|
||||
"to_node": "roadm Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Vannes_KBE",
|
||||
"to_node": "trx Vannes_KBE"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Rennes_STA",
|
||||
"to_node": "roadm Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Rennes_STA",
|
||||
"to_node": "trx Rennes_STA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Brest_KLA",
|
||||
"to_node": "roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "trx Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "trx toto",
|
||||
"to_node": "roadm toto"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm toto",
|
||||
"to_node": "trx toto"
|
||||
},
|
||||
{
|
||||
"from_node": "trx tata",
|
||||
"to_node": "roadm tata"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm tata",
|
||||
"to_node": "trx tata"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "Brest_KLA",
|
||||
"destination": "Vannes_KBE",
|
||||
"src-tp-id": "trx Brest_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
{
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lannion_CAS",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"index": 1,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "Lorient_KMA",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "Lannion_CAS",
|
||||
"destination": "Rennes_STA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Rennes_STA",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "4",
|
||||
"source": "Rennes_STA",
|
||||
"destination": "Lannion_CAS",
|
||||
"src-tp-id": "trx Rennes_STA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 64,
|
||||
"output-power": 0.0019952623149688794
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "5",
|
||||
"source": "Lorient_KMA",
|
||||
"destination": "Lannion_CAS",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673
|
||||
}
|
||||
},
|
||||
"optimizations": {
|
||||
"explicit-route-include-objects": [
|
||||
{
|
||||
"index": 0,
|
||||
"unnumbered-hop": {
|
||||
"node-id": "toto",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "loose",
|
||||
"direction": "direction is not used"
|
||||
},
|
||||
"label-hop": {
|
||||
"te-label": {
|
||||
"generic": "generic is not used",
|
||||
"direction": "direction is not used"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronisation": [
|
||||
{
|
||||
"synchonization-id": "0",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"0",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "3",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"3",
|
||||
"4"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchonization-id": "5",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"link-diverse": "True",
|
||||
"node-diverse": "True",
|
||||
"request-id-number": [
|
||||
"5",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
223
tests/data/raman_fiber_config.json
Normal file
223
tests/data/raman_fiber_config.json
Normal file
@@ -0,0 +1,223 @@
|
||||
{
|
||||
"uid": "Span1",
|
||||
"params": {
|
||||
"length": 80,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0.5,
|
||||
"con_out": 0.5,
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 0.0000167,
|
||||
"gamma": 0.00127,
|
||||
"raman_efficiency": {
|
||||
"cr": [
|
||||
0,
|
||||
0.0000094,
|
||||
0.0000292,
|
||||
0.0000488,
|
||||
0.0000682,
|
||||
0.0000831,
|
||||
0.000094,
|
||||
0.0001014,
|
||||
0.0001069,
|
||||
0.0001119,
|
||||
0.0001217,
|
||||
0.0001268,
|
||||
0.0001365,
|
||||
0.000149,
|
||||
0.000165,
|
||||
0.000181,
|
||||
0.0001977,
|
||||
0.0002192,
|
||||
0.0002469,
|
||||
0.0002749,
|
||||
0.0002999,
|
||||
0.0003206,
|
||||
0.0003405,
|
||||
0.0003592,
|
||||
0.000374,
|
||||
0.0003826,
|
||||
0.0003841,
|
||||
0.0003826,
|
||||
0.0003802,
|
||||
0.0003756,
|
||||
0.0003549,
|
||||
0.0003795,
|
||||
0.000344,
|
||||
0.0002933,
|
||||
0.0002024,
|
||||
0.0001158,
|
||||
0.0000846,
|
||||
0.0000714,
|
||||
0.0000686,
|
||||
0.000085,
|
||||
0.0000893,
|
||||
0.0000901,
|
||||
0.0000815,
|
||||
0.0000667,
|
||||
0.0000437,
|
||||
0.0000328,
|
||||
0.0000296,
|
||||
0.0000265,
|
||||
0.0000257,
|
||||
0.0000281,
|
||||
0.0000308,
|
||||
0.0000367,
|
||||
0.0000585,
|
||||
0.0000663,
|
||||
0.0000636,
|
||||
0.000055,
|
||||
0.0000406,
|
||||
0.0000277,
|
||||
0.0000242,
|
||||
0.0000187,
|
||||
0.000016,
|
||||
0.000014,
|
||||
0.0000113,
|
||||
0.0000105,
|
||||
0.0000098,
|
||||
0.0000098,
|
||||
0.0000113,
|
||||
0.0000164,
|
||||
0.0000195,
|
||||
0.0000238,
|
||||
0.0000226,
|
||||
0.0000203,
|
||||
0.0000148,
|
||||
0.0000109,
|
||||
0.0000098,
|
||||
0.0000105,
|
||||
0.0000117,
|
||||
0.0000125,
|
||||
0.0000121,
|
||||
0.0000109,
|
||||
0.0000098,
|
||||
0.0000082,
|
||||
0.0000066,
|
||||
0.0000047,
|
||||
0.0000027,
|
||||
0.0000019,
|
||||
0.0000012,
|
||||
4e-7,
|
||||
2e-7,
|
||||
1e-7
|
||||
],
|
||||
"frequency_offset": [
|
||||
0,
|
||||
500000000000,
|
||||
1000000000000,
|
||||
1500000000000,
|
||||
2000000000000,
|
||||
2500000000000,
|
||||
3000000000000,
|
||||
3500000000000,
|
||||
4000000000000,
|
||||
4500000000000,
|
||||
5000000000000,
|
||||
5500000000000,
|
||||
6000000000000,
|
||||
6500000000000,
|
||||
7000000000000,
|
||||
7500000000000,
|
||||
8000000000000,
|
||||
8500000000000,
|
||||
9000000000000,
|
||||
9500000000000,
|
||||
10000000000000,
|
||||
10500000000000,
|
||||
11000000000000,
|
||||
11500000000000,
|
||||
12000000000000,
|
||||
12500000000000,
|
||||
12750000000000,
|
||||
13000000000000,
|
||||
13250000000000,
|
||||
13500000000000,
|
||||
14000000000000,
|
||||
14500000000000,
|
||||
14750000000000,
|
||||
15000000000000,
|
||||
15500000000000,
|
||||
16000000000000,
|
||||
16500000000000,
|
||||
17000000000000,
|
||||
17500000000000,
|
||||
18000000000000,
|
||||
18250000000000,
|
||||
18500000000000,
|
||||
18750000000000,
|
||||
19000000000000,
|
||||
19500000000000,
|
||||
20000000000000,
|
||||
20500000000000,
|
||||
21000000000000,
|
||||
21500000000000,
|
||||
22000000000000,
|
||||
22500000000000,
|
||||
23000000000000,
|
||||
23500000000000,
|
||||
24000000000000,
|
||||
24500000000000,
|
||||
25000000000000,
|
||||
25500000000000,
|
||||
26000000000000,
|
||||
26500000000000,
|
||||
27000000000000,
|
||||
27500000000000,
|
||||
28000000000000,
|
||||
28500000000000,
|
||||
29000000000000,
|
||||
29500000000000,
|
||||
30000000000000,
|
||||
30500000000000,
|
||||
31000000000000,
|
||||
31500000000000,
|
||||
32000000000000,
|
||||
32500000000000,
|
||||
33000000000000,
|
||||
33500000000000,
|
||||
34000000000000,
|
||||
34500000000000,
|
||||
35000000000000,
|
||||
35500000000000,
|
||||
36000000000000,
|
||||
36500000000000,
|
||||
37000000000000,
|
||||
37500000000000,
|
||||
38000000000000,
|
||||
38500000000000,
|
||||
39000000000000,
|
||||
39500000000000,
|
||||
40000000000000,
|
||||
40500000000000,
|
||||
41000000000000,
|
||||
41500000000000,
|
||||
42000000000000
|
||||
]
|
||||
}
|
||||
},
|
||||
"operational": {
|
||||
"temperature": 283,
|
||||
"raman_pumps": [
|
||||
{
|
||||
"power": 0.2,
|
||||
"frequency": 205000000000000,
|
||||
"propagation_direction": "counterprop"
|
||||
},
|
||||
{
|
||||
"power": 0.206,
|
||||
"frequency": 201000000000000,
|
||||
"propagation_direction": "counterprop"
|
||||
}
|
||||
]
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1,
|
||||
"longitude": 0,
|
||||
"city": null,
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
14
tests/data/sim_params.json
Normal file
14
tests/data/sim_params.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"raman_computed_channels": [1, 18, 37, 56, 75],
|
||||
"raman_parameters": {
|
||||
"flag_raman": true,
|
||||
"space_resolution": 10e3,
|
||||
"tolerance": 1e-8
|
||||
},
|
||||
"nli_parameters": {
|
||||
"nli_method_name": "ggn_spectrally_separated",
|
||||
"wdm_grid_size": 50e9,
|
||||
"dispersion_tolerance": 1,
|
||||
"phase_shift_tollerance": 0.1
|
||||
}
|
||||
}
|
||||
BIN
tests/data/testService.xls
Normal file
BIN
tests/data/testService.xls
Normal file
Binary file not shown.
79
tests/data/testService_services_expected.json
Normal file
79
tests/data/testService_services_expected.json
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "trx Lorient_KMA",
|
||||
"destination": "trx Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "trx Brest_KLA",
|
||||
"destination": "trx Vannes_KBE",
|
||||
"src-tp-id": "trx Brest_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": 0.0012589254117941673,
|
||||
"path_bandwidth": 10000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "trx Lannion_CAS",
|
||||
"destination": "trx Rennes_STA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Rennes_STA",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673,
|
||||
"path_bandwidth": 60000000000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
tests/data/testTopology.xls
Normal file
BIN
tests/data/testTopology.xls
Normal file
Binary file not shown.
3357
tests/data/testTopology_auto_design_expected.json
Normal file
3357
tests/data/testTopology_auto_design_expected.json
Normal file
File diff suppressed because it is too large
Load Diff
1781
tests/data/testTopology_expected.json
Normal file
1781
tests/data/testTopology_expected.json
Normal file
File diff suppressed because it is too large
Load Diff
1471
tests/data/testTopology_response.json
Normal file
1471
tests/data/testTopology_response.json
Normal file
File diff suppressed because it is too large
Load Diff
8
tests/data/testTopology_response_expected.csv
Normal file
8
tests/data/testTopology_response_expected.csv
Normal file
@@ -0,0 +1,8 @@
|
||||
response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,OSNR-0.1nm,SNR-0.1nm,SNR-bandwidth,baud rate (Gbaud),input power (dBm),path,"spectrum (N,M)",reversed path OSNR-0.1nm,reversed path SNR-0.1nm,reversed path SNR-bandwidth
|
||||
0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,30.84,30.84,26.75,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE,"-284, 4"
|
||||
1,trx Brest_KLA,trx Vannes_KBE,10.0,True,1,1,Voyager,mode 1,22.65,22.11,18.03,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | Edfa0_roadm Brest_KLA | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | Edfa0_fiber (Loudeac → Lorient_KMA)-F054 | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE,"-276, 4"
|
||||
3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,28.29,25.85,21.77,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | Edfa0_fiber (Stbrieuc → Rennes_STA)-F057 | roadm Rennes_STA | trx Rennes_STA,"-284, 4"
|
||||
4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,22.27,22.15,15.05,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | Edfa1_roadm Rennes_STA | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | Edfa0_fiber (Ploermel → Vannes_KBE)- | roadm Vannes_KBE | Edfa0_roadm Vannes_KBE | fiber (Vannes_KBE → Lorient_KMA)-F055 | Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055 | roadm Lorient_KMA | Edfa0_roadm Lorient_KMA | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS,"-266, 6"
|
||||
5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,30.79,28.77,21.68,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | Edfa0_roadm Rennes_STA | fiber (Rennes_STA → Stbrieuc)-F057 | Edfa0_fiber (Rennes_STA → Stbrieuc)-F057 | fiber (Stbrieuc → Lannion_CAS)-F056 | Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056 | roadm Lannion_CAS | trx Lannion_CAS,"-274, 6"
|
||||
6,,,,NO_PATH,,,,,,,,,,,
|
||||
|
||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
218
tests/data/testTopology_services_expected.json
Normal file
218
tests/data/testTopology_services_expected.json
Normal file
@@ -0,0 +1,218 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "trx Lorient_KMA",
|
||||
"destination": "trx Vannes_KBE",
|
||||
"src-tp-id": "trx Lorient_KMA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "trx Brest_KLA",
|
||||
"destination": "trx Vannes_KBE",
|
||||
"src-tp-id": "trx Brest_KLA",
|
||||
"dst-tp-id": "trx Vannes_KBE",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": 0.0012589254117941673,
|
||||
"path_bandwidth": 10000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Brest_KLA",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 1,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Lannion_CAS",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 2,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Lorient_KMA",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 3,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Vannes_KBE",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "trx Lannion_CAS",
|
||||
"destination": "trx Rennes_STA",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx Rennes_STA",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.0012589254117941673,
|
||||
"path_bandwidth": 60000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "4",
|
||||
"source": "trx Rennes_STA",
|
||||
"destination": "trx Lannion_CAS",
|
||||
"src-tp-id": "trx Rennes_STA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 150000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "5",
|
||||
"source": "trx Rennes_STA",
|
||||
"destination": "trx Lannion_CAS",
|
||||
"src-tp-id": "trx Rennes_STA",
|
||||
"dst-tp-id": "trx Lannion_CAS",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 63,
|
||||
"output-power": 0.0019952623149688794,
|
||||
"path_bandwidth": 20000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "6",
|
||||
"source": "trx Lannion_CAS",
|
||||
"destination": "trx a",
|
||||
"src-tp-id": "trx Lannion_CAS",
|
||||
"dst-tp-id": "trx a",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronization": [
|
||||
{
|
||||
"synchronization-id": "3",
|
||||
"svec": {
|
||||
"relaxable": "false",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"3",
|
||||
"1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchronization-id": "4",
|
||||
"svec": {
|
||||
"relaxable": "false",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"4",
|
||||
"5"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
2884
tests/data/testTopology_testresults.json
Normal file
2884
tests/data/testTopology_testresults.json
Normal file
File diff suppressed because it is too large
Load Diff
614
tests/data/testTopology_testservices.json
Normal file
614
tests/data/testTopology_testservices.json
Normal file
@@ -0,0 +1,614 @@
|
||||
{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "trx a",
|
||||
"destination": "trx g",
|
||||
"src-tp-id": "trx a",
|
||||
"dst-tp-id": "trx g",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "2a",
|
||||
"source": "trx a",
|
||||
"destination": "trx h",
|
||||
"src-tp-id": "trx a",
|
||||
"dst-tp-id": "trx h",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "trx f",
|
||||
"destination": "trx b",
|
||||
"src-tp-id": "trx f",
|
||||
"dst-tp-id": "trx b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "ee",
|
||||
"source": "trx c",
|
||||
"destination": "trx f",
|
||||
"src-tp-id": "trx c",
|
||||
"dst-tp-id": "trx f",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm e",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 1,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm g",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "ff",
|
||||
"source": "trx c",
|
||||
"destination": "trx f",
|
||||
"src-tp-id": "trx c",
|
||||
"dst-tp-id": "trx f",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 2 - fake",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 63,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "10",
|
||||
"source": "trx a",
|
||||
"destination": "trx g",
|
||||
"src-tp-id": "trx a",
|
||||
"dst-tp-id": "trx g",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 63,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "11",
|
||||
"source": "trx a",
|
||||
"destination": "trx h",
|
||||
"src-tp-id": "trx a",
|
||||
"dst-tp-id": "trx h",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "bb",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "12",
|
||||
"source": "trx f",
|
||||
"destination": "trx b",
|
||||
"src-tp-id": "trx f",
|
||||
"dst-tp-id": "trx b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 3",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 62500000000.0,
|
||||
"max-nb-of-channel": 76,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "trx b",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "13",
|
||||
"source": "trx c",
|
||||
"destination": "trx f",
|
||||
"src-tp-id": "trx c",
|
||||
"dst-tp-id": "trx f",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "14",
|
||||
"source": "trx c",
|
||||
"destination": "trx f",
|
||||
"src-tp-id": "trx c",
|
||||
"dst-tp-id": "trx f",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm e",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 1,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm g",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "e:1# /",
|
||||
"source": "trx a",
|
||||
"destination": "trx g",
|
||||
"src-tp-id": "trx a",
|
||||
"dst-tp-id": "trx g",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager_16QAM",
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "b-2a",
|
||||
"source": "trx a",
|
||||
"destination": "trx h",
|
||||
"src-tp-id": "trx a",
|
||||
"dst-tp-id": "trx h",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3a;?",
|
||||
"source": "trx f",
|
||||
"destination": "trx b",
|
||||
"src-tp-id": "trx f",
|
||||
"dst-tp-id": "trx b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "ee-s",
|
||||
"source": "trx c",
|
||||
"destination": "trx f",
|
||||
"src-tp-id": "trx c",
|
||||
"dst-tp-id": "trx f",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": 80,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm e",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 1,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm g",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "ff-b",
|
||||
"source": "trx c",
|
||||
"destination": "trx f",
|
||||
"src-tp-id": "trx c",
|
||||
"dst-tp-id": "trx f",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": 0.001,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "10-z",
|
||||
"source": "trx a",
|
||||
"destination": "trx g",
|
||||
"src-tp-id": "trx a",
|
||||
"dst-tp-id": "trx g",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": 63,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "11 g",
|
||||
"source": "trx a",
|
||||
"destination": "trx h",
|
||||
"src-tp-id": "trx a",
|
||||
"dst-tp-id": "trx h",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": 300000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "12<",
|
||||
"source": "trx f",
|
||||
"destination": "trx b",
|
||||
"src-tp-id": "trx f",
|
||||
"dst-tp-id": "trx b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "12>",
|
||||
"source": "trx f",
|
||||
"destination": "trx b",
|
||||
"src-tp-id": "trx f",
|
||||
"dst-tp-id": "trx b",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
}
|
||||
],
|
||||
"spacing": 30000000000.0,
|
||||
"max-nb-of-channel": null,
|
||||
"output-power": null,
|
||||
"path_bandwidth": null
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronization": [
|
||||
{
|
||||
"synchronization-id": "1",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"1",
|
||||
"2a"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchronization-id": "3",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"3",
|
||||
"1"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchronization-id": "ff",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"ff",
|
||||
"13"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"synchronization-id": "13",
|
||||
"svec": {
|
||||
"relaxable": "False",
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"13",
|
||||
"14"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -77,7 +77,22 @@
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"uid": "Att_B",
|
||||
"type": "Fused",
|
||||
"params":{
|
||||
"loss":16
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0,
|
||||
"city": "Corlay",
|
||||
"region": "RLD"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Site_B",
|
||||
"type": "Transceiver",
|
||||
@@ -110,6 +125,10 @@
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa2",
|
||||
"to_node": "Att_B"
|
||||
},
|
||||
{
|
||||
"from_node": "Att_B",
|
||||
"to_node": "Site_B"
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
from gnpy.core.elements import Edfa
|
||||
from numpy import zeros, array
|
||||
from json import load, dumps
|
||||
from json import load
|
||||
from gnpy.core.elements import Transceiver, Fiber, Edfa
|
||||
from gnpy.core.utils import lin2db, db2lin
|
||||
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
|
||||
from gnpy.core.equipment import load_equipment
|
||||
from gnpy.core.network import build_network, load_network, set_roadm_loss
|
||||
from gnpy.core.equipment import load_equipment, automatic_fmax, automatic_nch
|
||||
from gnpy.core.network import build_network, load_network
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
@@ -66,7 +66,9 @@ def setup_trx():
|
||||
def si(nch_and_spacing, bw):
|
||||
"""parametrize a channel comb with nb_channel, spacing and signal bw"""
|
||||
nb_channel, spacing = nch_and_spacing
|
||||
return create_input_spectral_information(191.3e12, 0.15, bw, 1e-3, spacing, nb_channel)
|
||||
f_min = 191.3e12
|
||||
f_max = automatic_fmax(f_min, spacing, nb_channel)
|
||||
return create_input_spectral_information(f_min, f_max, 0.15, bw, 1e-3, spacing)
|
||||
|
||||
@pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)])
|
||||
def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si):
|
||||
@@ -77,7 +79,7 @@ def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si):
|
||||
pin = pin/db2lin(gain)
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, -gain)
|
||||
pref = Pref(0, -gain, lin2db(len(frequencies)))
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
result = edfa.nf
|
||||
assert pytest.approx(nf_expected, abs=0.01) == result[0]
|
||||
@@ -91,7 +93,7 @@ def test_fixed_gain_nf(gain, nf_expected, setup_edfa_fixed_gain, si):
|
||||
pin = pin/db2lin(gain)
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, -gain)
|
||||
pref = Pref(0, -gain, lin2db(len(frequencies)))
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
|
||||
assert pytest.approx(nf_expected, abs=0.01) == edfa.nf[0]
|
||||
@@ -116,7 +118,7 @@ def test_compare_nf_models(gain, setup_edfa_variable_gain, si):
|
||||
pin = pin/db2lin(gain)
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, -gain)
|
||||
pref = Pref(0, -gain, lin2db(len(frequencies)))
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
nf_model = edfa.nf[0]
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
@@ -135,7 +137,7 @@ def test_ase_noise(gain, si, setup_edfa_variable_gain, setup_trx, bw):
|
||||
pin = array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
|
||||
baud_rates = array([c.baud_rate for c in si.carriers])
|
||||
edfa.operational.gain_target = gain
|
||||
pref = Pref(0, 0)
|
||||
pref = Pref(0, 0, lin2db(len(frequencies)))
|
||||
edfa.interpol_params(frequencies, pin, baud_rates, pref)
|
||||
nf = edfa.nf
|
||||
pin = lin2db(pin[0]*1e3)
|
||||
|
||||
86
tests/test_automaticmodefeature.py
Normal file
86
tests/test_automaticmodefeature.py
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/usr/bin/env python3
|
||||
# TelecomInfraProject/gnpy/examples
|
||||
# Module name : test_automaticmodefeature.py
|
||||
# Version :
|
||||
# License : BSD 3-Clause Licence
|
||||
# Copyright (c) 2018, Telecom Infra Project
|
||||
|
||||
"""
|
||||
@author: esther.lerouzic
|
||||
checks that empty info on mode, power, nbchannel in service file are supported
|
||||
uses combination of [mode, pow, nb channel] filled or empty defined in meshTopologyToy_services.json
|
||||
leading to feasible path or not, and check the propagate and propagate_and_optimize_mode
|
||||
return the expected based on a reference toy example
|
||||
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch
|
||||
from gnpy.core.network import load_network, build_network
|
||||
from examples.path_requests_run import requests_from_json, correct_route_list, load_requests
|
||||
from gnpy.core.request import compute_path_dsjctn, propagate, propagate_and_optimize_mode
|
||||
from gnpy.core.utils import db2lin, lin2db
|
||||
from gnpy.core.elements import Roadm
|
||||
|
||||
network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json'
|
||||
service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json'
|
||||
result_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testresults.json'
|
||||
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
|
||||
|
||||
@pytest.mark.parametrize("net",[network_file_name])
|
||||
@pytest.mark.parametrize("eqpt", [eqpt_library_name])
|
||||
@pytest.mark.parametrize("serv",[service_file_name])
|
||||
@pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 2', 'mode 1', 'mode 2', 'nok']])
|
||||
def test_automaticmodefeature(net,eqpt,serv,expected_mode):
|
||||
data = load_requests(serv, eqpt, bidir=False)
|
||||
equipment = load_equipment(eqpt)
|
||||
network = load_network(net,equipment)
|
||||
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
|
||||
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
|
||||
rqs = requests_from_json(data, equipment)
|
||||
rqs = correct_route_list(network, rqs)
|
||||
dsjn = []
|
||||
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
|
||||
path_res_list = []
|
||||
|
||||
for i,pathreq in enumerate(rqs):
|
||||
|
||||
# use the power specified in requests but might be different from the one specified for design
|
||||
# the power is an optional parameter for requests definition
|
||||
# if optional, use the one defines in eqt_config.json
|
||||
p_db = lin2db(pathreq.power*1e3)
|
||||
p_total_db = p_db + lin2db(pathreq.nb_channel)
|
||||
print(f'request {pathreq.request_id}')
|
||||
print(f'Computing path from {pathreq.source} to {pathreq.destination}')
|
||||
print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output
|
||||
|
||||
total_path = pths[i]
|
||||
print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}\n')
|
||||
# for debug
|
||||
# print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}')
|
||||
if pathreq.baud_rate is not None:
|
||||
print(pathreq.format)
|
||||
path_res_list.append(pathreq.format)
|
||||
total_path = propagate(total_path,pathreq,equipment)
|
||||
else:
|
||||
total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment)
|
||||
# if no baudrate satisfies spacing, no mode is returned and an empty path is returned
|
||||
# a warning is shown in the propagate_and_optimize_mode
|
||||
if mode is not None :
|
||||
print (mode['format'])
|
||||
path_res_list.append(mode['format'])
|
||||
else :
|
||||
print('nok')
|
||||
path_res_list.append('nok')
|
||||
print(path_res_list)
|
||||
assert path_res_list==expected_mode
|
||||
|
||||
109
tests/test_disjunction.py
Normal file
109
tests/test_disjunction.py
Normal file
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
# TelecomInfraProject/gnpy/examples
|
||||
# Module name : test_disjunction.py
|
||||
# Version :
|
||||
# License : BSD 3-Clause Licence
|
||||
# Copyright (c) 2018, Telecom Infra Project
|
||||
|
||||
"""
|
||||
@author: esther.lerouzic
|
||||
checks that computed paths are disjoint as specified in the json service file
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch
|
||||
from gnpy.core.network import load_network, build_network
|
||||
from examples.path_requests_run import (requests_from_json , correct_route_list ,
|
||||
load_requests , disjunctions_from_json)
|
||||
from gnpy.core.request import compute_path_dsjctn, isdisjoint , find_reversed_path
|
||||
from gnpy.core.utils import db2lin, lin2db
|
||||
from gnpy.core.elements import Roadm
|
||||
from gnpy.core.spectrum_assignment import build_oms_list
|
||||
|
||||
network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json'
|
||||
service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json'
|
||||
result_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testresults.json'
|
||||
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
|
||||
|
||||
@pytest.mark.parametrize("net",[network_file_name])
|
||||
@pytest.mark.parametrize("eqpt", [eqpt_library_name])
|
||||
@pytest.mark.parametrize("serv",[service_file_name])
|
||||
def test_disjunction(net,eqpt,serv):
|
||||
data = load_requests(serv, eqpt, bidir=False)
|
||||
equipment = load_equipment(eqpt)
|
||||
network = load_network(net,equipment)
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
|
||||
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
build_oms_list(network, equipment)
|
||||
|
||||
rqs = requests_from_json(data, equipment)
|
||||
rqs = correct_route_list(network, rqs)
|
||||
dsjn = disjunctions_from_json(data)
|
||||
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
|
||||
print(dsjn)
|
||||
|
||||
dsjn_list = [d.disjunctions_req for d in dsjn ]
|
||||
|
||||
# assumes only pairs in dsjn list
|
||||
test = True
|
||||
for e in dsjn_list:
|
||||
rqs_id_list = [r.request_id for r in rqs]
|
||||
p1 = pths[rqs_id_list.index(e[0])][1:-1]
|
||||
p2 = pths[rqs_id_list.index(e[1])][1:-1]
|
||||
if isdisjoint(p1, p2) + isdisjoint(p1, find_reversed_path(p2)) > 0:
|
||||
test = False
|
||||
print(f'Computed path (roadms):{[e.uid for e in p1 if isinstance(e, Roadm)]}\n')
|
||||
print(f'Computed path (roadms):{[e.uid for e in p2 if isinstance(e, Roadm)]}\n')
|
||||
break
|
||||
print(dsjn_list)
|
||||
assert test
|
||||
|
||||
@pytest.mark.parametrize("net",[network_file_name])
|
||||
@pytest.mark.parametrize("eqpt", [eqpt_library_name])
|
||||
@pytest.mark.parametrize("serv",[service_file_name])
|
||||
def test_does_not_loop_back(net,eqpt,serv):
|
||||
data = load_requests(serv, eqpt, bidir=False)
|
||||
equipment = load_equipment(eqpt)
|
||||
network = load_network(net,equipment)
|
||||
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
|
||||
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
build_oms_list(network, equipment)
|
||||
|
||||
rqs = requests_from_json(data, equipment)
|
||||
rqs = correct_route_list(network, rqs)
|
||||
dsjn = disjunctions_from_json(data)
|
||||
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
|
||||
|
||||
# check that computed paths do not loop back ie each element appears only once
|
||||
test = True
|
||||
for p in pths :
|
||||
for el in p:
|
||||
p.remove(el)
|
||||
a = [e for e in p if e.uid == el.uid]
|
||||
if a :
|
||||
test = False
|
||||
break
|
||||
|
||||
assert test
|
||||
|
||||
# TODO : test that identical requests are correctly agregated
|
||||
# and reproduce disjunction vector as well as route constraints
|
||||
# check that requests with different parameters are not aggregated
|
||||
|
||||
# check that the total agregated bandwidth is the same after aggregation
|
||||
|
||||
#
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user