mirror of
				https://github.com/Telecominfraproject/oopt-gnpy.git
				synced 2025-10-31 01:57:54 +00:00 
			
		
		
		
	Compare commits
	
		
			1019 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 40bbb9b553 | ||
|   | c5a29d7b81 | ||
|   | 2fa0dac2f1 | ||
|   | 02916691c7 | ||
|   | 5d3526a74c | ||
|   | 459a82150b | ||
|   | f67e9c4914 | ||
|   | 0c2bf58080 | ||
|   | 396020eeb5 | ||
|   | 84fd574df9 | ||
|   | e97d20c3fe | ||
|   | f52ff92918 | ||
|   | 4707abd2bd | ||
|   | 5b824a7a2c | ||
|   | 3784627bc9 | ||
|   | 1b265a3d27 | ||
|   | 927d73c720 | ||
|   | 1cf90a019b | ||
|   | e2fac13219 | ||
|   | be5519455f | ||
|   | f98eb2c10c | ||
|   | 60b9256f22 | ||
|   | 94b9c16d67 | ||
|   | eaccd63739 | ||
|   | 8fcb61f12c | ||
|   | 8e62955bb0 | ||
|   | 561fd76a20 | ||
|   | ccb6653f50 | ||
|   | 3a31d458ee | ||
|   | 22d55ae881 | ||
|   | 3324645f78 | ||
|   | 3014a881f5 | ||
|   | 35877022ec | ||
|   | 9b985d1fc5 | ||
|   | cd95c83bbf | ||
|   | f9dbf7d132 | ||
|   | d2a8d8e887 | ||
|   | 35c4073292 | ||
|   | 120c326e77 | ||
|   | 01115f9852 | ||
|   | b91ea1828f | ||
|   | d2a294ac5a | ||
|   | f41acf31f6 | ||
|   | 8cef09158f | ||
|   | 918c19b1bc | ||
|   | 6820a3fc36 | ||
|   | 7e8ed590eb | ||
|   | 4218b7ef44 | ||
|   | 87af343b38 | ||
|   | 26cd33b4dc | ||
|   | 861724ef4f | ||
|   | 86492cff60 | ||
|   | 27fd5cdad6 | ||
|   | 2fc444be4b | ||
|   | d3490ae30c | ||
|   | 59c3895a51 | ||
|   | 11bc41b941 | ||
|   | 9a7f94a391 | ||
|   | 6487b98136 | ||
|   | 15df99510f | ||
|   | 3d5b1fcf64 | ||
|   | 928bc42cb9 | ||
|   | 643680ec47 | ||
|   | a4a144a319 | ||
|   | 093085fba8 | ||
|   | c56ea898a6 | ||
|   | a5398a5c57 | ||
|   | 6dd40935b7 | ||
|   | e6ee512001 | ||
|   | e13d27c1f5 | ||
|   | bb552fbdd6 | ||
|   | ed8a3dd933 | ||
|   | 3204077a6c | ||
|   | 85d1bf4e1e | ||
|   | 42edb2e6b9 | ||
|   | 21174a4190 | ||
|   | f6c2da24cd | ||
|   | 9f16aaac61 | ||
|   | 29fc9d7dac | ||
|   | be3af5c2e5 | ||
|   | e0e9ebde28 | ||
|   | 5dc16a39c5 | ||
|   | 416da5c60b | ||
|   | f8047f9afe | ||
|   | 9e91933106 | ||
|   | 7407e6809b | ||
|   | 56f66779f9 | ||
|   | 2704c56e50 | ||
|   | ba4cc1ceef | ||
|   | 8396cea652 | ||
|   | 2b1029f3b6 | ||
|   | 8e2709490f | ||
|   | ebf8249154 | ||
|   | aaddffcb2e | ||
|   | 29d1f8c666 | ||
|   | 0126645c4d | ||
|   | 1d657d6819 | ||
|   | 0b965d931c | ||
|   | d3eaa4d7ba | ||
|   | 1dbbc6273b | ||
|   | efa8b83249 | ||
|   | 30599bf63a | ||
|   | 7c14fe02ab | ||
|   | ee92011b21 | ||
|   | 9861a22ef9 | ||
|   | 59f9f35817 | ||
|   | 1bb475671d | ||
|   | 566dedbdbb | ||
|   | b44c4cec16 | ||
|   | 93d11ba408 | ||
|   | 637670fcfa | ||
|   | 7836297708 | ||
|   | 1f8b4ab9a2 | ||
|   | 05eb312f4a | ||
|   | a98e244abd | ||
|   | c945bc40fe | ||
|   | 749b9287a9 | ||
|   | 202c76bd6e | ||
|   | eec0943ca2 | ||
|   | cd0415e523 | ||
|   | 06d59a5834 | ||
|   | 33dcdde422 | ||
|   | 94949d955b | ||
|   | b74d0a4919 | ||
|   | c8fa7635e0 | ||
|   | 4c6cfbda5d | ||
|   | 80e9423590 | ||
|   | 78010aaaef | ||
|   | 3857ab1dbb | ||
|   | dbb09e4108 | ||
|   | 94a8f3568a | ||
|   | ae7c9321d0 | ||
|   | 648cc3a8e5 | ||
|   | f9e0d18a9d | ||
|   | 2d57fd9f85 | ||
|   | f053f32301 | ||
|   | 8e9d715e9f | ||
|   | 9fd55a5289 | ||
|   | 914d0dbecd | ||
|   | c38fe72ff7 | ||
|   | 80f63d32ed | ||
|   | 9030f8f84f | ||
|   | 0d5f1c7d80 | ||
|   | 5bc42332cd | ||
|   | 093b85d4a3 | ||
|   | 0efa0d310d | ||
|   | 8eb5980ca9 | ||
|   | 754be7ca08 | ||
|   | c5c5b693f2 | ||
|   | 7f816eb6e7 | ||
|   | 1009b44d2a | ||
|   | 3b61c6ca4c | ||
|   | cc11bd186c | ||
|   | 0d1225824e | ||
|   | a2ecfd924c | ||
|   | 95c3c9c488 | ||
|   | 11033a284f | ||
|   | 357e6cbf18 | ||
|   | bfa6d29908 | ||
|   | 21e0589e7f | ||
|   | 4d836246ec | ||
|   | 566943a099 | ||
|   | a4c1ea1f55 | ||
|   | 6b10ed15f8 | ||
|   | c8daa5ed8c | ||
|   | 0b03725295 | ||
|   | ee5e64408d | ||
|   | b96ffe6c7b | ||
|   | 3b981853d4 | ||
|   | 6fa3ef8df1 | ||
|   | 1b2b048b47 | ||
|   | 94c5281260 | ||
|   | 7da4ec08d8 | ||
|   | 2b473d26d3 | ||
|   | 9e74e8b0a0 | ||
|   | 648039521e | ||
|   | 24bc023a07 | ||
|   | 19c2ae7f7a | ||
|   | 9f6894a176 | ||
|   | a094568d6e | ||
|   | f728d96d07 | ||
|   | 6b1fb7061f | ||
|   | 7ef505f259 | ||
|   | c8d394348d | ||
|   | 0daa1c3e8c | ||
|   | 60bafd114d | ||
|   | 11509f5686 | ||
|   | 785c823fa2 | ||
|   | 9faf6430a5 | ||
|   | 01c566a325 | ||
|   | baa9171315 | ||
|   | 2e50337f38 | ||
|   | 8daa298699 | ||
|   | 76cdd5dc71 | ||
|   | 07eb2dd13a | ||
|   | 04e764d024 | ||
|   | 05ccb14e5d | ||
|   | f60d035e66 | ||
|   | 0823f8de46 | ||
|   | a453c57996 | ||
|   | 2f84bb5286 | ||
|   | 7b8e68aea9 | ||
|   | 8d553a255f | ||
|   | 2766e37438 | ||
|   | f56e64410b | ||
|   | 15ea7218e9 | ||
|   | db28011c61 | ||
|   | faccc23018 | ||
|   | 49514c0c70 | ||
|   | 0b1557fdf1 | ||
|   | 46f89aa770 | ||
|   | 3548ed74e2 | ||
|   | 145653df6e | ||
|   | c7589e0bca | ||
|   | 531810cc85 | ||
|   | 63a6256b5e | ||
|   | c3febb6db4 | ||
|   | 8b1d8b3479 | ||
|   | 3168603908 | ||
|   | a2128227bd | ||
|   | 376826b3ae | ||
|   | 4b258cdf2e | ||
|   | fbdd132a3d | ||
|   | eebcebb33d | ||
|   | 20152036ff | ||
|   | 2a477071a0 | ||
|   | f02d11e8bc | ||
|   | 0d542f22a7 | ||
|   | 5af195bd2b | ||
|   | 7ab93e7cd9 | ||
|   | 0aec47ddeb | ||
|   | 9a54dbab43 | ||
|   | fc03be8bbe | ||
|   | 32f10a4507 | ||
|   | 04544d41f6 | ||
|   | c87be89e07 | ||
|   | efa05f0653 | ||
|   | 4cf4db9bc2 | ||
|   | 16434c5737 | ||
|   | 14ee9c9a91 | ||
|   | 7cfc4bd1ec | ||
|   | 9d55cde50d | ||
|   | 72183c24da | ||
|   | 4d1a628488 | ||
|   | fdeaf75361 | ||
|   | 5695fafac6 | ||
|   | 0fe1d195a3 | ||
|   | ed1aa0aa03 | ||
|   | 3fc024f5ba | ||
|   | 4f8177908f | ||
|   | ad64595d75 | ||
|   | 80133e9521 | ||
|   | de58d7d7c2 | ||
|   | 491a05c5a7 | ||
|   | 1d791aa295 | ||
|   | 58921bc346 | ||
|   | ab6a91692b | ||
|   | 3b45968799 | ||
|   | c7d69b9a99 | ||
|   | 1eeed78430 | ||
|   | 1cdafbae0f | ||
|   | 47b4f87bc5 | ||
|   | e24b766cdc | ||
|   | 51fb6bd68e | ||
|   | 7e405a0514 | ||
|   | 194a13e607 | ||
|   | 856f07e707 | ||
|   | 4582aed895 | ||
|   | 734b6dfef4 | ||
|   | da6e4f33a4 | ||
|   | 5a1e3f30b3 | ||
|   | 094af16792 | ||
|   | 9474ff17de | ||
|   | c675f5bd38 | ||
|   | 2556658e68 | ||
|   | 4d5d10935a | ||
|   | eb87e36781 | ||
|   | 14d8d793f3 | ||
|   | bc4b6642a0 | ||
|   | fe2b39ee3b | ||
|   | d56c91ca7f | ||
|   | 25819fadf5 | ||
|   | 0cacb8851d | ||
|   | 14f98836ed | ||
|   | 82ce3384ba | ||
|   | 1f1877f7a9 | ||
|   | db26ce07db | ||
|   | e08ae9c959 | ||
|   | 2de1b5567a | ||
|   | 639b379a5b | ||
|   | 0465397b1d | ||
|   | d3ec39d506 | ||
|   | bfe68a5948 | ||
|   | 2ea3363613 | ||
|   | 89cce6e6a3 | ||
|   | 0f10ac706c | ||
|   | 66d26f0ffa | ||
|   | 3c96914482 | ||
|   | 61b1e73362 | ||
|   | 03435079cc | ||
|   | 6661907c1d | ||
|   | fe811f725c | ||
|   | 80eced85ec | ||
|   | 2960d307fa | ||
|   | c41cddfff5 | ||
|   | 8598e6591f | ||
|   | 5e2259062c | ||
|   | d483802a86 | ||
|   | 9a0eece69c | ||
|   | 1657bfd05f | ||
|   | 49bf558916 | ||
|   | 99f44a597b | ||
|   | a21f3fe6ee | ||
|   | 0ccbb2960c | ||
|   | 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 | ||
|   | 9be0607b2e | ||
|   | 6dc3f2ffa6 | ||
|   | df7cbf0b76 | ||
|   | 19b6378b1a | ||
|   | 29cb2b50a8 | ||
|   | 5932c014a0 | ||
|   | a3f75e9af0 | ||
|   | 9ed31e2c4e | ||
|   | 8599f63fbf | ||
|   | 07de78cb05 | ||
|   | f764bbe080 | ||
|   | 7aa343b767 | ||
|   | 69198779a7 | ||
|   | 3088032ad8 | ||
|   | dfaa12598d | ||
|   | 1c724cdc6c | ||
|   | b59423fb01 | ||
|   | 96bceed102 | ||
|   | f8e146b9b9 | ||
|   | c54ddb644a | ||
|   | 2be616411f | ||
|   | 7415744807 | ||
|   | 23905a90f4 | ||
|   | 83444b329e | ||
|   | 9898dc85a9 | ||
|   | 661287f600 | ||
|   | 353c6a77e9 | ||
|   | 8c006fec3f | ||
|   | 5896b7ce6a | ||
|   | f831f6cb2c | ||
|   | b505a1ae01 | ||
|   | a0fac17c0e | ||
|   | 851f606fc0 | ||
|   | 8940430ee0 | ||
|   | 06d3927275 | ||
|   | 7cea13e90d | ||
|   | de2a504078 | ||
|   | df14b441a3 | ||
|   | ab1391440f | ||
|   | 310d32dcea | ||
|   | f8cd822c92 | ||
|   | 3ade885e41 | ||
|   | 008a88192c | ||
|   | 639e7f012c | ||
|   | c8ecc16648 | ||
|   | 225fb1ec0c | ||
|   | c943e0e9b4 | ||
|   | 3d8ac83fcc | ||
|   | 06399ca8af | ||
|   | 8f8fc13ded | ||
|   | 2b018cb9a5 | ||
|   | 9577f4c9a3 | ||
|   | 771d98cc10 | ||
|   | 91e875d54c | ||
|   | da39f1489f | ||
|   | 2940576681 | ||
|   | a49c137b78 | ||
|   | 87e748cd83 | ||
|   | 94c2e332bb | ||
|   | 1437b6010e | ||
|   | 5fc203482d | ||
|   | ff6d81b749 | ||
|   | 167e644bd0 | ||
|   | d1c7489768 | ||
|   | a783e165dd | ||
|   | 45bdd82864 | ||
|   | 79c5cb6b78 | ||
|   | 63ade5fdef | ||
|   | 853b8c7aa3 | ||
|   | 0655fb60de | ||
|   | 69b28e3508 | ||
|   | 5c16d9539f | ||
|   | e3acf02bde | ||
|   | 10268e82d4 | ||
|   | bc44bf726a | ||
|   | 9839681bc0 | ||
|   | beb292cb07 | ||
|   | 29c4134f60 | ||
|   | a6157f328d | ||
|   | c6432e2c28 | ||
|   | 4a756bf2a9 | ||
|   | b77cc5cd40 | ||
|   | 9f94af6ff7 | ||
|   | 7aba40cd5b | ||
|   | fb6c17c5ff | ||
|   | 4504d80c80 | ||
|   | 8a8c8989cb | ||
|   | 20731dbe4b | ||
|   | 55393ca9eb | ||
|   | e9aa4d5601 | ||
|   | 6d49769df9 | ||
|   | 0333e9d094 | ||
|   | 53bedca50a | ||
|   | b9518ca987 | ||
|   | 794e713d6d | ||
|   | 5e1fd7501e | ||
|   | c86ea206d9 | ||
|   | b810cf84c2 | ||
|   | 15bc5db2ed | ||
|   | 4845d9005e | ||
|   | bed4e9f1e1 | ||
|   | 7b20db10cc | ||
|   | d59b3e8c46 | ||
|   | 8fccbb0ac2 | ||
|   | f36a610e52 | ||
|   | 479a2f358e | ||
|   | 8795c357ae | ||
|   | d8cb7526bb | ||
|   | 6ead8e391b | ||
|   | 362f45083d | ||
|   | 36218037ec | ||
|   | 584b56bc83 | ||
|   | d33602e131 | ||
|   | 4304b49bf0 | ||
|   | 4f5325acac | ||
|   | f8a40bfaf0 | ||
|   | 52dfb20a2b | ||
|   | 34b20cdfe0 | ||
|   | f462201499 | ||
|   | 5106bdf634 | ||
|   | b93c6dbcbd | ||
|   | 2ca141baba | ||
|   | 01fe5d2147 | ||
|   | 74830cede4 | ||
|   | 2f21cc29f7 | ||
|   | 05f8d97d68 | ||
|   | 04c4795192 | ||
|   | ff6e379b6f | ||
|   | 25cfc375bc | ||
|   | 627184ef2d | ||
|   | 180e1178ef | ||
|   | 1c33454ce3 | ||
|   | c7abe11dd8 | ||
|   | 7a6feccc8b | ||
|   | f05e578f51 | ||
|   | 277ebea2b5 | ||
|   | 4982c5e147 | ||
|   | 726e217205 | ||
|   | 695c538ee5 | ||
|   | e9b287ceac | ||
|   | ecdae68b62 | ||
|   | e737564b32 | ||
|   | 75c4a8d96d | ||
|   | 28dc1b050f | ||
|   | 4a9a7e31ba | ||
|   | 92da31f905 | ||
|   | afd264c816 | ||
|   | d676e3e217 | ||
|   | 4af117aacf | ||
|   | b27e567a5e | ||
|   | 8ec9a5bf99 | ||
|   | f129f92cfd | ||
|   | ef87373d4b | ||
|   | ff938d4ced | ||
|   | ae555e0ce1 | ||
|   | 44db6093f5 | ||
|   | d4bf6ce201 | ||
|   | 0b1c7bfa30 | ||
|   | 80f8a345b5 | ||
|   | c929ce0257 | ||
|   | 7d3ab357d0 | ||
|   | 84d87602b8 | ||
|   | 14938ea7dd | ||
|   | be0c052e5e | ||
|   | af90a6658e | ||
|   | 9720c979b3 | ||
|   | 7c86186416 | ||
|   | 404dbfe725 | ||
|   | 02eac208dd | ||
|   | bc9a0432cd | ||
|   | dba7846d6c | ||
|   | 238d7723bb | ||
|   | d2724bb1a5 | ||
|   | 8e046a9b50 | ||
|   | 1b808afd5d | ||
|   | 8d6f69eb05 | ||
|   | c4562df955 | ||
|   | 8e1e8a8be3 | ||
|   | 363e92e072 | ||
|   | c5a52fdb6d | ||
|   | 2dd096cfab | ||
|   | 80e1ce12ce | ||
|   | 2777d957e4 | ||
|   | 898aa4f41a | ||
|   | c9ece6ad7c | ||
|   | a6265c1b8d | ||
|   | 5646714c13 | ||
|   | 8ab13537a9 | ||
|   | 25e14e4846 | ||
|   | 55d67f890d | ||
|   | 9b3d7614f5 | ||
|   | a3273d24b5 | ||
|   | ffd7bec485 | ||
|   | 0aef76407d | ||
|   | f2ad236863 | ||
|   | 487237638e | ||
|   | 3a78ccafce | 
							
								
								
									
										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 /opt/application/oopt-gnpy/gnpy/example-data /shared | ||||||
|  | exec "$@" | ||||||
							
								
								
									
										47
									
								
								.docker-travis.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										47
									
								
								.docker-travis.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | #!/bin/bash | ||||||
|  |  | ||||||
|  | set -e | ||||||
|  |  | ||||||
|  | IMAGE_NAME=telecominfraproject/oopt-gnpy | ||||||
|  | IMAGE_TAG=$(git describe --tags) | ||||||
|  |  | ||||||
|  | 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} | ||||||
|  |  | ||||||
|  |   # shared directory setup: do not clobber the real data | ||||||
|  |   mkdir trash | ||||||
|  |   cd trash | ||||||
|  |   docker run -it --rm --volume $(pwd):/shared ${IMAGE_NAME} gnpy-transmission-example | ||||||
|  | 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 | ||||||
|  |   fi | ||||||
|  | fi | ||||||
							
								
								
									
										1
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.dockerignore
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | venv/ | ||||||
							
								
								
									
										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. | ||||||
							
								
								
									
										7
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.github/pull_request_template.md
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | # Thanks for contributing to GNPy | ||||||
|  |  | ||||||
|  | If it isn't much trouble, please send your contribution as patches to our Gerrit. | ||||||
|  | Here's [how to submit patches](https://review.gerrithub.io/Documentation/intro-gerrit-walkthrough-github.html), and here's a [list of stuff we are currently working on](https://review.gerrithub.io/p/Telecominfraproject/oopt-gnpy/+/dashboard/main:main). | ||||||
|  | Just sign in via your existing GitHub account. | ||||||
|  |  | ||||||
|  | However, if you feel more comfortable with filing GitHub PRs, we can work with that too. | ||||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -2,6 +2,8 @@ | |||||||
| __pycache__/ | __pycache__/ | ||||||
| *.py[cod] | *.py[cod] | ||||||
| *$py.class | *$py.class | ||||||
|  | .ipynb_checkpoints | ||||||
|  | .idea | ||||||
|  |  | ||||||
| # C extensions | # C extensions | ||||||
| *.so | *.so | ||||||
| @@ -63,3 +65,5 @@ target/ | |||||||
|  |  | ||||||
| # MacOS DS_store | # MacOS DS_store | ||||||
| .DS_Store | .DS_Store | ||||||
|  |  | ||||||
|  | venv/ | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitreview
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | [gerrit] | ||||||
|  | host=review.gerrithub.io | ||||||
|  | project=Telecominfraproject/oopt-gnpy | ||||||
|  | defaultrebase=0 | ||||||
							
								
								
									
										4
									
								
								.readthedocs.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.readthedocs.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | build: | ||||||
|  |     image: latest | ||||||
|  | python: | ||||||
|  |     version: 3.6 | ||||||
							
								
								
									
										29
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								.travis.yml
									
									
									
									
									
								
							| @@ -1,9 +1,28 @@ | |||||||
|  | dist: focal | ||||||
|  | os: linux | ||||||
| language: python | language: python | ||||||
|  | services: docker | ||||||
| python: | python: | ||||||
|   - "3.6" |   - "3.6" | ||||||
| # command to install dependencies |   - "3.7" | ||||||
| install: |   - "3.8" | ||||||
|   - pip install -r requirements.txt |   - "3.9" | ||||||
| # command to run tests | before_install: | ||||||
|  |   - sudo apt-get -y install graphviz | ||||||
|  | install: skip | ||||||
| script: | script: | ||||||
|   - pytest |   - pip install --editable . | ||||||
|  |   - pip install pytest-cov rstcheck | ||||||
|  |   - pytest --cov-report=xml --cov=gnpy -v | ||||||
|  |   - rstcheck --ignore-roles cite *.rst | ||||||
|  |   - sphinx-build -W --keep-going 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 | ||||||
|   | |||||||
							
								
								
									
										45
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | --- | ||||||
|  | - project: | ||||||
|  |     check: | ||||||
|  |       jobs: | ||||||
|  |         - tox-py38-cover | ||||||
|  |         - coverage-diff: | ||||||
|  |             voting: false | ||||||
|  |             dependencies: | ||||||
|  |               - tox-py38-cover-previous | ||||||
|  |               - tox-py38-cover | ||||||
|  |             vars: | ||||||
|  |               coverage_job_name_previous: tox-py38-cover-previous | ||||||
|  |               coverage_job_name_current: tox-py38-cover | ||||||
|  |         - tox-linters-diff: | ||||||
|  |             voting: false | ||||||
|  |         - tox-py36-el8 | ||||||
|  |         - tox-docs-f32 | ||||||
|  |         - tox-py38-cover-previous | ||||||
|  |     gate: | ||||||
|  |       jobs: | ||||||
|  |         - tox-py38-f32 | ||||||
|  |         - tox-docs-f32 | ||||||
|  |     tag: | ||||||
|  |       jobs: | ||||||
|  |         - oopt-release-python: | ||||||
|  |             secrets: | ||||||
|  |               - secret: pypi-oopt-gnpy | ||||||
|  |                 name: pypi_info | ||||||
|  |                 pass-to-parent: true | ||||||
|  |  | ||||||
|  | - secret: | ||||||
|  |     name: pypi-oopt-gnpy | ||||||
|  |     data: | ||||||
|  |       username: __token__ | ||||||
|  |       password: !encrypted/pkcs1-oaep | ||||||
|  |         - Taod9JmSMtVAvC5ShSbB3UWuccktQvutdySrj0G7a1Nk4tKFQIdwDXEnBuLpHsZVvsU9Q | ||||||
|  |           6uk4wRVQABDSdNNI/+M/1FwmZfoxuOXa02U5S1deuxW/rBHTxzYcuB8xriwhArBvTiDMk | ||||||
|  |           zyWHVysgDsjlR+85h/DkEhvsaMRDLYWqFwYgXizMoGNKVkwDVIH+qkhBmbggQfDpcYPKT | ||||||
|  |           1gq0d6fw0eKVJtO8+vonMEcE0sWZvHmZvSSu0H++gxoe1W/JtzbCteH3Ak0zktwBHI8Qt | ||||||
|  |           WBqFvY3laad335tpkFJN5b949N+DP8svCWwRwXmkZlHplPYZWF6QpYbEEXL/6Q0H6VwL+ | ||||||
|  |           om4f7ybYpKe9Gl939uv2INnXaKe5EU6CMsSw40r2XZCjnSTjWOTgh9pUn2PsoHnqUlALW | ||||||
|  |           VR4Z+ipnCrEbu8aTmX3ROcnwYNS7OXkq4uhwDU1u9QjzyMHet6NQQhwhGtimsTo9KhL4E | ||||||
|  |           TEUNiRlbAgow9WOwM5r3vRzddO8T2HZZSGaWj75qNRX46XPQWRWgB7ItAwyXgwLZ8UzWl | ||||||
|  |           HdztjS3D7Hlsqno3zxNOVlhA5/vl9uVnhFbJnMtUOJAB07YoTJOeR+LjQ0avx/VzopxXc | ||||||
|  |           RA/WvJXVZSBrlAHY0+ip4wPZvdi4Ph90gpmvHJvoH82KVfp2j5jxzUhsage94I= | ||||||
							
								
								
									
										29
									
								
								AUTHORS.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								AUTHORS.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | |||||||
|  | gnpy is written and maintained by the Telecom Infra Project with work | ||||||
|  | contributed by the following TIP members. | ||||||
|  |  | ||||||
|  | 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> | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| Contributors in alphabetical order |  | ||||||
| ================================== |  | ||||||
|  |  | ||||||
| Name | Surname | Affiliation | Contact |  | ||||||
| -----|---------|-------------|-------- |  | ||||||
| Alessio | Ferrari | Politecnico di Torino | alessio.ferrari@polito.it |  | ||||||
| Brian | Taylor | Facebook | briantaylor@fb.com |  | ||||||
| David | Boertjes | Ciena | dboertje@ciena.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 | Consultant | james@dontusethiscode.com |  | ||||||
| Jeanluc | Auge | Orange | jeanluc.auge@orange.com |  | ||||||
| Liu | Xufeng | Jabil | Xufeng_Liu@jabil.com |  | ||||||
| Mattia | Cantono | Politecnico di Torino | mattia.cantono@polito.it |  | ||||||
| Vittorio | Curri | Politecnico di Torino | vittorio.curri@polito.it |  | ||||||
							
								
								
									
										18
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | |||||||
|  | FROM python:3.7-slim | ||||||
|  | WORKDIR /opt/application/oopt-gnpy | ||||||
|  | RUN mkdir -p /shared/example-data \ | ||||||
|  |     && groupadd gnpy \ | ||||||
|  |     && useradd -u 1000 -g gnpy -m gnpy \ | ||||||
|  |     && apt-get update \ | ||||||
|  |     && apt-get install git -y \ | ||||||
|  |     && rm -rf /var/lib/apt/lists/* | ||||||
|  | COPY . /opt/application/oopt-gnpy | ||||||
|  | WORKDIR /opt/application/oopt-gnpy | ||||||
|  | RUN mkdir topology \ | ||||||
|  |     && mkdir equipment \ | ||||||
|  |     && mkdir autodesign \ | ||||||
|  |     && pip install . \ | ||||||
|  |     && chown -Rc gnpy:gnpy /opt/application/oopt-gnpy /shared/example-data | ||||||
|  | USER gnpy | ||||||
|  | ENTRYPOINT ["/opt/application/oopt-gnpy/.docker-entry.sh"] | ||||||
|  | CMD ["/bin/bash"] | ||||||
							
								
								
									
										215
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										215
									
								
								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 | `gnpy`: mesh optical network route planning and optimization library | ||||||
| ==== | ==================================================================== | ||||||
|  |  | ||||||
| |docs| |build| | |pypi| |docs| |travis| |doi| |contributors| |codacy-quality| |codecov| | ||||||
|  |  | ||||||
| **gnpy is an open-source, community-developed library for building route planning | **`gnpy` is an open-source, community-developed library for building route | ||||||
| and optimization tools in real-world mesh optical networks.** | planning and optimization tools in real-world mesh optical networks.** | ||||||
|  |  | ||||||
| `gnpy <http://github.com/telecominfraproject/gnpy>`_ is: | `gnpy <http://github.com/telecominfraproject/oopt-gnpy>`__ is: | ||||||
|  | -------------------------------------------------------------- | ||||||
|  |  | ||||||
| - a sponsored project of the `OOPT/PSE <http://telecominfraproject.com/project-groups-2/backhaul-projects/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 | - fully community-driven, fully open source library | ||||||
| - driven by a consortium of operators, vendors, and academic researchers | - driven by a consortium of operators, vendors, and academic researchers | ||||||
| - intended for rapid development of production-grade route planning tools | - intended for rapid development of production-grade route planning tools | ||||||
| @@ -18,63 +24,60 @@ and optimization tools in real-world mesh optical networks.** | |||||||
|  |  | ||||||
| Documentation: https://gnpy.readthedocs.io | Documentation: https://gnpy.readthedocs.io | ||||||
|  |  | ||||||
| Installation | Get In Touch | ||||||
| ------------ | ~~~~~~~~~~~~ | ||||||
|  |  | ||||||
| ``gnpy`` is hosted in the `Python Package Index <http://pypi.org/>`_ (`gnpy <https://pypi.org/project/gnpy/>`_). It can be installed via: | 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/>`__. | ||||||
|  |  | ||||||
| .. code-block:: shell | How to Install | ||||||
|  | -------------- | ||||||
|  |  | ||||||
|     $ pip install gnpy | Install either via `Docker <docs/install.rst#install-docker>`__, or as a `Python package <docs/install.rst#install-pip>`__. | ||||||
|  |  | ||||||
| It can also be installed directly from the repo. | Instructions for First Use | ||||||
|  | -------------------------- | ||||||
| .. code-block:: shell |  | ||||||
|  |  | ||||||
|     $ git clone https://github.com/telecominfraproject/gnpy |  | ||||||
|     $ cd gnpy |  | ||||||
|     $ python setup.py install |  | ||||||
|  |  | ||||||
| Both approaches above will handle installing any additional software dependencies. |  | ||||||
|  |  | ||||||
|     **Note**: *We recommend the use of the Anaconda Python distribution |  | ||||||
|     (https://www.anaconda.com/download) which comes with many scientific |  | ||||||
|     computing dependencies pre-installed.* |  | ||||||
|  |  | ||||||
| Instructions for Use |  | ||||||
| -------------------- |  | ||||||
|  |  | ||||||
| ``gnpy`` is a library for building route planning and optimization tools. | ``gnpy`` is a library for building route planning and optimization tools. | ||||||
|  |  | ||||||
| It ships with a number of example programs. Release versions will ship with | It ships with a number of example programs. Release versions will ship with | ||||||
| fully-functional programs. | fully-functional programs. | ||||||
|  |  | ||||||
|  |  | ||||||
|     **Note**: *If you are a network operator or involved in route planning and |     **Note**: *If you are a network operator or involved in route planning and | ||||||
|     optimization for your organization, please contact project maintainer James |     optimization for your organization, please contact project maintainer Jan | ||||||
|     Powell <james.powell@telecominfraproject>. gnpy is looking for users with |     Kundrát <jan.kundrat@telecominfraproject.com>. gnpy is looking for users with | ||||||
|     specific, delineated use cases to drive requirements for future |     specific, delineated use cases to drive requirements for future | ||||||
|     development.* |     development.* | ||||||
|  |  | ||||||
|  | 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: | ||||||
|  |  | ||||||
| **To get started, run the transmission example:** | .. 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 | ||||||
|  |  | ||||||
| .. code-block:: shell | By default, this script operates on a single span network defined in | ||||||
|  | `gnpy/example-data/edfa_example_network.json <gnpy/example-data/edfa_example_network.json>`_ | ||||||
|     $ python examples/transmission_main_example.py |  | ||||||
|  |  | ||||||
| By default, this script operates on a single span network defined in `examples/edfa/edfa_example_network.json <examples/edfa/edfa_example_network.json>`_ |  | ||||||
|  |  | ||||||
| You can specify a different network at the command line as follows. For | 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 | ||||||
|  | `gnpy/example-data/CORONET_Global_Topology.json <gnpy/example-data/CORONET_Global_Topology.json>`_: | ||||||
|  |  | ||||||
| .. code-block:: shell | .. code-block:: shell-session | ||||||
|  |  | ||||||
|     $ python examples/transmission_main_example.py examples/coronet_conus_example.json |     $ gnpy-transmission-example $(gnpy-example-data)/CORONET_Global_Topology.json | ||||||
|  |  | ||||||
| This script will calculate the average signal osnr and snr across 93 network | It is also possible to use an Excel file input (for example | ||||||
| elements (transceiver, ROADMs, fibers, and amplifiers) between Abilene, Texas | `gnpy/example-data/CORONET_Global_Topology.xls <gnpy/example-data/CORONET_Global_Topology.xls>`_). | ||||||
| and Albany, New York. | The Excel file will be processed into a JSON file with the same prefix. | ||||||
|  | Further details about the Excel data structure are available `in the documentation <docs/excel.rst>`__. | ||||||
|  |  | ||||||
|  | The main transmission example will calculate the average signal OSNR and SNR | ||||||
|  | across network elements (transceiver, ROADMs, fibers, and amplifiers) | ||||||
|  | between two transceivers selected by the user. Additional details are provided by doing ``gnpy-transmission-example -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|. | This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|. | ||||||
|  |  | ||||||
| @@ -87,16 +90,74 @@ interference noise. | |||||||
| .. |Pase| replace:: P\ :sub:`ase` | .. |Pase| replace:: P\ :sub:`ase` | ||||||
| .. |Pnli| replace:: P\ :sub:`nli` | .. |Pnli| replace:: P\ :sub:`nli` | ||||||
|  |  | ||||||
| The `transmission_main_example.py <examples/transmission_main_example.py>`_ | Further Instructions for Use | ||||||
| script propagates a specrum of 96 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 tuple) to accomodate any baud rate, |  | ||||||
| spacing, power or channel count demand. |  | ||||||
|  |  | ||||||
| The amplifier's gain is set to exactly compsenate for the loss in each network | Simulations are driven by a set of `JSON <docs/json.rst>`__ or `XLS <docs/excel.rst>`__ files. | ||||||
| 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 | The ``gnpy-transmission-example`` script propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing and 0 dBm/channel.  | ||||||
| `examples/edfa_config.json <examples/edfa_config.json>`_ | 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. | ||||||
|  |  | ||||||
|  | An experimental support for Raman amplification is available: | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |      $ gnpy-transmission-example \ | ||||||
|  |        $(gnpy-example-data)/raman_edfa_example_network.json \ | ||||||
|  |        --sim $(gnpy-example-data)/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 <gnpy/example-data/raman_edfa_example_network.json>`_. | ||||||
|  | General numeric parameters for simulaiton control are provided in the `gnpy/example-data/sim_params.json <gnpy/example-data/sim_params.json>`_. | ||||||
|  |  | ||||||
|  | Use ``gnpy-path-request`` to request several paths at once: | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |      $ cd $(gnpy-example-data) | ||||||
|  |      $ gnpy-path-request -o output_file.json \ | ||||||
|  |        meshTopologyExampleV2.xls meshTopologyExampleV2_services.json | ||||||
|  |  | ||||||
|  | This program operates on a network topology (`JSON <docs/json.rst>`__ or `Excel <docs/excel.rst>`__ format), processing the list of service requests (JSON or XLS again). | ||||||
|  | The service requests and reply formats are based on the `draft-ietf-teas-yang-path-computation-01 <https://tools.ietf.org/html/draft-ietf-teas-yang-path-computation-01>`__ with custom extensions (e.g., for transponder modes). | ||||||
|  | An example of the JSON input is provided in file `service-template.json`, while results are shown in `path_result_template.json`. | ||||||
|  |  | ||||||
|  | Important note: ``gnpy-path-request`` is not a network dimensionning tool: each service does not reserve spectrum, or occupy ressources such as transponders. It only computes path feasibility assuming the spectrum (between defined frequencies) is loaded with "nb of channels" spaced by "spacing" values as specified in the system parameters input in the service file, each cannel having the same characteristics in terms of baudrate, format,... as the service transponder. The transceiver element acts as a "logical starting/stopping point" for the spectral information propagation. At that point it is not meant to represent the capacity of add drop ports. | ||||||
|  | As a result transponder type is not part of the network info. it is related to the list of services requests. | ||||||
|  |  | ||||||
|  | The current version includes a spectrum assigment features that enables to compute a candidate spectrum assignment for each service based on a first fit policy. Spectrum is assigned based on service specified spacing value, path_bandwidth value and selected mode for the transceiver. This spectrum assignment includes a basic capacity planning capability so that the spectrum resource is limited by the frequency min and max values defined for the links. If the requested services reach the link spectrum capacity, additional services feasibility are computed but marked as blocked due to spectrum reason. | ||||||
|  |  | ||||||
|  | REST API (experimental) | ||||||
|  | ----------------------- | ||||||
|  | ``gnpy`` provides an experimental api for requesting several paths at once. It is based on Flask server. | ||||||
|  | You can run it through command line or Docker. | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |      $ gnpy-rest | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |     $ docker run -p 8080:8080 -it emmanuelledelfour/gnpy-experimental:candi-1.1 gnpy-rest | ||||||
|  |  | ||||||
|  | When starting the api server will aks for an encryption/decryption key. This key i used to encrypt equipment file when using /api/v1/equipments endpoint. | ||||||
|  | This key is a Fernet key and can be generated this way: | ||||||
|  |  | ||||||
|  | .. code-block:: python | ||||||
|  |  | ||||||
|  | from cryptography.fernet import Fernet | ||||||
|  | Fernet.generate_key() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | After typing the key, you can detach the container by typing ^P^Q. | ||||||
|  | After starting the api server, you can launch a request | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |     $ curl -v -X POST -H "Content-Type: application/json" -d @<PATH_TO_JSON_REQUEST_FILE> https://localhost:8080/api/v1/path-computation -k | ||||||
|  |  | ||||||
|  | TODO: api documentation, unit tests, real WSGI server with trusted certificates | ||||||
|  |  | ||||||
| Contributing | Contributing | ||||||
| ------------ | ------------ | ||||||
| @@ -104,17 +165,18 @@ Contributing | |||||||
| ``gnpy`` is looking for additional contributors, especially those with experience | ``gnpy`` is looking for additional contributors, especially those with experience | ||||||
| planning and maintaining large-scale, real-world mesh optical networks. | planning and maintaining large-scale, real-world mesh optical networks. | ||||||
|  |  | ||||||
| To get involved, please contact James Powell | To get involved, please contact Jan Kundrát | ||||||
| <james.powell@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>. | <jan.kundrat@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>. | ||||||
|  |  | ||||||
| ``gnpy`` contributions are currently limited to members of `TIP | ``gnpy`` contributions are currently limited to members of `TIP | ||||||
| <http://telecominfraproject.com>`_. Membership is free and open to all. | <http://telecominfraproject.com>`_. Membership is free and open to all. | ||||||
|  |  | ||||||
| See the `Onboarding Guide | See the `Onboarding Guide | ||||||
| <https://github.com/Telecominfraproject/gnpy/wiki/Onboarding-Guide>`_ for | <https://github.com/Telecominfraproject/gnpy/wiki/Onboarding-Guide>`_ for | ||||||
| specific details on code contribtions. | specific details on code contributions, or just `upload patches to our Gerrit | ||||||
|  | <https://review.gerrithub.io/Documentation/intro-gerrit-walkthrough-github.html>`_. | ||||||
|  |  | ||||||
| See `AUTHORS.Md <AUTHORS.Md>`_ for past and present contributors. | See `AUTHORS.rst <AUTHORS.rst>`_ for past and present contributors. | ||||||
|  |  | ||||||
| Project Background | Project Background | ||||||
| ------------------ | ------------------ | ||||||
| @@ -122,7 +184,7 @@ Project Background | |||||||
| Data Centers are built upon interchangeable, highly standardized node and | Data Centers are built upon interchangeable, highly standardized node and | ||||||
| network architectures rather than a sum of isolated solutions. This also | network architectures rather than a sum of isolated solutions. This also | ||||||
| translates to optical networking. It leads to a push in enabling multi-vendor | 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 | interoperability. In this paradigm, the burden of responsibility for ensuring | ||||||
| the performance of such disaggregated open optical systems falls on the | the performance of such disaggregated open optical systems falls on the | ||||||
| operators. Consequently, operators and vendors are collaborating in defining | operators. Consequently, operators and vendors are collaborating in defining | ||||||
| @@ -153,16 +215,42 @@ working group set out to disrupt the planning landscape by providing an open | |||||||
| source simulation model which can be used freely across multiple vendor | source simulation model which can be used freely across multiple vendor | ||||||
| implementations. | implementations. | ||||||
|  |  | ||||||
| .. |docs| image:: https://readthedocs.org/projects/gnpy/badge/?version=develop | .. |docs| image:: https://readthedocs.org/projects/gnpy/badge/?version=master | ||||||
|   :target: http://gnpy.readthedocs.io/en/develop/?badge=develop |   :target: http://gnpy.readthedocs.io/en/master/?badge=master | ||||||
|   :alt: Documentation Status |   :alt: Documentation Status | ||||||
|   :scale: 100% |   :scale: 100% | ||||||
|  |  | ||||||
| .. |build| image:: https://travis-ci.org/mcantono/gnpy.svg?branch=develop | .. |travis| image:: https://travis-ci.com/Telecominfraproject/oopt-gnpy.svg?branch=master | ||||||
|   :target: https://travis-ci.org/mcantono/gnpy |   :target: https://travis-ci.com/Telecominfraproject/oopt-gnpy | ||||||
|   :alt: Build Status |   :alt: Build Status via Travis CI | ||||||
|   :scale: 100% |   :scale: 100% | ||||||
|  |  | ||||||
|  | .. |doi| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3458319.svg | ||||||
|  |   :target: https://doi.org/10.5281/zenodo.3458319 | ||||||
|  |   :alt: DOI | ||||||
|  |   :scale: 100% | ||||||
|  |  | ||||||
|  | .. |contributors| image:: https://img.shields.io/github/contributors-anon/Telecominfraproject/oopt-gnpy | ||||||
|  |   :target: https://github.com/Telecominfraproject/oopt-gnpy/graphs/contributors | ||||||
|  |   :alt: Code Contributors via GitHub | ||||||
|  |   :scale: 100% | ||||||
|  |  | ||||||
|  | .. |codacy-quality| image:: https://img.shields.io/lgtm/grade/python/github/Telecominfraproject/oopt-gnpy | ||||||
|  |   :target: https://lgtm.com/projects/g/Telecominfraproject/oopt-gnpy/ | ||||||
|  |   :alt: Code Quality via LGTM.com | ||||||
|  |   :scale: 100% | ||||||
|  |  | ||||||
|  | .. |codecov| image:: https://img.shields.io/codecov/c/github/Telecominfraproject/oopt-gnpy | ||||||
|  |   :target: https://codecov.io/gh/Telecominfraproject/oopt-gnpy | ||||||
|  |   :alt: Code Coverage via codecov | ||||||
|  |   :scale: 100% | ||||||
|  |  | ||||||
|  | .. |pypi| image:: https://img.shields.io/pypi/v/gnpy | ||||||
|  |   :target: https://pypi.org/project/gnpy/ | ||||||
|  |   :alt: Install via PyPI | ||||||
|  |   :scale: 100% | ||||||
|  |  | ||||||
|  |  | ||||||
| TIP OOPT/PSE & PSE WG Charter | TIP OOPT/PSE & PSE WG Charter | ||||||
| ----------------------------- | ----------------------------- | ||||||
|  |  | ||||||
| @@ -187,5 +275,4 @@ License | |||||||
|  |  | ||||||
| ``gnpy`` is distributed under a standard BSD 3-Clause License. | ``gnpy`` is distributed under a standard BSD 3-Clause License. | ||||||
|  |  | ||||||
| See `LICENSE <LICENSE>`_ for more details. | See `LICENSE <LICENSE>`__ for more details. | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								bindep.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								bindep.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | graphviz | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| Contributors in alphabetical order |  | ||||||
| ================================== |  | ||||||
|  |  | ||||||
| Name | Surname | Affiliation | Contact |  | ||||||
| -----|---------|-------------|-------- |  | ||||||
| Alessio | Ferrari | Politecnico di Torino | alessio.ferrari@polito.it |  | ||||||
| Brian | Taylor | Facebook | briantaylor@fb.com |  | ||||||
| David | Boertjes | Ciena | dboertje@ciena.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 | Consultant | james@dontusethiscode.com |  | ||||||
| Jeanluc | Auge | Orange | jeanluc.auge@orange.com |  | ||||||
| Liu | Xufeng | Jabil | Xufeng_Liu@jabil.com |  | ||||||
| Mattia | Cantono | Politecnico di Torino | mattia.cantono@polito.it |  | ||||||
| Vittorio | Curri | Politecnico di Torino | vittorio.curri@polito.it |  | ||||||
| @@ -874,7 +874,7 @@ month={Sept},} | |||||||
|   number = {7}, |   number = {7}, | ||||||
|   journal = {Optics Express}, |   journal = {Optics Express}, | ||||||
|   urlyear = {2017-11-14}, |   urlyear = {2017-11-14}, | ||||||
|   year = {2012-03-26}, |   date = {2012-03-26}, | ||||||
|   year = {2012}, |   year = {2012}, | ||||||
|   pages = {7777}, |   pages = {7777}, | ||||||
|   author = {Bononi, A. and Serena, P. and Rossi, N. and Grellier, E. and Vacondio, F.} |   author = {Bononi, A. and Serena, P. and Rossi, N. and Grellier, E. and Vacondio, F.} | ||||||
| @@ -1114,7 +1114,7 @@ month={Sept},} | |||||||
|   number = {26}, |   number = {26}, | ||||||
|   journal = {Optics Express}, |   journal = {Optics Express}, | ||||||
|   urlyear = {2017-11-16}, |   urlyear = {2017-11-16}, | ||||||
|   year = {2013-12-30}, |   date = {2013-12-30}, | ||||||
|   year = {2013}, |   year = {2013}, | ||||||
|   pages = {32254}, |   pages = {32254}, | ||||||
|   author = {Bononi, Alberto and Beucher, Ottmar and Serena, Paolo} |   author = {Bononi, Alberto and Beucher, Ottmar and Serena, Paolo} | ||||||
|   | |||||||
							
								
								
									
										269
									
								
								docs/concepts.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								docs/concepts.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,269 @@ | |||||||
|  | .. _concepts: | ||||||
|  |  | ||||||
|  | Simulating networks with GNPy | ||||||
|  | ============================= | ||||||
|  |  | ||||||
|  | Running simulations with GNPy requires three pieces of information: | ||||||
|  |  | ||||||
|  | - the :ref:`network topology<concepts-topology>`, which describes how the network looks like, what are the fiber lengths, what amplifiers are used, etc., | ||||||
|  | - the :ref:`equipment library<concepts-equipment>`, which holds machine-readable datasheets of the equipment used in the network, | ||||||
|  | - the :ref:`simulation options<concepts-simulation>` holding instructions about what to simulate, and under which conditions. | ||||||
|  |  | ||||||
|  | .. _concepts-topology: | ||||||
|  |  | ||||||
|  | Network Topology | ||||||
|  | ---------------- | ||||||
|  |  | ||||||
|  | The *topology* acts as a "digital self" of the simulated network. | ||||||
|  | When given a network topology, GNPy can either run a specific simulation as-is, or it can *optimize* the topology before performing the simulation. | ||||||
|  |  | ||||||
|  | A network topology for GNPy is often a generic, mesh network. | ||||||
|  | This enables GNPy to take into consideration the current spectrum allocation as well as availability and resiliency considerations. | ||||||
|  | When the time comes to run a particular *propagation* of a signal and its impairments are computed, though, a linear path through the network is used. | ||||||
|  | For this purpose, the *path* through the network refers to an ordered, acyclic sequence of *nodes* that are processed. | ||||||
|  | This path is directional, and all "GNPy elements" along the path match the unidirectional part of a real-world network equipment. | ||||||
|  |  | ||||||
|  | .. note:: | ||||||
|  |   In practical terms, an amplifier in GNPy refers to an entity with a single input port and a single output port. | ||||||
|  |   A real-world inline EDFA enclosed in a single chassis will be therefore represented as two GNPy-level amplifiers. | ||||||
|  |  | ||||||
|  | The network topology contains not just the physical topology of the network, but also references to the :ref:`equipment library<concepts-equipment>` and a set of *operating parameters* for each entity. | ||||||
|  | These parameters include the **fiber length** of each fiber, the connector **attenutation losses**, or an amplifier's specific **gain setting**. | ||||||
|  |  | ||||||
|  | .. _complete-vs-incomplete: | ||||||
|  |  | ||||||
|  | Fully Specified vs. Partially Designed Networks | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | Let's consider a simple triangle topology with three :abbr:`PoPs (Points of Presence)` covering three cities: | ||||||
|  |  | ||||||
|  | .. graphviz:: | ||||||
|  |   :layout: neato | ||||||
|  |   :align: center | ||||||
|  |  | ||||||
|  |   graph "High-level topology with three PoPs" { | ||||||
|  |     A -- B | ||||||
|  |     B -- C | ||||||
|  |     C -- A | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | In the real world, each city would probably host a ROADM and some transponders: | ||||||
|  |  | ||||||
|  | .. graphviz:: | ||||||
|  |   :layout: neato | ||||||
|  |   :align: center | ||||||
|  |  | ||||||
|  |   graph "Simplified topology with transponders" { | ||||||
|  |     "ROADM A" [pos="2,2!"] | ||||||
|  |     "ROADM B" [pos="4,2!"] | ||||||
|  |     "ROADM C" [pos="3,1!"] | ||||||
|  |     "Transponder A" [shape=box, pos="0,2!"] | ||||||
|  |     "Transponder B" [shape=box, pos="6,2!"] | ||||||
|  |     "Transponder C" [shape=box, pos="3,0!"] | ||||||
|  |  | ||||||
|  |     "ROADM A" -- "ROADM B" | ||||||
|  |     "ROADM B" -- "ROADM C" | ||||||
|  |     "ROADM C" -- "ROADM A" | ||||||
|  |  | ||||||
|  |     "Transponder A" -- "ROADM A" | ||||||
|  |     "Transponder B" -- "ROADM B" | ||||||
|  |     "Transponder C" -- "ROADM C" | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | GNPy simulation works by propagating the optical signal over a sequence of elements, which means that one has to add some preamplifiers and boosters. | ||||||
|  | The amplifiers are, by definition, unidirectional, so the graph becomes quite complex: | ||||||
|  |  | ||||||
|  | .. _topo-roadm-preamp-booster: | ||||||
|  |  | ||||||
|  | .. graphviz:: | ||||||
|  |   :layout: neato | ||||||
|  |   :align: center | ||||||
|  |  | ||||||
|  |   digraph "Preamps and boosters are explicitly modeled in GNPy" { | ||||||
|  |     "ROADM A" [pos="2,4!"] | ||||||
|  |     "ROADM B" [pos="6,4!"] | ||||||
|  |     "ROADM C" [pos="4,0!"] | ||||||
|  |     "Transponder A" [shape=box, pos="1,5!"] | ||||||
|  |     "Transponder B" [shape=box, pos="7,5!"] | ||||||
|  |     "Transponder C" [shape=box, pos="4,-1!"] | ||||||
|  |  | ||||||
|  |     "Transponder A" -> "ROADM A" | ||||||
|  |     "Transponder B" -> "ROADM B" | ||||||
|  |     "Transponder C" -> "ROADM C" | ||||||
|  |     "ROADM A" -> "Transponder A" | ||||||
|  |     "ROADM B" -> "Transponder B" | ||||||
|  |     "ROADM C" -> "Transponder C" | ||||||
|  |  | ||||||
|  |     "Booster A C" [shape=triangle, orientation=-150, fixedsize=true, width=0.5, height=0.5, pos="2.2,3.2!", color=red, label=""] | ||||||
|  |     "Preamp A C" [shape=triangle, orientation=0, fixedsize=true, width=0.5, height=0.5, pos="1.5,3.0!", color=red, label=""] | ||||||
|  |     "ROADM A" -> "Booster A C" | ||||||
|  |     "Preamp A C" -> "ROADM A" | ||||||
|  |  | ||||||
|  |     "Booster A B" [shape=triangle, orientation=-90, fixedsize=true, width=0.5, height=0.5, pos="3,4.3!", color=red, fontcolor=red, labelloc=b, label="\N\n\n"] | ||||||
|  |     "Preamp A B" [shape=triangle, orientation=90, fixedsize=true, width=0.5, height=0.5, pos="3,3.6!", color=red, fontcolor=red, labelloc=t, label="\n        \N"] | ||||||
|  |     "ROADM A" -> "Booster A B" | ||||||
|  |     "Preamp A B" -> "ROADM A" | ||||||
|  |  | ||||||
|  |     "Booster C B" [shape=triangle, orientation=-30, fixedsize=true, width=0.5, height=0.5, pos="4.7,0.9!", color=red, label=""] | ||||||
|  |     "Preamp C B" [shape=triangle, orientation=120, fixedsize=true, width=0.5, height=0.5, pos="5.4,0.7!", color=red, label=""] | ||||||
|  |     "ROADM C" -> "Booster C B" | ||||||
|  |     "Preamp C B" -> "ROADM C" | ||||||
|  |  | ||||||
|  |     "Booster C A" [shape=triangle, orientation=30, fixedsize=true, width=0.5, height=0.5, pos="2.6,0.7!", color=red, label=""] | ||||||
|  |     "Preamp C A" [shape=triangle, orientation=-30, fixedsize=true, width=0.5, height=0.5, pos="3.3,0.9!", color=red, label=""] | ||||||
|  |     "ROADM C" -> "Booster C A" | ||||||
|  |     "Preamp C A" -> "ROADM C" | ||||||
|  |  | ||||||
|  |     "Booster B A" [shape=triangle, orientation=90, fixedsize=true, width=0.5, height=0.5, pos="5,3.6!", labelloc=t, color=red, fontcolor=red, label="\n\N        "] | ||||||
|  |     "Preamp B A" [shape=triangle, orientation=-90, fixedsize=true, width=0.5, height=0.5, pos="5,4.3!", labelloc=b, color=red, fontcolor=red, label="\N\n\n"] | ||||||
|  |     "ROADM B" -> "Booster B A" | ||||||
|  |     "Preamp B A" -> "ROADM B" | ||||||
|  |  | ||||||
|  |     "Booster B C" [shape=triangle, orientation=-180, fixedsize=true, width=0.5, height=0.5, pos="6.5,3.0!", color=red, label=""] | ||||||
|  |     "Preamp B C" [shape=triangle, orientation=-20, fixedsize=true, width=0.5, height=0.5, pos="5.8,3.2!", color=red, label=""] | ||||||
|  |     "ROADM B" -> "Booster B C" | ||||||
|  |     "Preamp B C" -> "ROADM B" | ||||||
|  |  | ||||||
|  |     "Booster A C" -> "Preamp C A" | ||||||
|  |     "Booster A B" -> "Preamp B A" | ||||||
|  |     "Booster C A" -> "Preamp A C" | ||||||
|  |     "Booster C B" -> "Preamp B C" | ||||||
|  |     "Booster B C" -> "Preamp C B" | ||||||
|  |     "Booster B A" -> "Preamp A B" | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | In many regions, the ROADMs are not placed physically close to each other, so the long-haul fiber links (:abbr:`OMS (Optical Multiplex Section)`) are split into individual spans (:abbr:`OTS (Optical Transport Section)`) by in-line amplifiers, resulting in an even more complicated topology graphs: | ||||||
|  |  | ||||||
|  | .. graphviz:: | ||||||
|  |   :layout: neato | ||||||
|  |   :align: center | ||||||
|  |  | ||||||
|  |   digraph "A subset of a real topology with inline amplifiers" { | ||||||
|  |     "ROADM A" [pos="2,4!"] | ||||||
|  |     "ROADM B" [pos="6,4!"] | ||||||
|  |     "ROADM C" [pos="4,-3!"] | ||||||
|  |     "Transponder A" [shape=box, pos="1,5!"] | ||||||
|  |     "Transponder B" [shape=box, pos="7,5!"] | ||||||
|  |     "Transponder C" [shape=box, pos="4,-4!"] | ||||||
|  |  | ||||||
|  |     "Transponder A" -> "ROADM A" | ||||||
|  |     "Transponder B" -> "ROADM B" | ||||||
|  |     "Transponder C" -> "ROADM C" | ||||||
|  |     "ROADM A" -> "Transponder A" | ||||||
|  |     "ROADM B" -> "Transponder B" | ||||||
|  |     "ROADM C" -> "Transponder C" | ||||||
|  |  | ||||||
|  |     "Booster A C" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, height=0.5, pos="2.2,3.2!", label=""] | ||||||
|  |     "Preamp A C" [shape=triangle, orientation=0, fixedsize=true, width=0.5, height=0.5, pos="1.5,3.0!", label=""] | ||||||
|  |     "ROADM A" -> "Booster A C" | ||||||
|  |     "Preamp A C" -> "ROADM A" | ||||||
|  |  | ||||||
|  |     "Booster A B" [shape=triangle, orientation=-90, fixedsize=true, width=0.5, height=0.5, pos="3,4.3!", label=""] | ||||||
|  |     "Preamp A B" [shape=triangle, orientation=90, fixedsize=true, width=0.5, height=0.5, pos="3,3.6!", label=""] | ||||||
|  |     "ROADM A" -> "Booster A B" | ||||||
|  |     "Preamp A B" -> "ROADM A" | ||||||
|  |  | ||||||
|  |     "Booster C B" [shape=triangle, orientation=-30, fixedsize=true, width=0.5, height=0.5, pos="4.7,-2.1!", label=""] | ||||||
|  |     "Preamp C B" [shape=triangle, orientation=10, fixedsize=true, width=0.5, height=0.5, pos="5.4,-2.3!", label=""] | ||||||
|  |     "ROADM C" -> "Booster C B" | ||||||
|  |     "Preamp C B" -> "ROADM C" | ||||||
|  |  | ||||||
|  |     "Booster C A" [shape=triangle, orientation=20, fixedsize=true, width=0.5, height=0.5, pos="2.6,-2.3!", label=""] | ||||||
|  |     "Preamp C A" [shape=triangle, orientation=-30, fixedsize=true, width=0.5, height=0.5, pos="3.3,-2.1!", label=""] | ||||||
|  |     "ROADM C" -> "Booster C A" | ||||||
|  |     "Preamp C A" -> "ROADM C" | ||||||
|  |  | ||||||
|  |     "Booster B A" [shape=triangle, orientation=90, fixedsize=true, width=0.5, height=0.5, pos="5,3.6!", label=""] | ||||||
|  |     "Preamp B A" [shape=triangle, orientation=-90, fixedsize=true, width=0.5, height=0.5, pos="5,4.3!", label=""] | ||||||
|  |     "ROADM B" -> "Booster B A" | ||||||
|  |     "Preamp B A" -> "ROADM B" | ||||||
|  |  | ||||||
|  |     "Booster B C" [shape=triangle, orientation=-180, fixedsize=true, width=0.5, height=0.5, pos="6.5,3.0!", label=""] | ||||||
|  |     "Preamp B C" [shape=triangle, orientation=-20, fixedsize=true, width=0.5, height=0.5, pos="5.8,3.2!", label=""] | ||||||
|  |     "ROADM B" -> "Booster B C" | ||||||
|  |     "Preamp B C" -> "ROADM B" | ||||||
|  |  | ||||||
|  |     "Inline A C 1" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, pos="2.4,2.2!", label="                             \N", color=red, fontcolor=red] | ||||||
|  |     "Inline A C 2" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, pos="2.6,1.2!", label="                             \N", color=red, fontcolor=red] | ||||||
|  |     "Inline A C 3" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, pos="2.8,0.2!", label="                             \N", color=red, fontcolor=red] | ||||||
|  |     "Inline A C n" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, pos="3.0,-1.1!", label="                             \N", color=red, fontcolor=red] | ||||||
|  |  | ||||||
|  |     "Booster A C" -> "Inline A C 1" | ||||||
|  |     "Inline A C 1" -> "Inline A C 2" | ||||||
|  |     "Inline A C 2" -> "Inline A C 3" | ||||||
|  |     "Inline A C 3" -> "Inline A C n" [style=dotted] | ||||||
|  |     "Inline A C n" -> "Preamp C A" | ||||||
|  |     "Booster A B" -> "Preamp B A" [style=dotted] | ||||||
|  |     "Booster C A" -> "Preamp A C" [style=dotted] | ||||||
|  |     "Booster C B" -> "Preamp B C" [style=dotted] | ||||||
|  |     "Booster B C" -> "Preamp C B" [style=dotted] | ||||||
|  |     "Booster B A" -> "Preamp A B" [style=dotted] | ||||||
|  |   } | ||||||
|  |  | ||||||
|  | In such networks, GNPy's autodesign features becomes very useful. | ||||||
|  | It is possible to connect ROADMs via "tentative links" which will be replaced by a sequence of actual fibers and specific amplifiers. | ||||||
|  | In other cases where the location of amplifier huts is already known, but the specific EDFA models have not yet been decided, one can put in amplifier placeholders and let GNPy assign the best amplifier. | ||||||
|  |  | ||||||
|  | .. _concepts-equipment: | ||||||
|  |  | ||||||
|  | The Equipment Library | ||||||
|  | --------------------- | ||||||
|  |  | ||||||
|  | In order to produce an accurate simulation, GNPy needs to know the physical properties of each entity which affects the optical signal. | ||||||
|  | Entries in the equipment library correspond to actual real-world, tangible entities. | ||||||
|  | Unlike a typical :abbr:`NMS (Network Management System)`, GNPy considers not just the active :abbr:`NEs (Network Elements)` such as amplifiers and :abbr:`ROADMs (Reconfigurable Optical Add/Drop Multiplexers)`, but also the passive ones, such as the optical fiber. | ||||||
|  |  | ||||||
|  | As the signal propagates through the network, the largest source of optical impairments is the noise introduced from amplifiers. | ||||||
|  | An accurate description of the :abbr:`EDFA (Erbium-Doped Fiber Amplifier)` and especially its noise characteristics is required. | ||||||
|  | GNPy describes this property in terms of the **Noise Figure (NF)** of an amplifier model as a function of its operating point. | ||||||
|  |  | ||||||
|  | The amplifiers compensate power losses induced on the signal in the optical fiber. | ||||||
|  | The linear losses, however, are just one phenomenon of a multitude of effects that affect the signals in a long fiber run. | ||||||
|  | While a more detailed description is available :ref:`in the literature<physical-model>`, for the purpose of the equipment library, the description of the *optical fiber* comprises its **linear attenutation coefficient**, a set of parameters for the **Raman effect**, optical **dispersion**, etc. | ||||||
|  |  | ||||||
|  | Signals are introduced into the network via *transponders*. | ||||||
|  | The set of parameters that are required describe the physical properties of each supported *mode* of the transponder, including its **symbol rate**, spectral **width**, etc. | ||||||
|  |  | ||||||
|  | In the junctions of the network, *ROADMs* are used for spectrum routing. | ||||||
|  | GNPy currently does not take into consideration the spectrum filtering penalties of the :abbr:`WSSes (Wavelength Selective Switches)`, but the equipment library nonetheless contains a list of required parameters, such as the attenuation options, so that the network can be properly simulated. | ||||||
|  |  | ||||||
|  | .. _concepts-nf-model: | ||||||
|  |  | ||||||
|  | Amplifier Noise Figure Models | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | One of the key parameters of an amplifier is the method to use for computing the Noise Figure (NF). | ||||||
|  | GNPy supports several different noise models with varying level of accuracy. | ||||||
|  | When in doubt, contact your vendor's technical support and ask them to :ref:`contribute their equipment descriptions<extending-edfa>` to GNPy. | ||||||
|  |  | ||||||
|  | The most accurate noise models describe the resulting NF of an EDFA as a third-degree polynomial. | ||||||
|  | GNPy understands polynomials as a NF-yielding function of the :ref:`gain difference from the optimal gain<ext-nf-model-polynomial-NF>`, or as a function of the input power resulting in an :ref:`incremental OSNR as used in OpenROADM<ext-nf-model-polynomial-OSNR-OpenROADM>`. | ||||||
|  | For scenarios where the vendor has not yet contributed an accurate EDFA NF description to GNPy, it is possible to approximate the characteristics via an operator-focused, min-max NF model. | ||||||
|  |  | ||||||
|  | .. _nf-model-min-max-NF: | ||||||
|  |  | ||||||
|  | Min-max NF | ||||||
|  | ********** | ||||||
|  |  | ||||||
|  | This is an operator-focused model where performance is defined by the *minimal* and *maximal NF*. | ||||||
|  | These are especially suited to model a dual-coil EDFA with a VOA in between. | ||||||
|  | In these amplifiers, the minimal NF is achieved when the EDFA operates at its maximal (and usually optimal, in terms of flatness) gain. | ||||||
|  | The worst (maximal) NF applies  when the EDFA operates at its minimal gain. | ||||||
|  |  | ||||||
|  | This model is suitable for use when the vendor has not provided a more accurate performance description of the EDFA. | ||||||
|  |  | ||||||
|  | Raman Approximation | ||||||
|  | ******************* | ||||||
|  |  | ||||||
|  | While GNPy is fully Raman-aware, under certain scenarios it is useful to be able to run a simulation without an accurate Raman description. | ||||||
|  | For these purposes the :ref:`polynomial NF<ext-nf-model-polynomial-NF>` model with :math:`\text{a} = \text{b} = \text{c} = 0`, and :math:`\text{d} = NF` can be used. | ||||||
|  |  | ||||||
|  | .. _concepts-simulation: | ||||||
|  |  | ||||||
|  | Simulation | ||||||
|  | ---------- | ||||||
|  |  | ||||||
|  | When the network model has been instantiated and the physical properties and operational settings of the actual physical devices are known, GNPy can start simulating how the signal propagate through the optical fiber. | ||||||
|  |  | ||||||
|  | This set of input parameters include options such as the *spectrum allocation*, i.e., the number of channels and their spacing. | ||||||
|  | Various strategies for network optimization can be provided as well. | ||||||
							
								
								
									
										54
									
								
								docs/conf.py
									
									
									
									
									
								
							
							
						
						
									
										54
									
								
								docs/conf.py
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| #!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
| # | # | ||||||
| # GNpy documentation build configuration file, created by | # gnpy documentation build configuration file, created by | ||||||
| # sphinx-quickstart on Mon Dec 18 14:41:01 2017. | # sphinx-quickstart on Mon Dec 18 14:41:01 2017. | ||||||
| # | # | ||||||
| # This file is execfile()d with the current directory set to its | # This file is execfile()d with the current directory set to its | ||||||
| @@ -32,7 +32,11 @@ sys.path.insert(0, os.path.abspath('../')) | |||||||
| # ones. | # ones. | ||||||
| extensions = ['sphinx.ext.autodoc', | extensions = ['sphinx.ext.autodoc', | ||||||
|               'sphinx.ext.mathjax', |               'sphinx.ext.mathjax', | ||||||
|     'sphinx.ext.githubpages','sphinxcontrib.bibtex'] |               'sphinx.ext.githubpages', | ||||||
|  |               'sphinxcontrib.bibtex', | ||||||
|  |               'pbr.sphinxext', | ||||||
|  |               'sphinx.ext.graphviz', | ||||||
|  |               ] | ||||||
|  |  | ||||||
| # Add any paths that contain templates here, relative to this directory. | # Add any paths that contain templates here, relative to this directory. | ||||||
| templates_path = ['_templates'] | templates_path = ['_templates'] | ||||||
| @@ -47,18 +51,9 @@ source_suffix = ['.rst', '.md'] | |||||||
| master_doc = 'index' | master_doc = 'index' | ||||||
|  |  | ||||||
| # General information about the project. | # General information about the project. | ||||||
| project = 'GNpy' | project = 'gnpy' | ||||||
| copyright = '2017, Telecom InfraProject - OOPT PSE Group' | copyright = '2018 - 2021, Telecom Infra Project - OOPT PSE Group' | ||||||
| author = 'Telecom InfraProject - OOPT PSE Group' | author = 'Telecom Infra Project - OOPT PSE Group' | ||||||
|  |  | ||||||
| # The version info for the project you're documenting, acts as replacement for |  | ||||||
| # |version| and |release|, also used in various other places throughout the |  | ||||||
| # built documents. |  | ||||||
| # |  | ||||||
| # The short X.Y version. |  | ||||||
| version = '0.1' |  | ||||||
| # The full version, including alpha/beta/rc tags. |  | ||||||
| release = '0.1' |  | ||||||
|  |  | ||||||
| # The language for content autogenerated by Sphinx. Refer to documentation | # The language for content autogenerated by Sphinx. Refer to documentation | ||||||
| # for a list of supported languages. | # for a list of supported languages. | ||||||
| @@ -87,8 +82,17 @@ todo_include_todos = False | |||||||
| on_rtd = os.environ.get('READTHEDOCS') == 'True' | on_rtd = os.environ.get('READTHEDOCS') == 'True' | ||||||
| if on_rtd: | if on_rtd: | ||||||
|     html_theme = 'default' |     html_theme = 'default' | ||||||
|  |     html_theme_options = { | ||||||
|  |         'logo_only': True, | ||||||
|  |     } | ||||||
| else: | else: | ||||||
|     html_theme = 'alabaster' |     html_theme = 'alabaster' | ||||||
|  |     html_theme_options = { | ||||||
|  |         'logo': 'images/GNPy-logo.png', | ||||||
|  |         'logo_name': False, | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | html_logo = 'images/GNPy-logo.png' | ||||||
|  |  | ||||||
| # Theme options are theme-specific and customize the look and feel of a theme | # Theme options are theme-specific and customize the look and feel of a theme | ||||||
| # further.  For a list of options available for each theme, see the | # further.  For a list of options available for each theme, see the | ||||||
| @@ -99,7 +103,7 @@ else: | |||||||
| # Add any paths that contain custom static files (such as style sheets) here, | # Add any paths that contain custom static files (such as style sheets) here, | ||||||
| # relative to this directory. They are copied after the builtin static files, | # relative to this directory. They are copied after the builtin static files, | ||||||
| # so a file named "default.css" will overwrite the builtin "default.css". | # so a file named "default.css" will overwrite the builtin "default.css". | ||||||
| html_static_path = ['_static'] | html_static_path = [] | ||||||
|  |  | ||||||
| # Custom sidebar templates, must be a dictionary that maps document names | # Custom sidebar templates, must be a dictionary that maps document names | ||||||
| # to template names. | # to template names. | ||||||
| @@ -120,7 +124,7 @@ html_sidebars = { | |||||||
| # -- Options for HTMLHelp output ------------------------------------------ | # -- Options for HTMLHelp output ------------------------------------------ | ||||||
|  |  | ||||||
| # Output file base name for HTML help builder. | # Output file base name for HTML help builder. | ||||||
| htmlhelp_basename = 'GNpydoc' | htmlhelp_basename = 'gnpydoc' | ||||||
|  |  | ||||||
|  |  | ||||||
| # -- Options for LaTeX output --------------------------------------------- | # -- Options for LaTeX output --------------------------------------------- | ||||||
| @@ -147,8 +151,8 @@ latex_elements = { | |||||||
| # (source start file, target name, title, | # (source start file, target name, title, | ||||||
| #  author, documentclass [howto, manual, or own class]). | #  author, documentclass [howto, manual, or own class]). | ||||||
| latex_documents = [ | latex_documents = [ | ||||||
|     (master_doc, 'GNpy.tex', 'GNpy Documentation', |     (master_doc, 'gnpy.tex', 'gnpy Documentation', | ||||||
|      'Telecom InfraProject - OOPT PSE Group', 'manual'), |      'Telecom Infra Project - OOPT PSE Group', 'manual'), | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -157,7 +161,7 @@ latex_documents = [ | |||||||
| # One entry per manual page. List of tuples | # One entry per manual page. List of tuples | ||||||
| # (source start file, name, description, authors, manual section). | # (source start file, name, description, authors, manual section). | ||||||
| man_pages = [ | man_pages = [ | ||||||
|     (master_doc, 'gnpy', 'GNpy Documentation', |     (master_doc, 'gnpy', 'gnpy Documentation', | ||||||
|      [author], 1) |      [author], 1) | ||||||
| ] | ] | ||||||
|  |  | ||||||
| @@ -168,10 +172,16 @@ man_pages = [ | |||||||
| # (source start file, target name, title, author, | # (source start file, target name, title, author, | ||||||
| #  dir menu entry, description, category) | #  dir menu entry, description, category) | ||||||
| texinfo_documents = [ | texinfo_documents = [ | ||||||
|     (master_doc, 'GNpy', 'GNpy Documentation', |     (master_doc, 'gnpy', 'gnpy Documentation', | ||||||
|      author, 'GNpy', 'One line description of project.', |      author, 'gnpy', 'One line description of project.', | ||||||
|      'Miscellaneous'), |      'Miscellaneous'), | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | autodoc_default_options = { | ||||||
|  |     'members': True, | ||||||
|  |     'undoc-members': True, | ||||||
|  |     'private-members': True, | ||||||
|  |     'show-inheritance': True, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | graphviz_output_format = 'svg' | ||||||
|   | |||||||
							
								
								
									
										230
									
								
								docs/excel.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								docs/excel.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,230 @@ | |||||||
|  | Excel (XLS, XLSX) input files | ||||||
|  | ============================= | ||||||
|  |  | ||||||
|  | ``gnpy-transmission-example`` gives the possibility to use an excel input file instead of a json file. The program then will generate the corresponding json file for you. | ||||||
|  |  | ||||||
|  | The file named 'meshTopologyExampleV2.xls' is an example. | ||||||
|  |  | ||||||
|  | In order to work the excel file MUST contain at least 2 sheets: | ||||||
|  |  | ||||||
|  | - Nodes | ||||||
|  | - Links | ||||||
|  |  | ||||||
|  | (In progress) The File MAY contain an additional sheet: | ||||||
|  |  | ||||||
|  | - Eqt | ||||||
|  | - Service | ||||||
|  |  | ||||||
|  | .. _excel-nodes-sheet: | ||||||
|  |  | ||||||
|  | Nodes sheet | ||||||
|  | ----------- | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  | - **City** is used for the name of a node of the graph. It accepts letters, numbers,underscore,dash, blank... (not exhaustive). The user may want to avoid commas for future CSV exports. | ||||||
|  |  | ||||||
|  |    **City name MUST be unique**  | ||||||
|  |  | ||||||
|  | - **Type** is not mandatory.  | ||||||
|  |  | ||||||
|  |   - If not filled, it will be interpreted as an 'ILA' site if node degree is 2 and as a ROADM otherwise. | ||||||
|  |   - If filled, it can take "ROADM", "FUSED" or "ILA" values. If another string is used, it will be considered as not filled. FUSED means that ingress and egress spans will be fused together.   | ||||||
|  |  | ||||||
|  | - *State*, *Country*, *Region* are not mandatory. | ||||||
|  |   "Region" is a holdover from the CORONET topology reference file `CORONET_Global_Topology.xlsx <gnpy/example-data/CORONET_Global_Topology.xlsx>`_. CORONET separates its network into geographical regions (Europe, Asia, Continental US.) This information is not used by gnpy. | ||||||
|  |  | ||||||
|  | - *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** | ||||||
|  |  | ||||||
|  |  | ||||||
|  | .. _excel-links-sheet: | ||||||
|  |  | ||||||
|  | Links sheet | ||||||
|  | ----------- | ||||||
|  |  | ||||||
|  | Links sheet must contain sixteen columns:: | ||||||
|  |  | ||||||
|  |                    <--           east cable from a to z                                   --> <--                  west from z to                                   --> | ||||||
|  |    NodeA ; NodeZ ; 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 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Links sheets MUST contain all links between nodes defined in Nodes sheet. | ||||||
|  | Each line represents a 'bidir link' between two nodes. The two directions are represented on a single line with "east cable from a to z" fields and "west from z to a" fields. Values for 'a to z' may be different from values from 'z to a'.  | ||||||
|  | Since both direction of a bidir 'a-z' link are described on the same line (east and west), 'z to a' direction MUST NOT be repeated in a different line. If repeated, it will generate another parrallel bidir link between the same end nodes. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Parameters for "east cable from a to z" and "west from z to a" are detailed in 2x7 columns. If not filled, "west from z to a" is copied from "east cable from a to z". | ||||||
|  |  | ||||||
|  | For example, a line filled with:: | ||||||
|  |  | ||||||
|  |   node6 ; node3 ; 80 ; SSMF ; 0.2 ; 0.5 ; 0.5 ; 0.1 ; cableB ;  ;  ; 0.21 ; 0.2 ;  ;  ;   | ||||||
|  |  | ||||||
|  | will generate a unidir fiber span from node6 to node3 with:: | ||||||
|  |   | ||||||
|  |   [node6 node3 80 SSMF 0.2 0.5 0.5 0.1 cableB]  | ||||||
|  |  | ||||||
|  | and a fiber span from node3 to node6:: | ||||||
|  |  | ||||||
|  |  [node6 node3 80 SSMF 0.21 0.2 0.5 0.1 cableB] attributes.  | ||||||
|  |  | ||||||
|  | - **NodeA** and **NodeZ** are Mandatory.  | ||||||
|  |   They are the two endpoints of the link. They MUST contain a node name from the **City** names listed in Nodes sheet. | ||||||
|  |  | ||||||
|  | - **Distance km** is not mandatory.  | ||||||
|  |   It is the link length. | ||||||
|  |  | ||||||
|  |   - If filled it MUST contain numbers. If empty it is replaced by a default "80" km value.  | ||||||
|  |   - If value is below 150 km, it is considered as a single (bidirectional) fiber span. | ||||||
|  |   - If value is over 150 km the `gnpy-transmission-example`` program will automatically suppose that intermediate span description are required and will generate fiber spans elements with "_1","_2", ... trailing strings which are not visible in the json output. The reason for the splitting is that current edfa usually do not support large span loss. The current assumption is that links larger than 150km will require intermediate amplification. This value will be revisited when Raman amplification is added” | ||||||
|  |  | ||||||
|  | - **Fiber type** is not mandatory.  | ||||||
|  |  | ||||||
|  |   If filled it must contain types listed in `eqpt_config.json <gnpy/example-data/eqpt_config.json>`_ in "Fiber" list "type_variety". | ||||||
|  |   If not filled it takes "SSMF" as default value. | ||||||
|  |  | ||||||
|  | - **Lineic att** is not mandatory.  | ||||||
|  |  | ||||||
|  |   It is the lineic attenuation expressed in dB/km. | ||||||
|  |   If filled it must contain positive numbers. | ||||||
|  |   If not filled it takes "0.2" dB/km value | ||||||
|  |  | ||||||
|  | - *Con_in*, *Con_out* are not mandatory.  | ||||||
|  |  | ||||||
|  |   They are the connector loss in dB at ingress and egress of the fiber spans. | ||||||
|  |   If filled they must contain positive numbers. | ||||||
|  |   If not filled they take "0.5" dB default value. | ||||||
|  |  | ||||||
|  | - *PMD* is not mandatory and and is not used yet.  | ||||||
|  |  | ||||||
|  |   It is the PMD value of the link in ps. | ||||||
|  |   If filled they must contain positive numbers. | ||||||
|  |   If not filled, it takes "0.1" ps value. | ||||||
|  |  | ||||||
|  | - *Cable Id* is not mandatory.  | ||||||
|  |   If filled they must contain strings with the same constraint as "City" names. Its value is used to differenate links having the same end points. In this case different Id should be used. Cable Ids are not meant to be unique in general. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | (in progress) | ||||||
|  |  | ||||||
|  | .. _excel-equipment-sheet: | ||||||
|  |  | ||||||
|  | Eqpt sheet  | ||||||
|  | ---------- | ||||||
|  |  | ||||||
|  | The equipment sheet (named "Eqpt") is optional. | ||||||
|  | If provided, it specifies types of boosters and preamplifiers for all ROADM degrees of all ROADM nodes, and for all ILA nodes. | ||||||
|  |  | ||||||
|  | This sheet contains twelve columns:: | ||||||
|  |  | ||||||
|  |                    <--           east cable from a to z                  --> <--        west from z to a                          --> | ||||||
|  |   Node A ; Node Z ; amp type ; att_in ; amp gain ; tilt ; att_out ; delta_p ; amp type ; att_in ; amp gain ; tilt ; att_out ; delta_p | ||||||
|  |  | ||||||
|  | If the sheet is present, it MUST have as many lines as there are egress directions of ROADMs defined in Links Sheet, and all ILAs. | ||||||
|  |  | ||||||
|  | For example, consider the following list of links (A, B and C being a ROADM and amp# ILAs): | ||||||
|  |  | ||||||
|  | :: | ||||||
|  |  | ||||||
|  |   A    - amp1 | ||||||
|  |   amp1 - amp2 | ||||||
|  |   Amp2 - B | ||||||
|  |   A    - amp3 | ||||||
|  |   amp3 - C | ||||||
|  |  | ||||||
|  | then Eqpt sheet should contain: | ||||||
|  |   - one line for each ILAs: amp1, amp2, amp3  | ||||||
|  |   - one line for each one-degree ROADM (B and C in this example) | ||||||
|  |   - two lines for each two-degree ROADM (just the ROADM A) | ||||||
|  |  | ||||||
|  | :: | ||||||
|  |  | ||||||
|  |   A    - amp1 | ||||||
|  |   amp1 - amp2 | ||||||
|  |   Amp2 - B | ||||||
|  |   A    - amp3 | ||||||
|  |   amp3 - C | ||||||
|  |   B    - amp2 | ||||||
|  |   C    - amp3 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | In case you already have filled Nodes and Links sheets `create_eqpt_sheet.py <gnpy/example-data/create_eqpt_sheet.py>`_  can be used to automatically create a template for the mandatory entries of the list. | ||||||
|  |  | ||||||
|  | .. code-block:: shell | ||||||
|  |  | ||||||
|  |     $ cd $(gnpy-example-data) | ||||||
|  |     $ python create_eqpt_sheet.py meshTopologyExampleV2.xls | ||||||
|  |  | ||||||
|  | This generates a text file meshTopologyExampleV2_eqt_sheet.txt  whose content can be directly copied into the Eqt sheet of the excel file. The user then can fill the values in the rest of the columns. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | - **Node A** is mandatory. It is the name of the node (as listed in Nodes sheet). | ||||||
|  |   If Node A is a 'ROADM' (Type attribute in sheet Node), its number of occurence must be equal to its degree. | ||||||
|  |   If Node A is an 'ILA' it should appear only once. | ||||||
|  |  | ||||||
|  | - **Node Z** is mandatory. It is the egress direction from the *Node A* site. Multiple Links between the same Node A and NodeZ is not supported. | ||||||
|  |  | ||||||
|  | - **amp type** is not mandatory.  | ||||||
|  |   If filled it must contain types listed in `eqpt_config.json <gnpy/example-data/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. | ||||||
|  |   If filled, it must contain positive numbers. | ||||||
|  |  | ||||||
|  | - *att_in* and *att_out* are not mandatory and are not used yet. They are the value of the attenuator at input and output of amplifier (in dB). | ||||||
|  |   If filled they must contain positive numbers. | ||||||
|  |  | ||||||
|  | - *tilt* --TODO-- | ||||||
|  |  | ||||||
|  | - **delta_p**, in dBm,  is not mandatory. If filled it is used to set the output target power per channel at the output of the amplifier, if power_mode is True. The output power is then set to power_dbm + delta_power. | ||||||
|  |  | ||||||
|  | # to be completed # | ||||||
|  |  | ||||||
|  | (in progress) | ||||||
|  |  | ||||||
|  | .. _excel-service-sheet: | ||||||
|  |  | ||||||
|  | Service sheet  | ||||||
|  | ------------- | ||||||
|  |  | ||||||
|  | Service sheet is optional. It lists the services for which path and feasibility must be computed with ``gnpy-path-request``. | ||||||
|  |  | ||||||
|  | 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 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** 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. | ||||||
|  |  | ||||||
|  | - **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. | ||||||
|  |  | ||||||
|  |   - 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: 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.  | ||||||
|  |  | ||||||
|  | - **path bandwidth** is mandatory. It is the amount of capacity required between source and destination in Gbit/s. Value should be positive (non zero). It is used to compute the amount of required spectrum for the service.   | ||||||
							
								
								
									
										161
									
								
								docs/extending.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								docs/extending.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | |||||||
|  | .. _extending: | ||||||
|  |  | ||||||
|  | Extending GNPy with vendor-specific data | ||||||
|  | ======================================== | ||||||
|  |  | ||||||
|  | GNPy ships with an :ref:`equipment library<concepts-equipment>` containing machine-readable datasheets of networking equipment. | ||||||
|  | Vendors who are willing to contribute descriptions of their supported products are encouraged to `submit a patch <https://review.gerrithub.io/Documentation/intro-gerrit-walkthrough-github.html>`__. | ||||||
|  |  | ||||||
|  | This chapter discusses option for modeling performance of :ref:`EDFA amplifiers<extending-edfa>`, :ref:`Raman amplifiers<extending-raman>`, :ref:`transponders<extending-transponder>` and :ref:`ROADMs<extending-roadm>`. | ||||||
|  |  | ||||||
|  | .. _extending-edfa: | ||||||
|  |  | ||||||
|  | EDFAs | ||||||
|  | ----- | ||||||
|  |  | ||||||
|  | An accurate description of the :abbr:`EDFA (Erbium-Doped Fiber Amplifier)` and especially its noise characteristics is required. | ||||||
|  | GNPy describes this property in terms of the **Noise Figure (NF)** of an amplifier model as a function of its operating point. | ||||||
|  | GNPy supports several different :ref:`noise models<concepts-nf-model>`, and vendors are encouraged to pick one which describes performance of their equipment most accurately. | ||||||
|  |  | ||||||
|  | .. _ext-nf-model-polynomial-NF: | ||||||
|  |  | ||||||
|  | Polynomial NF | ||||||
|  | ************* | ||||||
|  |  | ||||||
|  | This model computes the NF as a function of the difference between the optimal gain and the current gain. | ||||||
|  | The NF is expressed as a third-degree polynomial: | ||||||
|  |  | ||||||
|  | .. math:: | ||||||
|  |  | ||||||
|  |        f(x) &= \text{a}x^3 + \text{b}x^2 + \text{c}x + \text{d} | ||||||
|  |  | ||||||
|  |   \text{NF} &= f(G_\text{max} - G) | ||||||
|  |  | ||||||
|  | This model can be also used for fixed-gain fixed-NF amplifiers. | ||||||
|  | In that case, use: | ||||||
|  |  | ||||||
|  | .. math:: | ||||||
|  |  | ||||||
|  |   a = b = c &= 0 | ||||||
|  |  | ||||||
|  |           d &= \text{NF} | ||||||
|  |  | ||||||
|  | .. _ext-nf-model-polynomial-OSNR-OpenROADM: | ||||||
|  |  | ||||||
|  | Polynomial OSNR (OpenROADM-style) | ||||||
|  | ********************************* | ||||||
|  |  | ||||||
|  | This model is useful for amplifiers compliant to the OpenROADM specification for ILA. | ||||||
|  | In OpenROADM, amplifier performance is evaluated via its incremental OSNR, which is a function of the input power. | ||||||
|  |  | ||||||
|  | .. math:: | ||||||
|  |  | ||||||
|  |     \text{OSNR}_\text{inc}(P_\text{in}) = \text{a}P_\text{in}^3 + \text{b}P_\text{in}^2 + \text{c}P_\text{in} + \text{d} | ||||||
|  |  | ||||||
|  | .. _ext-nf-model-min-max-NF: | ||||||
|  |  | ||||||
|  | Min-max NF | ||||||
|  | ********** | ||||||
|  |  | ||||||
|  | When the vendor prefers not to share the amplifier description in full detail, GNPy also supports describing the NF characteristics via the *minimal* and *maximal NF*. | ||||||
|  | This approximates a more accurate polynomial description reasonably well for some models of a dual-coil EDFA with a VOA in between. | ||||||
|  | In these amplifiers, the minimal NF is achieved when the EDFA operates at its maximal (and usually optimal, in terms of flatness) gain. | ||||||
|  | The worst (maximal) NF applies  when the EDFA operates at the minimal gain. | ||||||
|  |  | ||||||
|  | .. _ext-nf-model-dual-stage-amplifier: | ||||||
|  |  | ||||||
|  | Dual-stage | ||||||
|  | ********** | ||||||
|  |  | ||||||
|  | Dual-stage amplifier combines two distinct amplifiers. | ||||||
|  | Vendors which provide an accurate description of their preamp and booster stages separately can use the dual-stage model for an aggregate description of the whole amplifier. | ||||||
|  |  | ||||||
|  | .. _ext-nf-model-advanced: | ||||||
|  |  | ||||||
|  | Advanced Specification | ||||||
|  | ********************** | ||||||
|  |  | ||||||
|  | The amplifier performance can be further described in terms of gain ripple, NF ripple, and the dynamic gain tilt. | ||||||
|  | When provided, the amplifier characteristic is fine-tuned as a function of carrier frequency. | ||||||
|  |  | ||||||
|  | .. _extending-raman: | ||||||
|  |  | ||||||
|  | Raman Amplifiers | ||||||
|  | ---------------- | ||||||
|  |  | ||||||
|  | An accurate simulation of Raman amplification requires knowledge of: | ||||||
|  |  | ||||||
|  | - the *power* and *wavelength* of all Raman pumping lasers, | ||||||
|  | - the *direction*, whether it is co-propagating or counter-propagating, | ||||||
|  | - the Raman efficiency of the fiber, | ||||||
|  | - the fiber temperature. | ||||||
|  |  | ||||||
|  | Under certain scenarios it is useful to be able to run a simulation without an accurate Raman description. | ||||||
|  | For these purposes, it is possible to approximate a Raman amplifier via a fixed-gain EDFA with the :ref:`polynomial NF<ext-nf-model-polynomial-NF>` model using :math:`\text{a} = \text{b} = \text{c} = 0`, and a desired effective :math:`\text{d} = NF`. | ||||||
|  | This is also useful to quickly approximate a hybrid EDFA+Raman amplifier. | ||||||
|  |  | ||||||
|  | .. _extending-transponder: | ||||||
|  |  | ||||||
|  | Transponders | ||||||
|  | ------------ | ||||||
|  |  | ||||||
|  | Since transponders are usually capable of operating in a variety of modes, these are described separately. | ||||||
|  | A *mode* usually refers to a particular performance point that is defined by a combination of the symbol rate, modulation format, and :abbr:`FEC (Forward Error Correction)`. | ||||||
|  |  | ||||||
|  | The following data are required for each mode: | ||||||
|  |  | ||||||
|  | ``bit-rate`` | ||||||
|  |   Data bit rate, in :math:`\text{Gbits}\times s^{-1}`. | ||||||
|  | ``baud-rate`` | ||||||
|  |   Symbol modulation rate, in :math:`\text{Gbaud}`. | ||||||
|  | ``required-osnr`` | ||||||
|  |   Minimal allowed OSNR for the receiver. | ||||||
|  | ``tx-osnr`` | ||||||
|  |   Initial OSNR at the transmitter's output. | ||||||
|  | ``grid-spacing`` | ||||||
|  |   Minimal grid spacing, i.e., an effective channel spectral bandwidth. | ||||||
|  |   In :math:`\text{Hz}`. | ||||||
|  | ``tx-roll-off`` | ||||||
|  |   Roll-off parameter (:math:`\beta`) of the TX pulse shaping filter. | ||||||
|  |   This assumes a raised-cosine filter. | ||||||
|  | ``rx-power-min`` and ``rx-power-max`` | ||||||
|  |   The allowed range of power at the receiver. | ||||||
|  |   In :math:`\text{dBm}`. | ||||||
|  | ``cd-max`` | ||||||
|  |   Maximal allowed Chromatic Dispersion (CD). | ||||||
|  |   In :math:`\text{ps}/\text{nm}`. | ||||||
|  | ``pmd-max`` | ||||||
|  |   Maximal allowed Polarization Mode Dispersion (PMD). | ||||||
|  |   In :math:`\text{ps}`. | ||||||
|  | ``cd-penalty`` | ||||||
|  |   *Work-in-progress.* | ||||||
|  |   Describes the increase of the requires GSNR as the :abbr:`CD (Chromatic Dispersion)` deteriorates. | ||||||
|  | ``dgd-penalty`` | ||||||
|  |   *Work-in-progress.* | ||||||
|  |   Describes the increase of the requires GSNR as the :abbr:`DGD (Differential Group Delay)` deteriorates. | ||||||
|  | ``pmd-penalty`` | ||||||
|  |   *Work-in-progress.* | ||||||
|  |   Describes the increase of the requires GSNR as the :abbr:`PMD (Polarization Mode Dispersion)` deteriorates. | ||||||
|  |  | ||||||
|  | GNPy does not directly track the FEC performance, so the type of chosen FEC is likely indicated in the *name* of the selected transponder mode alone. | ||||||
|  |  | ||||||
|  | .. _extending-roadm: | ||||||
|  |  | ||||||
|  | ROADMs | ||||||
|  | ------ | ||||||
|  |  | ||||||
|  | In a :abbr:`ROADM (Reconfigurable Add/Drop Multiplexer)`, GNPy simulates the impairments of the preamplifiers and boosters of line degrees :ref:`separately<topo-roadm-preamp-booster>`. | ||||||
|  | The set of parameters for each ROADM model therefore includes: | ||||||
|  |  | ||||||
|  | ``add-drop-osnr`` | ||||||
|  |   OSNR penalty introduced by the Add and Drop stages of this ROADM type. | ||||||
|  | ``target-channel-out-power`` | ||||||
|  |   Per-channel target TX power towards the egress amplifier. | ||||||
|  |   Within GNPy, a ROADM is expected to attenuate any signal that enters the ROADM node to this level. | ||||||
|  |   This can be overridden on a per-link in the network topology. | ||||||
|  | ``pmd`` | ||||||
|  |   Polarization mode dispersion (PMD) penalty of the express path. | ||||||
|  |   In :math:`\text{ps}`. | ||||||
|  |  | ||||||
|  | Provisions are in place to define the list of all allowed booster and preamplifier types. | ||||||
|  | This is useful for specifying constraints on what amplifier modules fit into ROADM chassis, and when using fully disaggregated ROADM topologies as well. | ||||||
							
								
								
									
										13
									
								
								docs/gnpy-api-core.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								docs/gnpy-api-core.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | ``gnpy.core`` | ||||||
|  | ------------- | ||||||
|  |  | ||||||
|  | .. automodule:: gnpy.core | ||||||
|  | .. automodule:: gnpy.core.ansi_escapes | ||||||
|  | .. automodule:: gnpy.core.elements | ||||||
|  | .. automodule:: gnpy.core.equipment | ||||||
|  | .. automodule:: gnpy.core.exceptions | ||||||
|  | .. automodule:: gnpy.core.info | ||||||
|  | .. automodule:: gnpy.core.network | ||||||
|  | .. automodule:: gnpy.core.parameters | ||||||
|  | .. automodule:: gnpy.core.science_utils | ||||||
|  | .. automodule:: gnpy.core.utils | ||||||
							
								
								
									
										9
									
								
								docs/gnpy-api-tools.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								docs/gnpy-api-tools.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | ``gnpy.tools`` | ||||||
|  | -------------- | ||||||
|  |  | ||||||
|  | .. automodule:: gnpy.tools | ||||||
|  | .. automodule:: gnpy.tools.cli_examples | ||||||
|  | .. automodule:: gnpy.tools.convert | ||||||
|  | .. automodule:: gnpy.tools.json_io | ||||||
|  | .. automodule:: gnpy.tools.plots | ||||||
|  | .. automodule:: gnpy.tools.service_sheet | ||||||
							
								
								
									
										6
									
								
								docs/gnpy-api-topology.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								docs/gnpy-api-topology.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | ``gnpy.topology`` | ||||||
|  | ----------------- | ||||||
|  |  | ||||||
|  | .. automodule:: gnpy.topology | ||||||
|  | .. automodule:: gnpy.topology.request | ||||||
|  | .. automodule:: gnpy.topology.spectrum_assignment | ||||||
							
								
								
									
										14
									
								
								docs/gnpy-api.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								docs/gnpy-api.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | *************************** | ||||||
|  | API Reference Documentation | ||||||
|  | *************************** | ||||||
|  |  | ||||||
|  | ``gnpy`` package | ||||||
|  | ================ | ||||||
|  |  | ||||||
|  | .. automodule:: gnpy | ||||||
|  |  | ||||||
|  | .. toctree:: | ||||||
|  |  | ||||||
|  |    gnpy-api-core | ||||||
|  |    gnpy-api-topology | ||||||
|  |    gnpy-api-tools | ||||||
							
								
								
									
										
											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 | 
							
								
								
									
										
											BIN
										
									
								
								docs/images/GNPy-logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/images/GNPy-logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 20 KiB | 
| @@ -1,37 +1,20 @@ | |||||||
| .. GNpy documentation master file, created by | GNPy: Optical Route Planning Library | ||||||
|    sphinx-quickstart on Mon Dec 18 14:41:01 2017. | ===================================================================== | ||||||
|    You can adapt this file completely to your liking, but it should at least |  | ||||||
|    contain the root `toctree` directive. |  | ||||||
|  |  | ||||||
| Welcome to GNpy's documentation! | `GNPy <http://github.com/telecominfraproject/gnpy>`_ is an open-source, | ||||||
| ================================ | community-developed library for building route planning and optimization tools | ||||||
|  | in real-world mesh optical networks. It is based on the Gaussian Noise Model. | ||||||
| Gaussian Noise (GN) based modeling library for physical layer impairment evaluation in optical networks. |  | ||||||
|  |  | ||||||
| Summary |  | ||||||
| -------- |  | ||||||
|  |  | ||||||
| We believe that openly sharing ideas, specifications, and other intellectual property is the key to maximizing innovation and reducing complexity |  | ||||||
|  |  | ||||||
| PSE WG Charter |  | ||||||
| -------------- |  | ||||||
|  |  | ||||||
| - Goal is to build an end-to-end simulation environment which defines the network models of the optical device transfer functions and their parameters. This environment will provide validation of the optical performance requirements for the TIP OLS building blocks.    |  | ||||||
| - The model may be approximate or complete depending on the network complexity. Each model shall be validated against the proposed network scenario.  |  | ||||||
| - The environment must be able to process network models from multiple vendors, and also allow users to pick any implementation in an open source framework.  |  | ||||||
| - The PSE will influence and benefit from the innovation of the DTC, API, and OLS working groups. |  | ||||||
| - The PSE represents a step along the journey towards multi-layer optimization. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Documentation |  | ||||||
| ============= |  | ||||||
|  |  | ||||||
| The following pages are meant to describe specific implementation details and modeling assumptions behind GNpy.  |  | ||||||
|  |  | ||||||
| .. toctree:: | .. toctree:: | ||||||
|    :maxdepth: 2 |    :maxdepth: 4 | ||||||
|  |  | ||||||
|    gn_model |    concepts | ||||||
|  |    install | ||||||
|  |    json | ||||||
|  |    excel | ||||||
|  |    extending | ||||||
|  |    model | ||||||
|  |    gnpy-api | ||||||
|  |  | ||||||
| Indices and tables | Indices and tables | ||||||
| ================== | ================== | ||||||
| @@ -40,31 +23,3 @@ Indices and tables | |||||||
| * :ref:`modindex` | * :ref:`modindex` | ||||||
| * :ref:`search` | * :ref:`search` | ||||||
|  |  | ||||||
| Contributors in alphabetical order |  | ||||||
| ================================== |  | ||||||
| +----------+------------+-----------------------+----------------------------+ |  | ||||||
| | Name     | Surname    | Affiliation           | Contact                    | |  | ||||||
| +==========+============+=======================+============================+ |  | ||||||
| | Alessio  | Ferrari    | Politecnico di Torino | alessio.ferrari@polito.it  | |  | ||||||
| +----------+------------+-----------------------+----------------------------+ |  | ||||||
| | Brian    | Taylor     | Facebook              | briantaylor@fb.com         | |  | ||||||
| +----------+------------+-----------------------+----------------------------+ |  | ||||||
| | David    | Boertjes   | Ciena                 | dboertje@ciena.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     | Consultant            | james@dontusethiscode.com  | |  | ||||||
| +----------+------------+-----------------------+----------------------------+ |  | ||||||
| | Jeanluc  | Auge       | Orange                | jeanluc.auge@orange.com    | |  | ||||||
| +----------+------------+-----------------------+----------------------------+ |  | ||||||
| | Mattia   | Cantono    | Politecnico di Torino | mattia.cantono@polito.it   | |  | ||||||
| +----------+------------+-----------------------+----------------------------+ |  | ||||||
| | Vittorio | Curri      | Politecnico di Torino | vittorio.curri@polito.it   | |  | ||||||
| +----------+------------+-----------------------+----------------------------+ |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										111
									
								
								docs/install.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								docs/install.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | Installing GNPy | ||||||
|  | --------------- | ||||||
|  |  | ||||||
|  | There are several methods on how to obtain GNPy. | ||||||
|  | The easiest option for a non-developer is probably going via our :ref:`Docker images<install-docker>`. | ||||||
|  | Developers are encouraged to install the :ref:`Python package in the same way as any other Python package<install-pip>`. | ||||||
|  | Note that this needs a :ref:`working installation of Python<install-python>`, for example :ref:`via Anaconda<install-anaconda>`. | ||||||
|  |  | ||||||
|  | .. _install-docker: | ||||||
|  |  | ||||||
|  | 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/example-data# | ||||||
|  |  | ||||||
|  | On Windows, launch from Powershell as: | ||||||
|  |  | ||||||
|  | .. code-block:: console | ||||||
|  |  | ||||||
|  |     PS C:\> docker run -it --rm --volume ${PWD}:/shared telecominfraproject/oopt-gnpy | ||||||
|  |     root@89784e577d44:/shared/example-data# | ||||||
|  |  | ||||||
|  | In both cases, a directory named ``example-data/`` 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. | ||||||
|  |  | ||||||
|  | .. _install-python: | ||||||
|  |  | ||||||
|  | Using Python on your computer | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |    **Note**: `gnpy` supports Python 3 only. Python 2 is not supported. | ||||||
|  |    `gnpy` requires Python ≥3.6 | ||||||
|  |  | ||||||
|  |    **Note**: the `gnpy` maintainers strongly recommend the use of Anaconda for | ||||||
|  |    managing dependencies. | ||||||
|  |  | ||||||
|  | It is recommended that you use a "virtual environment" when installing `gnpy`. | ||||||
|  | Do not install `gnpy` on your system Python. | ||||||
|  |  | ||||||
|  | .. _install-anaconda: | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | environments" yourself (see: | ||||||
|  | https://conda.io/docs/user-guide/tasks/manage-environments.html) | ||||||
|  |  | ||||||
|  | To activate your Anaconda virtual environment, you may need to do the | ||||||
|  | following: | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |     $ source /path/to/anaconda/bin/activate # activate Anaconda base environment | ||||||
|  |     (base) $                                # note the change to the prompt | ||||||
|  |  | ||||||
|  | You can check which Anaconda environment you are using with: | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |     (base) $ conda env list                          # list all environments | ||||||
|  |     # conda environments: | ||||||
|  |     # | ||||||
|  |     base                  *  /src/install/anaconda3 | ||||||
|  |  | ||||||
|  |     (base) $ echo $CONDA_DEFAULT_ENV                 # show default environment | ||||||
|  |     base | ||||||
|  |  | ||||||
|  | You can check your version of Python with the following. If you are using | ||||||
|  | Anaconda's Python 3, you should see similar output as below. Your results may | ||||||
|  | be slightly different depending on your Anaconda installation path and the | ||||||
|  | exact version of Python you are using. | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |     $ which python                   # check which Python executable is used | ||||||
|  |     /path/to/anaconda/bin/python | ||||||
|  |     $ python -V                      # check your Python version | ||||||
|  |     Python 3.6.5 :: Anaconda, Inc. | ||||||
|  |  | ||||||
|  | .. _install-pip: | ||||||
|  |  | ||||||
|  | Installing the Python package | ||||||
|  | ***************************** | ||||||
|  |  | ||||||
|  | From within your Anaconda Python 3 environment, you can clone the master branch | ||||||
|  | of the `gnpy` repo and install it with: | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |     $ git clone https://github.com/Telecominfraproject/oopt-gnpy # clone the repo | ||||||
|  |     $ cd oopt-gnpy | ||||||
|  |     $ pip install --editable . # note the trailing dot | ||||||
|  |  | ||||||
|  | To test that `gnpy` was successfully installed, you can run this command. If it | ||||||
|  | executes without a ``ModuleNotFoundError``, you have successfully installed | ||||||
|  | `gnpy`. | ||||||
|  |  | ||||||
|  | .. code-block:: shell-session | ||||||
|  |  | ||||||
|  |     $ python -c 'import gnpy' # attempt to import gnpy | ||||||
|  |  | ||||||
|  |     $ pytest                  # run tests | ||||||
							
								
								
									
										341
									
								
								docs/json.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										341
									
								
								docs/json.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,341 @@ | |||||||
|  | JSON Input Files | ||||||
|  | ================ | ||||||
|  |  | ||||||
|  | GNPy uses a set of JSON files for modeling the network. | ||||||
|  | Some data (such as network topology or the service requests) can be also passed via :ref:`XLS files<excel-service-sheet>`. | ||||||
|  |  | ||||||
|  | Equipment Library | ||||||
|  | ----------------- | ||||||
|  |  | ||||||
|  | Design and transmission parameters are defined in a dedicated json file. | ||||||
|  | By default, this information is read from `gnpy/example-data/eqpt_config.json <https://github.com/Telecominfraproject/oopt-gnpy/blob/master/gnpy/example-data/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.) | ||||||
|  |  | ||||||
|  | EDFA | ||||||
|  | ~~~~ | ||||||
|  |  | ||||||
|  | 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 `gnpy/example-data/std_medium_gain_advanced_config.json <https://github.com/Telecominfraproject/oopt-gnpy/blob/master/gnpy/example-data/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.)         | | ||||||
|  | +------------------------+-----------+-----------------------------------------+ | ||||||
|  |  | ||||||
|  | Fiber | ||||||
|  | ~~~~~ | ||||||
|  |  | ||||||
|  | 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 fiber in the    | | ||||||
|  | |                      |           | JSON or Excel template topology input   | | ||||||
|  | |                      |           | file                                    | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``dispersion``       | (number)  | (s.m-1.m-1)                             | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``dispersion_slope`` | (number)  | (s.m-1.m-1.m-1)                         | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``gamma``            | (number)  | 2pi.n2/(lambda*Aeff) (w-1.m-1)          | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``pmd_coef``         | (number)  | Polarization mode dispersion (PMD)      | | ||||||
|  | |                      |           | coefficient. (s.sqrt(m)-1)              | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  |  | ||||||
|  | Transceiver | ||||||
|  | ~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | ``gnpy-path-request`` script. | ||||||
|  |  | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | field                | type      | description                             | | ||||||
|  | +======================+===========+=========================================+ | ||||||
|  | | ``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.                       | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``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.       | | ||||||
|  | |                      |           | Each mode is described as below.        | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  |  | ||||||
|  | The modes are defined as follows: | ||||||
|  |  | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | field                | type      | description                             | | ||||||
|  | +======================+===========+=========================================+ | ||||||
|  | | ``format``           | (string)  | a unique name to ID the mode            | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``baud_rate``        | (number)  | in Hz                                   | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``OSNR``             | (number)  | min required OSNR in 0.1nm (dB)         | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``bit_rate``         | (number)  | in bit/s                                | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``roll_off``         | (number)  | Pure number between 0 and 1. TX signal  | | ||||||
|  | |                      |           | roll-off shape. Used by Raman-aware     | | ||||||
|  | |                      |           | simulation code.                        | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``tx_osnr``          | (number)  | In dB. OSNR out from transponder.       | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  | | ``cost``             | (number)  | Arbitrary unit                          | | ||||||
|  | +----------------------+-----------+-----------------------------------------+ | ||||||
|  |  | ||||||
|  | ROADM | ||||||
|  | ~~~~~ | ||||||
|  |  | ||||||
|  | The user can only modify the value of existing parameters: | ||||||
|  |  | ||||||
|  | +--------------------------+-----------+---------------------------------------------+ | ||||||
|  | | 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   | | ||||||
|  | +--------------------------+-----------+---------------------------------------------+ | ||||||
|  | | ``pmd``                  | (number)  | Polarization mode dispersion (PMD). (s)     | | ||||||
|  | +--------------------------+-----------+---------------------------------------------+ | ||||||
|  | | ``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.                                     | | ||||||
|  | +--------------------------+-----------+---------------------------------------------+ | ||||||
|  |  | ||||||
|  | Global parameters | ||||||
|  | ----------------- | ||||||
|  |  | ||||||
|  | The following options are still defined in ``eqpt_config.json`` for legacy reasons, but | ||||||
|  | they do not correspond to tangible network devices. | ||||||
|  |  | ||||||
|  | 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. | ||||||
|  | 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. | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | ~~~~ | ||||||
|  |  | ||||||
|  | Span configuration 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 = 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.xlsx 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 | ||||||
|  |  | ||||||
|  |     { | ||||||
|  |         "uid": "fiber (A1->A2)", | ||||||
|  |         "type": "Fiber", | ||||||
|  |         "type_variety": "SSMF", | ||||||
|  |         "params": | ||||||
|  |         { | ||||||
|  |               "length": 120.0, | ||||||
|  |               "loss_coef": 0.2, | ||||||
|  |               "length_units": "km", | ||||||
|  |               "att_in": 0, | ||||||
|  |               "con_in": 0, | ||||||
|  |               "con_out": 0 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | SpectralInformation | ||||||
|  | ~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | one power/channel definition. | ||||||
|  |  | ||||||
|  | +----------------------+-----------+-------------------------------------------+ | ||||||
|  | | field                |   type    | description                               | | ||||||
|  | +======================+===========+===========================================+ | ||||||
|  | | ``f_min``,           | (number)  | In Hz. Carrier min max excursion.         | | ||||||
|  | | ``f_max``            |           |                                           | | ||||||
|  | +----------------------+-----------+-------------------------------------------+ | ||||||
|  | | ``baud_rate``        | (number)  | In Hz. Simulated baud rate.               | | ||||||
|  | +----------------------+-----------+-------------------------------------------+ | ||||||
|  | | ``spacing``          | (number)  | In Hz. Carrier spacing.                   | | ||||||
|  | +----------------------+-----------+-------------------------------------------+ | ||||||
|  | | ``roll_off``         | (number)  | Pure number between 0 and 1. TX signal    | | ||||||
|  | |                      |           | roll-off shape. Used by Raman-aware       | | ||||||
|  | |                      |           | simulation code.                          | | ||||||
|  | +----------------------+-----------+-------------------------------------------+ | ||||||
|  | | ``tx_osnr``          | (number)  | In dB. OSNR out from transponder.         | | ||||||
|  | +----------------------+-----------+-------------------------------------------+ | ||||||
|  | | ``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           | | ||||||
|  | |                      |           | reference power for                       | | ||||||
|  | |                      |           | Spans/delta_power_range_db. For example,  | | ||||||
|  | |                      |           | if delta_power_range_db = `[0,0,0]`, the  | | ||||||
|  | |                      |           | same power=power_dbm is launched in every | | ||||||
|  | |                      |           | spans. The network design is performed    | | ||||||
|  | |                      |           | with the power_dbm value: even if a       | | ||||||
|  | |                      |           | power sweep is defined (see after) the    | | ||||||
|  | |                      |           | design is not repeated.                   | | ||||||
|  | +----------------------+-----------+-------------------------------------------+ | ||||||
|  | | ``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.                         | | ||||||
|  | +----------------------+-----------+-------------------------------------------+ | ||||||
| @@ -1,18 +1,20 @@ | |||||||
| The QoT estimation in the PSE framework of TIP-OOPT | .. _physical-model: | ||||||
| ======================================================= | 
 | ||||||
|  | Physical Model used in GNPy | ||||||
|  | =========================== | ||||||
| 
 | 
 | ||||||
| QoT-E including ASE noise and NLI accumulation  | QoT-E including ASE noise and NLI accumulation  | ||||||
| ---------------------------------------------- | ---------------------------------------------- | ||||||
| 
 | 
 | ||||||
| The operations of PSE simulative framework are based on the capability to estimate the QoT of one | The operations of PSE simulative framework are based on the capability to | ||||||
| or more channels operating lightpaths over a given network route. For | estimate the QoT of one or more channels operating lightpaths over a given | ||||||
| backbone transport networks, we can suppose that transceivers are | network route. For backbone transport networks, we can suppose that | ||||||
| operating polarization-division-multiplexed multilevel modulation | transceivers are operating polarization-division-multiplexed multilevel | ||||||
| formats with DSP-based coherent receivers, including equalization. For | modulation formats with DSP-based coherent receivers, including equalization. | ||||||
| the optical links, we focus on state-of-the-art amplified and | For the optical links, we focus on state-of-the-art amplified and uncompensated | ||||||
| uncompensated fiber links, connecting network nodes including ROADMs, | fiber links, connecting network nodes including ROADMs, where add and drop | ||||||
| where add and drop operations on data traffic are performed. In such a | operations on data traffic are performed. In such a transmission scenario, it | ||||||
| transmission scenario, it is well accepted | is well accepted | ||||||
| :cite:`vacondio_nonlinear_2012,bononi_modeling_2012,carena_modeling_2012,mecozzi_nonlinear_2012,secondini_analytical_2012,johannisson_perturbation_2013,dar_properties_2013,serena_alternative_2013,secondini_achievable_2013,poggiolini_gn-model_2014,dar_accumulation_2014,poggiolini_analytical_2011,savory_approximations_2013,bononi_single-_2013,johannisson_modeling_2014` | :cite:`vacondio_nonlinear_2012,bononi_modeling_2012,carena_modeling_2012,mecozzi_nonlinear_2012,secondini_analytical_2012,johannisson_perturbation_2013,dar_properties_2013,serena_alternative_2013,secondini_achievable_2013,poggiolini_gn-model_2014,dar_accumulation_2014,poggiolini_analytical_2011,savory_approximations_2013,bononi_single-_2013,johannisson_modeling_2014` | ||||||
| to assume that transmission performances are limited by the amplified | to assume that transmission performances are limited by the amplified | ||||||
| spontaneous emission (ASE) noise generated by optical amplifiers and and | spontaneous emission (ASE) noise generated by optical amplifiers and and | ||||||
| @@ -49,7 +51,6 @@ filtering effects. Note that for state-of-the art equipment, filtering | |||||||
| effects can be typically neglected over routes with few hops | effects can be typically neglected over routes with few hops | ||||||
| :cite:`rahman_mitigation_2014,foggi_overcoming_2015`. | :cite:`rahman_mitigation_2014,foggi_overcoming_2015`. | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| To properly estimate :math:`P_{\text{ch}}` and :math:`P_{\text{ASE}}` | To properly estimate :math:`P_{\text{ch}}` and :math:`P_{\text{ASE}}` | ||||||
| the transmitted power at the beginning of the considered route must be | the transmitted power at the beginning of the considered route must be | ||||||
| known, and losses and amplifiers gain and noise figure, including their | known, and losses and amplifiers gain and noise figure, including their | ||||||
| @@ -62,8 +63,10 @@ models have been proposed and validated in the technical literature | |||||||
| The decision about which model to test within the PSE activities was | The decision about which model to test within the PSE activities was | ||||||
| driven by requirements of the entire PSE framework: | driven by requirements of the entire PSE framework: | ||||||
| 
 | 
 | ||||||
| i. the model must be *local*, i.e., related individually to each network element (i.e. fiber span) generating NLI, independently of preceding and subsequent elements; and  | i. the model must be *local*, i.e., related individually to each network | ||||||
| ii. the related computational time must be compatible with interactive operations.  | element (i.e. fiber span) generating NLI, independently of preceding and | ||||||
|  | subsequent elements; and ii. the related computational time must be compatible | ||||||
|  | with interactive operations.  | ||||||
| 
 | 
 | ||||||
| So, the choice fell on the Gaussian Noise | So, the choice fell on the Gaussian Noise | ||||||
| (GN) model with incoherent accumulation of NLI over fiber spans | (GN) model with incoherent accumulation of NLI over fiber spans | ||||||
| @@ -79,46 +82,67 @@ for fiber types with chromatic dispersion roughly larger than 4 | |||||||
| ps/nm/km, the analytical approximation ensures an excellent accuracy | ps/nm/km, the analytical approximation ensures an excellent accuracy | ||||||
| with a computational time compatible with real-time operations. | with a computational time compatible with real-time operations. | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| The Gaussian Noise Model to evaluate the NLI | The Gaussian Noise Model to evaluate the NLI | ||||||
| -------------------------------------------- | -------------------------------------------- | ||||||
| As previously stated, fiber propagation of multilevel modulation formats relying on the polarization-division-multiplexing   |  | ||||||
| generates impairments that can be summarized as  a disturbance called nonlinear interference (NLI), |  | ||||||
| when exploiting a DSP-based coherent receiver, as in all state-of-the-art equipment. |  | ||||||
| From a practical point of view, the NLI can be modeled as an additive  |  | ||||||
| Gaussian random process added by each fiber span, and whose strength depends on the cube of the input power spectral density and  |  | ||||||
| on the fiber-span parameters.  |  | ||||||
| 
 | 
 | ||||||
| Since the introduction in the market in 2007 of the first transponder based on such a transmission technique, the scientific | As previously stated, fiber propagation of multilevel modulation formats | ||||||
| community has intensively worked to define the propagation behavior of such a trasnmission technique. | relying on the polarization-division-multiplexing  generates impairments that | ||||||
| First, the role of in-line chromatic dispersion compensation has been investigated, deducing that besides being  | can be summarized as  a disturbance called nonlinear interference (NLI), when | ||||||
| not essential, it is indeed detrimental for performances :cite:`curri_dispersion_2008`. | exploiting a DSP-based coherent receiver, as in all state-of-the-art equipment. | ||||||
| Then, it has been observed that the fiber propagation impairments are practically summarized by the sole NLI, being all the other  | From a practical point of view, the NLI can be modeled as an additive Gaussian | ||||||
| phenomena compensated for by the blind equalizer implemented in the receiver DSP :cite:`carena_statistical_2010`. | random process added by each fiber span, and whose strength depends on the cube | ||||||
| Once these assessments have been accepted by the community, several prestigious research groups have started to work  | of the input power spectral density and on the fiber-span parameters.  | ||||||
| on deriving analytical models able to estimating the NLI accumulation, and consequentially the generalized SNR that sets the BER, |  | ||||||
| according to the transponder BER vs. SNR performance. |  | ||||||
| Many models delivering different levels of accuracy have been developed and validated. As previously clarified, for the purposes |  | ||||||
| of the PSE framework, the  GN-model with incoherent accumulation of NLI over fiber spans has been selected as adequate. |  | ||||||
| The reason for such a choice is first such a model being a "local" model, so related to each fiber spans, independently of |  | ||||||
| the preceding and succeeding network elements. The other model characteristic driving the choice is  |  | ||||||
| the availability of a closed form for the model, so permitting a real-time evaluation, as required by the PSE framework. |  | ||||||
| For a detailed derivation of the model, please refer to :cite:`poggiolini_analytical_2011`, while a qualitative description |  | ||||||
| can be summarized as in the following. |  | ||||||
| The GN-model assumes that the channel comb propagating in the fiber is well approximated by unpolarized spectrally shaped |  | ||||||
| Gaussian noise. In such a scenario, supposing to rely - as in state-of-the-art equipment - on a receiver entirely compensating for linear propagation effects, propagation in the fiber only excites the four-wave mixing (FWM) process among the continuity of |  | ||||||
| the tones occupying the bandwidth. Such a FWM generates an unpolarized complex Gaussian disturbance in each spectral slot |  | ||||||
| that can be easily evaluated extending the FWM theory from a set of discrete tones - the standard FWM theory introduced back in the 90s by Inoue :cite:`Innoue-FWM`- to a continuity of tones, possibly spectrally shaped. |  | ||||||
| Signals propagating in the fiber are not equivalent to Gaussian noise, but thanks to the absence of in-line compensation for choromatic dispersion, |  | ||||||
| the become so, over short distances.  |  | ||||||
| So, the Gaussian noise model with incoherent accumulation of NLI has estensively proved to be a quick yet accurate and conservative tool |  | ||||||
| to estimate propagation impairments of fiber propagation. |  | ||||||
| Note that the GN-model has not been derived with the aim of an *exact* performance estimation, but to pursue a conservative performance prediction. So, considering these characteristics, and the fact that the NLI is always a secondary effect with respect to the ASE noise accumulation, and - most importantly - that typically linear propagation parameters (losses, gains and noise figures) are known within  |  | ||||||
| a variation range, a QoT estimator based on the GN model is adequate to deliver performance predictions in terms of a reasonable SNR range, rather than an exact value. |  | ||||||
| As final remark, it must be clarified that the GN-model is adequate to be used when relying on a relatively narrow bandwidth up to few THz. When exceeding such a bandwidth occupation, the GN-model must be generalized introducing the interaction with the Stimulated  |  | ||||||
| Raman Scattering in order to give a proper estimation for all channels :cite:`cantono2018modeling`.  |  | ||||||
| This will be the main upgrade required within the PSE framework. |  | ||||||
| 
 | 
 | ||||||
|  | Since the introduction in the market in 2007 of the first transponder based on | ||||||
|  | such a transmission technique, the scientific community has intensively worked | ||||||
|  | to define the propagation behavior of such a trasnmission technique.  First, | ||||||
|  | the role of in-line chromatic dispersion compensation has been investigated, | ||||||
|  | deducing that besides being not essential, it is indeed detrimental for | ||||||
|  | performances :cite:`curri_dispersion_2008`.  Then, it has been observed that | ||||||
|  | the fiber propagation impairments are practically summarized by the sole NLI, | ||||||
|  | being all the other phenomena compensated for by the blind equalizer | ||||||
|  | implemented in the receiver DSP :cite:`carena_statistical_2010`.  Once these | ||||||
|  | assessments have been accepted by the community, several prestigious research | ||||||
|  | groups have started to work on deriving analytical models able to estimating | ||||||
|  | the NLI accumulation, and consequentially the generalized SNR that sets the | ||||||
|  | BER, according to the transponder BER vs. SNR performance.  Many models | ||||||
|  | delivering different levels of accuracy have been developed and validated. As | ||||||
|  | previously clarified, for the purposes of the PSE framework, the  GN-model with | ||||||
|  | incoherent accumulation of NLI over fiber spans has been selected as adequate. | ||||||
|  | The reason for such a choice is first such a model being a "local" model, so | ||||||
|  | related to each fiber spans, independently of the preceding and succeeding | ||||||
|  | network elements. The other model characteristic driving the choice is the | ||||||
|  | availability of a closed form for the model, so permitting a real-time | ||||||
|  | evaluation, as required by the PSE framework.  For a detailed derivation of the | ||||||
|  | model, please refer to :cite:`poggiolini_analytical_2011`, while a qualitative | ||||||
|  | description can be summarized as in the following.  The GN-model assumes that | ||||||
|  | the channel comb propagating in the fiber is well approximated by unpolarized | ||||||
|  | spectrally shaped Gaussian noise. In such a scenario, supposing to rely - as in | ||||||
|  | state-of-the-art equipment - on a receiver entirely compensating for linear | ||||||
|  | propagation effects, propagation in the fiber only excites the four-wave mixing | ||||||
|  | (FWM) process among the continuity of the tones occupying the bandwidth. Such a | ||||||
|  | FWM generates an unpolarized complex Gaussian disturbance in each spectral slot | ||||||
|  | that can be easily evaluated extending the FWM theory from a set of discrete | ||||||
|  | tones - the standard FWM theory introduced back in the 90s by Inoue | ||||||
|  | :cite:`Innoue-FWM`- to a continuity of tones, possibly spectrally shaped. | ||||||
|  | Signals propagating in the fiber are not equivalent to Gaussian noise, but | ||||||
|  | thanks to the absence of in-line compensation for choromatic dispersion, the | ||||||
|  | become so, over short distances.  So, the Gaussian noise model with incoherent | ||||||
|  | accumulation of NLI has estensively proved to be a quick yet accurate and | ||||||
|  | conservative tool to estimate propagation impairments of fiber propagation. | ||||||
|  | Note that the GN-model has not been derived with the aim of an *exact* | ||||||
|  | performance estimation, but to pursue a conservative performance prediction. | ||||||
|  | So, considering these characteristics, and the fact that the NLI is always a | ||||||
|  | secondary effect with respect to the ASE noise accumulation, and - most | ||||||
|  | importantly - that typically linear propagation parameters (losses, gains and | ||||||
|  | noise figures) are known within a variation range, a QoT estimator based on the | ||||||
|  | GN model is adequate to deliver performance predictions in terms of a | ||||||
|  | reasonable SNR range, rather than an exact value.  As final remark, it must be | ||||||
|  | clarified that the GN-model is adequate to be used when relying on a relatively | ||||||
|  | narrow bandwidth up to few THz. When exceeding such a bandwidth occupation, the | ||||||
|  | GN-model must be generalized introducing the interaction with the Stimulated | ||||||
|  | Raman Scattering in order to give a proper estimation for all channels | ||||||
|  | :cite:`cantono2018modeling`.  This will be the main upgrade required within the | ||||||
|  | PSE framework. | ||||||
| 
 | 
 | ||||||
| .. bibliography:: biblio.bib   | .. bibliography:: biblio.bib   | ||||||
| @@ -1,70 +0,0 @@ | |||||||
| gnpy\.core package |  | ||||||
| ================== |  | ||||||
|  |  | ||||||
| Submodules |  | ||||||
| ---------- |  | ||||||
|  |  | ||||||
| gnpy\.core\.elements module |  | ||||||
| --------------------------- |  | ||||||
|  |  | ||||||
| .. automodule:: gnpy.core.elements |  | ||||||
|     :members: |  | ||||||
|     :undoc-members: |  | ||||||
|     :show-inheritance: |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
|     :members: |  | ||||||
|     :undoc-members: |  | ||||||
|     :show-inheritance: |  | ||||||
|  |  | ||||||
| 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: |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| gnpy package |  | ||||||
| ============ |  | ||||||
|  |  | ||||||
| Subpackages |  | ||||||
| ----------- |  | ||||||
|  |  | ||||||
| .. toctree:: |  | ||||||
|  |  | ||||||
|     gnpy.core |  | ||||||
|  |  | ||||||
| Module contents |  | ||||||
| --------------- |  | ||||||
|  |  | ||||||
| .. automodule:: gnpy |  | ||||||
|     :members: |  | ||||||
|     :undoc-members: |  | ||||||
|     :show-inheritance: |  | ||||||
| @@ -1,7 +0,0 @@ | |||||||
| gnpy |  | ||||||
| ==== |  | ||||||
|  |  | ||||||
| .. toctree:: |  | ||||||
|    :maxdepth: 4 |  | ||||||
|  |  | ||||||
|    gnpy |  | ||||||
										
											Binary file not shown.
										
									
								
							| @@ -1,11 +0,0 @@ | |||||||
| REGIONS = europe asia conus |  | ||||||
| TARGETS = $(foreach region,$(REGIONS),coronet.$(region).json) |  | ||||||
|  |  | ||||||
| all: $(TARGETS) |  | ||||||
|  |  | ||||||
| $(TARGETS): convert.py CORONET_Global_Topology.xls |  | ||||||
| 	python $< -f $(subst .json,,$(subst coronet.,,$@)) > $@ |  | ||||||
|  |  | ||||||
| .PHONY: clean |  | ||||||
| clean: |  | ||||||
| 	-rm $(TARGETS) -f |  | ||||||
| @@ -1,143 +0,0 @@ | |||||||
| { |  | ||||||
|   "networks": { |  | ||||||
|     "network": [ |  | ||||||
|       { |  | ||||||
|         "network-types": { |  | ||||||
|           "tip-oopt-pse": {} |  | ||||||
|         }, |  | ||||||
|         "network-id": "pt-to-pt", |  | ||||||
|         "node": [ |  | ||||||
|           { |  | ||||||
|             "node-id": "M_KMA", |  | ||||||
|             "type":"roadm", |  | ||||||
|             "termination-point": [ |  | ||||||
|               { |  | ||||||
|                 "tp-id": "1-2-1" |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "node-id": "T_CAS", |  | ||||||
|             "type":"roadm", |  | ||||||
|             "termination-point": [ |  | ||||||
|               { |  | ||||||
|                 "tp-id": "2-1-1" |  | ||||||
|               }, |  | ||||||
|               { |  | ||||||
|                 "tp-id": "2-3-1" |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "node-id": "LA", |  | ||||||
|             "type":"ila", |  | ||||||
|             "termination-point": [ |  | ||||||
|               { |  | ||||||
|                 "tp-id": "3-2-1" |  | ||||||
|               }, |  | ||||||
|               { |  | ||||||
|                 "tp-id": "3-4-1" |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "node-id": "SR", |  | ||||||
|             "type":"fused", |  | ||||||
|             "termination-point": [ |  | ||||||
|               { |  | ||||||
|                 "tp-id": "4-3-1" |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "node-id": "C", |  | ||||||
|             "type":"ila", |  | ||||||
|             "termination-point": [ |  | ||||||
|               { |  | ||||||
|                 "tp-id": "5-6-1" |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "node-id": "N_KBE", |  | ||||||
|             "type":"roadm", |  | ||||||
|             "termination-point": [ |  | ||||||
|               { |  | ||||||
|                 "tp-id": "6-5-1" |  | ||||||
|               }, |  | ||||||
|               { |  | ||||||
|                 "tp-id": "6-7-1" |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "node-id": "N_KBA", |  | ||||||
|             "type":"roadm", |  | ||||||
|             "termination-point": [ |  | ||||||
|               { |  | ||||||
|                 "tp-id": "7-6-1" |  | ||||||
|               } |  | ||||||
|             ] |  | ||||||
|           } |  | ||||||
|         ], |  | ||||||
|         "link": [ |  | ||||||
|           { |  | ||||||
|             "link-id": "M_KMA,1-2-1,T_CAS,2-1-1", |  | ||||||
|             "source": { |  | ||||||
|               "source-node": "M_KMA", |  | ||||||
|               "source-tp": "1-2-1" |  | ||||||
|             } |  | ||||||
|             "destination": { |  | ||||||
|               "dest-node": "T_CAS", |  | ||||||
|               "dest-tp": "2-1-1" |  | ||||||
|             } |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "link-id": "T_CAS,2-3-1,LA,3-2-1", |  | ||||||
|             "source": { |  | ||||||
|               "source-node": "T_CAS", |  | ||||||
|               "source-tp": "2-3-1" |  | ||||||
|             } |  | ||||||
|             "destination": { |  | ||||||
|               "dest-node": "LA", |  | ||||||
|               "dest-tp": "3-2-1" |  | ||||||
|             } |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "link-id": "LA,3-4-1,SR,4-3-1", |  | ||||||
|             "source": { |  | ||||||
|               "source-node": "LA", |  | ||||||
|               "source-tp": "3-4-1" |  | ||||||
|             } |  | ||||||
|             "destination": { |  | ||||||
|               "dest-node": "SR", |  | ||||||
|               "dest-tp": "4-3-1" |  | ||||||
|             } |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "link-id": "C,5-6-1,N_KBE,6-5-1", |  | ||||||
|             "source": { |  | ||||||
|               "source-node": "C", |  | ||||||
|               "source-tp": "5-6-1" |  | ||||||
|             } |  | ||||||
|             "destination": { |  | ||||||
|               "dest-node": "N_KBE", |  | ||||||
|               "dest-tp": "6-5-1" |  | ||||||
|             } |  | ||||||
|           }, |  | ||||||
|           { |  | ||||||
|             "link-id": "N_KBE,6-7-1,N_KBA,7-6-1", |  | ||||||
|             "source": { |  | ||||||
|               "source-node": "N_KBE", |  | ||||||
|               "source-tp": "6-7-1" |  | ||||||
|             } |  | ||||||
|             "destination": { |  | ||||||
|               "dest-node": "N_KBA", |  | ||||||
|               "dest-tp": "7-6-1" |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|         ] |  | ||||||
|       } |  | ||||||
|     ] |  | ||||||
|   } |  | ||||||
| } |  | ||||||
| @@ -1,157 +0,0 @@ | |||||||
| { |  | ||||||
| "network_name": "pt to pt", |  | ||||||
| "nodes_elements":  |  | ||||||
| [ |  | ||||||
| 	{ |  | ||||||
|         "id":"M_KMA", |  | ||||||
|         "type":"ROADM", |  | ||||||
|         "metadata": { |  | ||||||
|         	"city":"M", |  | ||||||
|         	"region":"RLD", |  | ||||||
|         	"latitude":0, |  | ||||||
|         	"longitude":0 |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "id":"T_CAS", |  | ||||||
|         "type":"ROADM", |  | ||||||
|         "metadata": { |  | ||||||
|         	"city":"T", |  | ||||||
|         	"region":"RLD", |  | ||||||
|         	"latitude":0, |  | ||||||
|         	"longitude":0 |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "id":"LA", |  | ||||||
|         "type":"ILA", |  | ||||||
|         "metadata": { |  | ||||||
|         	"city":"LA", |  | ||||||
|         	"region":"RLD", |  | ||||||
|         	"latitude":0, |  | ||||||
|         	"longitude":0 |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "id":"SR", |  | ||||||
|         "type":"fused", |  | ||||||
|         "metadata": { |  | ||||||
|         	"city":"SR", |  | ||||||
|         	"region":"RLD", |  | ||||||
|         	"latitude":0, |  | ||||||
|         	"longitude":0 |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "id":"C", |  | ||||||
|         "type":"ILA", |  | ||||||
|         "metadata": { |  | ||||||
|         	"city":"C", |  | ||||||
|         	"region":"RLD", |  | ||||||
|         	"latitude":0, |  | ||||||
|         	"longitude":0 |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "id":"N_KBE", |  | ||||||
|         "type":"ROADM", |  | ||||||
|         "metadata": { |  | ||||||
|         	"city":"N", |  | ||||||
|         	"region":"RLD", |  | ||||||
|         	"latitude":0, |  | ||||||
|         	"longitude":0 |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|         "id":"N_KBA", |  | ||||||
|         "type":"ROADM", |  | ||||||
|         "metadata": { |  | ||||||
|         	"city":"N", |  | ||||||
|         	"region":"RLD", |  | ||||||
|         	"latitude":0, |  | ||||||
|         	"longitude":0 |  | ||||||
|         } |  | ||||||
|     }, |  | ||||||
| ], |  | ||||||
| "OTS_elements":[ |  | ||||||
| 	{ |  | ||||||
| 		"id":1, |  | ||||||
| 		"source_id":"M_KMA", |  | ||||||
| 		"dest_id":"T_CAS", |  | ||||||
| 		"parameters_cable":{ |  | ||||||
| 			"units":"km", |  | ||||||
| 			"length":60, |  | ||||||
| 			"id":"F060", |  | ||||||
| 			"type":"G652" |  | ||||||
| 		}, |  | ||||||
| 		"parameters_east":{ |  | ||||||
| 			"con_in":0.5, |  | ||||||
| 			"con_out":0.5, |  | ||||||
| 			"loss":16, |  | ||||||
| 			"pmd":2, |  | ||||||
| 			"fo_id":5 |  | ||||||
| 		}, |  | ||||||
| 		"parameters_west":{ |  | ||||||
| 			"con_in":0.5, |  | ||||||
| 			"con_out":0.5, |  | ||||||
| 			"loss":15, |  | ||||||
| 			"pmd":2, |  | ||||||
| 			"fo_id":6			 |  | ||||||
| 		} |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		"id":2, |  | ||||||
| 		"source_id":"T_CAS", |  | ||||||
| 		"dest_id":"LA", |  | ||||||
| 		"parameters_cable":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_east":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_west":{ |  | ||||||
| 		} |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		"id":3, |  | ||||||
| 		"source_id":"LA", |  | ||||||
| 		"dest_id":"SR", |  | ||||||
| 		"parameters_cable":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_east":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_west":{ |  | ||||||
| 		} |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		"id":3, |  | ||||||
| 		"source_id":"SR", |  | ||||||
| 		"dest_id":"C", |  | ||||||
| 		"parameters_cable":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_east":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_west":{ |  | ||||||
| 		} |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		"id":5, |  | ||||||
| 		"source_id":"C", |  | ||||||
| 		"dest_id":"N_KBE", |  | ||||||
| 		"parameters_cable":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_east":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_west":{ |  | ||||||
| 		} |  | ||||||
| 	}, |  | ||||||
| 	{ |  | ||||||
| 		"id":6, |  | ||||||
| 		"source_id":"N_KBE", |  | ||||||
| 		"dest_id":"N_KBA", |  | ||||||
| 		"parameters_cable":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_east":{ |  | ||||||
| 		}, |  | ||||||
| 		"parameters_west":{ |  | ||||||
| 		} |  | ||||||
| 	},							 |  | ||||||
| ]} |  | ||||||
| @@ -1,162 +0,0 @@ | |||||||
| #!/usr/bin/env python3 |  | ||||||
|  |  | ||||||
| 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, Counter |  | ||||||
| from itertools import chain |  | ||||||
| from json import dumps |  | ||||||
| from uuid import uuid4 |  | ||||||
| import math |  | ||||||
| import numpy as np |  | ||||||
|  |  | ||||||
| output_json_file_name = 'coronet_conus_example.json' |  | ||||||
| Node = namedtuple('Node', 'city state country region latitude longitude') |  | ||||||
| class Link(namedtuple('Link', 'from_city to_city distance distance_units')): |  | ||||||
|     def __new__(cls, from_city, to_city, distance, distance_units='km'): |  | ||||||
|         return super().__new__(cls, from_city, to_city, distance, distance_units) |  | ||||||
|  |  | ||||||
| def define_span_range(min_span, max_span, nspans): |  | ||||||
|     srange = (max_span - min_span) + min_span*np.random.rand(nspans) |  | ||||||
|     return srange |  | ||||||
|  |  | ||||||
| def amp_spacings(min_span,max_span,length): |  | ||||||
|     nspans =  math.ceil(length/100) |  | ||||||
|     spans = define_span_range(min_span, max_span, nspans) |  | ||||||
|     tot = spans.sum() |  | ||||||
|     delta = length -tot |  | ||||||
|     if delta > 0 and delta < 25: |  | ||||||
|         ind  = np.where(np.min(spans)) |  | ||||||
|         spans[ind] = spans[ind] + delta |  | ||||||
|     elif delta >= 25 and delta < 40: |  | ||||||
|         spans = spans + delta/float(nspans) |  | ||||||
|     elif delta > 40 and delta < 100: |  | ||||||
|         spans = np.append(spans,delta) |  | ||||||
|     elif delta > 100: |  | ||||||
|         spans  = np.append(spans, [delta/2, delta/2]) |  | ||||||
|     elif delta < 0: |  | ||||||
|         spans = spans + delta/float(nspans) |  | ||||||
|     return list(spans) |  | ||||||
|  |  | ||||||
| def parse_excel(args): |  | ||||||
|     with open_workbook(args.workbook) as wb: |  | ||||||
|         nodes_sheet = wb.sheet_by_name('Nodes') |  | ||||||
|         links_sheet = wb.sheet_by_name('Links') |  | ||||||
|  |  | ||||||
|         # 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))) |  | ||||||
|  |  | ||||||
|         # sanity check |  | ||||||
|         header = [x.value.strip() for x in links_sheet.row(4)] |  | ||||||
|         expected = ['Node A', 'Node Z', 'Distance (km)'] |  | ||||||
|         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))) |  | ||||||
|  |  | ||||||
|     # 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.') |  | ||||||
|  |  | ||||||
|     return nodes, links |  | ||||||
|  |  | ||||||
| parser = ArgumentParser() |  | ||||||
| parser.add_argument('workbook', nargs='?', default='CORONET_Global_Topology.xls') |  | ||||||
| parser.add_argument('-f', '--filter-region', action='append', default=[]) |  | ||||||
|  |  | ||||||
| all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) |  | ||||||
|  |  | ||||||
| def midpoint(city_a, city_b): |  | ||||||
|     lats  = city_a.latitude, city_b.latitude |  | ||||||
|     longs = city_a.longitude, city_b.longitude |  | ||||||
|     return { |  | ||||||
|         'latitude':  sum(lats)  / 2, |  | ||||||
|         'longitude': sum(longs) / 2, |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     args = parser.parse_args() |  | ||||||
|     nodes, links = parse_excel(args) |  | ||||||
|  |  | ||||||
|     if args.filter_region: |  | ||||||
|         nodes = [n for n in nodes if n.region.lower() in args.filter_region] |  | ||||||
|         cities = {n.city for n in nodes} |  | ||||||
|         links = [lnk for lnk in links if lnk.from_city in cities and |  | ||||||
|                                          lnk.to_city in cities] |  | ||||||
|         cities = {lnk.from_city for lnk in links} | {lnk.to_city for lnk in links} |  | ||||||
|         nodes = [n for n in nodes if n.city in cities] |  | ||||||
|  |  | ||||||
|     nodes_by_city = {n.city: n for n in nodes} |  | ||||||
|  |  | ||||||
|     data = { |  | ||||||
|         'elements': |  | ||||||
|             [{'uid': f'trx {x.city}', |  | ||||||
|               'metadata': {'location': {'city':      x.city, |  | ||||||
|                                         'region':    x.region, |  | ||||||
|                                         'latitude':  x.latitude, |  | ||||||
|                                         'longitude': x.longitude}}, |  | ||||||
|               'type': 'Transceiver'} |  | ||||||
|              for x in nodes] + |  | ||||||
|             [{'uid': f'roadm {x.city}', |  | ||||||
|               'metadata': {'location': {'city':      x.city, |  | ||||||
|                                         'region':    x.region, |  | ||||||
|                                         'latitude':  x.latitude, |  | ||||||
|                                         'longitude': x.longitude}}, |  | ||||||
|               'type': 'Roadm'} |  | ||||||
|              for x in nodes] +              |  | ||||||
|             [{'uid': f'fiber ({x.from_city} → {x.to_city})', |  | ||||||
|               'metadata': {'location': midpoint(nodes_by_city[x.from_city], |  | ||||||
|                                                 nodes_by_city[x.to_city])}, |  | ||||||
|               'type': 'Fiber', |  | ||||||
|               'params': {'length':   round(x.distance, 3), |  | ||||||
|                          'length_units':    x.distance_units, |  | ||||||
|                          'loss_coef': 0.2, |  | ||||||
|                          'dispersion': 16.7E-6, |  | ||||||
|                          'gamma': 1.27E-3} |  | ||||||
|               } |  | ||||||
|              for x in links], |  | ||||||
|         'connections': |  | ||||||
|             list(chain.from_iterable(zip( # put bidi next to each other |  | ||||||
|             [{'from_node': f'roadm {x.from_city}', |  | ||||||
|               'to_node':   f'fiber ({x.from_city} → {x.to_city})'} |  | ||||||
|              for x in links], |  | ||||||
|             [{'from_node': f'fiber ({x.from_city} → {x.to_city})', |  | ||||||
|               'to_node':   f'roadm {x.from_city}'} |  | ||||||
|              for x in links]))) |  | ||||||
|             + |  | ||||||
|             list(chain.from_iterable(zip( |  | ||||||
|             [{'from_node': f'fiber ({x.from_city} → {x.to_city})', |  | ||||||
|               'to_node':   f'roadm {x.to_city}'} |  | ||||||
|              for x in links], |  | ||||||
|             [{'from_node': f'roadm {x.to_city}', |  | ||||||
|               'to_node':   f'fiber ({x.from_city} → {x.to_city})'} |  | ||||||
|              for x in links]))) |  | ||||||
|             + |  | ||||||
|             list(chain.from_iterable(zip( |  | ||||||
|             [{'from_node': f'trx {x.city}', |  | ||||||
|               'to_node':   f'roadm {x.city}'} |  | ||||||
|              for x in nodes], |  | ||||||
|             [{'from_node': f'roadm {x.city}', |  | ||||||
|               'to_node':   f'trx {x.city}'} |  | ||||||
|              for x in nodes])))             |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     print(dumps(data, indent=2)) |  | ||||||
|     with  open(output_json_file_name,'w') as edfa_json_file: |  | ||||||
|         edfa_json_file.write(dumps(data, indent=2)) |  | ||||||
| @@ -1,542 +0,0 @@ | |||||||
| { |  | ||||||
|   "elements": [ |  | ||||||
|     { |  | ||||||
|       "uid": "Bangkok", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Bangkok", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 13.73, |  | ||||||
|           "longitude": 100.5 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Beijing", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Beijing", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 39.92999979, |  | ||||||
|           "longitude": 116.4000013 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Delhi", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Delhi", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 28.6700003, |  | ||||||
|           "longitude": 77.2099989 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Hong_Kong", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Hong_Kong", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 22.267, |  | ||||||
|           "longitude": 114.14 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Honolulu", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Honolulu", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 21.3199996, |  | ||||||
|           "longitude": -157.800003 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Mumbai", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Mumbai", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 18.9599987, |  | ||||||
|           "longitude": 72.8199999 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Seoul", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Seoul", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 37.56000108, |  | ||||||
|           "longitude": 126.9899988 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Shanghai", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Shanghai", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 31.23, |  | ||||||
|           "longitude": 121.47 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Singapore", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Singapore", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 1.299999907, |  | ||||||
|           "longitude": 103.8499992 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Sydney", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Sydney", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": -33.86999896, |  | ||||||
|           "longitude": 151.2100066 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Taipei", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Taipei", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 25.0200005, |  | ||||||
|           "longitude": 121.449997 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Tokyo", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Tokyo", |  | ||||||
|           "region": "Asia", |  | ||||||
|           "latitude": 35.6699986, |  | ||||||
|           "longitude": 139.770004 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Bangkok \u2192 Delhi)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 3505.95, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 21.20000015, |  | ||||||
|           "longitude": 88.85499945000001 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Bangkok \u2192 Hong_Kong)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 2070.724, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 17.9985, |  | ||||||
|           "longitude": 107.32 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Beijing \u2192 Seoul)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 1146.124, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 38.745000434999994, |  | ||||||
|           "longitude": 121.69500005 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Beijing \u2192 Shanghai)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 1284.465, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 35.579999895, |  | ||||||
|           "longitude": 118.93500065 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Delhi \u2192 Mumbai)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 1402.141, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 23.8149995, |  | ||||||
|           "longitude": 75.0149994 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Hong_Kong \u2192 Shanghai)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 1480.406, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 26.7485, |  | ||||||
|           "longitude": 117.805 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Hong_Kong \u2192 Sydney)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 8856.6, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": -5.801499479999999, |  | ||||||
|           "longitude": 132.67500330000001 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Hong_Kong \u2192 Taipei)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 966.177, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 23.64350025, |  | ||||||
|           "longitude": 117.79499849999999 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Honolulu \u2192 Sydney)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 9808.616, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": -6.274999679999999, |  | ||||||
|           "longitude": -3.294998199999995 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Honolulu \u2192 Taipei)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 9767.013, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 23.17000005, |  | ||||||
|           "longitude": -18.175003000000004 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Mumbai \u2192 Singapore)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 4692.708, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 10.1299993035, |  | ||||||
|           "longitude": 88.33499954999999 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Seoul \u2192 Tokyo)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 1391.085, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 36.614999839999996, |  | ||||||
|           "longitude": 133.3800014 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Singapore \u2192 Sydney)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 7562.331, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": -16.2849995265, |  | ||||||
|           "longitude": 127.5300029 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Taipei \u2192 Tokyo)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 2537.345, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 30.344999549999997, |  | ||||||
|           "longitude": 130.6100005 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     } |  | ||||||
|   ], |  | ||||||
|   "connections": [ |  | ||||||
|     { |  | ||||||
|       "from_node": "Bangkok", |  | ||||||
|       "to_node": "fiber (Bangkok \u2192 Delhi)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Bangkok \u2192 Delhi)", |  | ||||||
|       "to_node": "Bangkok" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Bangkok", |  | ||||||
|       "to_node": "fiber (Bangkok \u2192 Hong_Kong)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Bangkok \u2192 Hong_Kong)", |  | ||||||
|       "to_node": "Bangkok" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Beijing", |  | ||||||
|       "to_node": "fiber (Beijing \u2192 Seoul)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Beijing \u2192 Seoul)", |  | ||||||
|       "to_node": "Beijing" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Beijing", |  | ||||||
|       "to_node": "fiber (Beijing \u2192 Shanghai)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Beijing \u2192 Shanghai)", |  | ||||||
|       "to_node": "Beijing" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Delhi", |  | ||||||
|       "to_node": "fiber (Delhi \u2192 Mumbai)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Delhi \u2192 Mumbai)", |  | ||||||
|       "to_node": "Delhi" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Hong_Kong", |  | ||||||
|       "to_node": "fiber (Hong_Kong \u2192 Shanghai)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Hong_Kong \u2192 Shanghai)", |  | ||||||
|       "to_node": "Hong_Kong" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Hong_Kong", |  | ||||||
|       "to_node": "fiber (Hong_Kong \u2192 Sydney)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Hong_Kong \u2192 Sydney)", |  | ||||||
|       "to_node": "Hong_Kong" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Hong_Kong", |  | ||||||
|       "to_node": "fiber (Hong_Kong \u2192 Taipei)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Hong_Kong \u2192 Taipei)", |  | ||||||
|       "to_node": "Hong_Kong" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Honolulu", |  | ||||||
|       "to_node": "fiber (Honolulu \u2192 Sydney)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Honolulu \u2192 Sydney)", |  | ||||||
|       "to_node": "Honolulu" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Honolulu", |  | ||||||
|       "to_node": "fiber (Honolulu \u2192 Taipei)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Honolulu \u2192 Taipei)", |  | ||||||
|       "to_node": "Honolulu" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Mumbai", |  | ||||||
|       "to_node": "fiber (Mumbai \u2192 Singapore)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Mumbai \u2192 Singapore)", |  | ||||||
|       "to_node": "Mumbai" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Seoul", |  | ||||||
|       "to_node": "fiber (Seoul \u2192 Tokyo)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Seoul \u2192 Tokyo)", |  | ||||||
|       "to_node": "Seoul" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Singapore", |  | ||||||
|       "to_node": "fiber (Singapore \u2192 Sydney)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Singapore \u2192 Sydney)", |  | ||||||
|       "to_node": "Singapore" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Taipei", |  | ||||||
|       "to_node": "fiber (Taipei \u2192 Tokyo)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Taipei \u2192 Tokyo)", |  | ||||||
|       "to_node": "Taipei" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Bangkok \u2192 Delhi)", |  | ||||||
|       "to_node": "Delhi" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Delhi", |  | ||||||
|       "to_node": "fiber (Bangkok \u2192 Delhi)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Bangkok \u2192 Hong_Kong)", |  | ||||||
|       "to_node": "Hong_Kong" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Hong_Kong", |  | ||||||
|       "to_node": "fiber (Bangkok \u2192 Hong_Kong)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Beijing \u2192 Seoul)", |  | ||||||
|       "to_node": "Seoul" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Seoul", |  | ||||||
|       "to_node": "fiber (Beijing \u2192 Seoul)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Beijing \u2192 Shanghai)", |  | ||||||
|       "to_node": "Shanghai" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Shanghai", |  | ||||||
|       "to_node": "fiber (Beijing \u2192 Shanghai)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Delhi \u2192 Mumbai)", |  | ||||||
|       "to_node": "Mumbai" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Mumbai", |  | ||||||
|       "to_node": "fiber (Delhi \u2192 Mumbai)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Hong_Kong \u2192 Shanghai)", |  | ||||||
|       "to_node": "Shanghai" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Shanghai", |  | ||||||
|       "to_node": "fiber (Hong_Kong \u2192 Shanghai)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Hong_Kong \u2192 Sydney)", |  | ||||||
|       "to_node": "Sydney" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Sydney", |  | ||||||
|       "to_node": "fiber (Hong_Kong \u2192 Sydney)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Hong_Kong \u2192 Taipei)", |  | ||||||
|       "to_node": "Taipei" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Taipei", |  | ||||||
|       "to_node": "fiber (Hong_Kong \u2192 Taipei)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Honolulu \u2192 Sydney)", |  | ||||||
|       "to_node": "Sydney" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Sydney", |  | ||||||
|       "to_node": "fiber (Honolulu \u2192 Sydney)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Honolulu \u2192 Taipei)", |  | ||||||
|       "to_node": "Taipei" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Taipei", |  | ||||||
|       "to_node": "fiber (Honolulu \u2192 Taipei)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Mumbai \u2192 Singapore)", |  | ||||||
|       "to_node": "Singapore" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Singapore", |  | ||||||
|       "to_node": "fiber (Mumbai \u2192 Singapore)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Seoul \u2192 Tokyo)", |  | ||||||
|       "to_node": "Tokyo" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Tokyo", |  | ||||||
|       "to_node": "fiber (Seoul \u2192 Tokyo)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Singapore \u2192 Sydney)", |  | ||||||
|       "to_node": "Sydney" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Sydney", |  | ||||||
|       "to_node": "fiber (Singapore \u2192 Sydney)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Taipei \u2192 Tokyo)", |  | ||||||
|       "to_node": "Tokyo" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Tokyo", |  | ||||||
|       "to_node": "fiber (Taipei \u2192 Tokyo)" |  | ||||||
|     } |  | ||||||
|   ] |  | ||||||
| } |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,582 +0,0 @@ | |||||||
| { |  | ||||||
|   "elements": [ |  | ||||||
|     { |  | ||||||
|       "uid": "Amsterdam", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Amsterdam", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 52.3699996, |  | ||||||
|           "longitude": 4.88999915 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Berlin", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Berlin", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 52.520002, |  | ||||||
|           "longitude": 13.379995 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Brussels", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Brussels", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 50.830002, |  | ||||||
|           "longitude": 4.330002 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Bucharest", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Bucharest", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 44.44, |  | ||||||
|           "longitude": 26.1 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Frankfurt", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Frankfurt", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 50.1199992, |  | ||||||
|           "longitude": 8.68000104 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Istanbul", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Istanbul", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 41.1, |  | ||||||
|           "longitude": 29.0 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "London", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "London", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 51.5200005, |  | ||||||
|           "longitude": -0.100000296 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Madrid", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Madrid", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 40.419998, |  | ||||||
|           "longitude": -3.7100002 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Paris", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Paris", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 48.86, |  | ||||||
|           "longitude": 2.3399995 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Rome", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Rome", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 41.8899996, |  | ||||||
|           "longitude": 12.5000004 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Vienna", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Vienna", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 48.2200024, |  | ||||||
|           "longitude": 16.3700005 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Warsaw", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Warsaw", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 52.2599987, |  | ||||||
|           "longitude": 21.0200005 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "Zurich", |  | ||||||
|       "metadata": { |  | ||||||
|         "location": { |  | ||||||
|           "city": "Zurich", |  | ||||||
|           "region": "Europe", |  | ||||||
|           "latitude": 47.3800015, |  | ||||||
|           "longitude": 8.5399996 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Transceiver" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Amsterdam \u2192 Berlin)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 690.608, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 52.4450008, |  | ||||||
|           "longitude": 9.134997075 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Amsterdam \u2192 Brussels)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 210.729, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 51.600000800000004, |  | ||||||
|           "longitude": 4.610000575000001 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Amsterdam \u2192 Frankfurt)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 436.324, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 51.2449994, |  | ||||||
|           "longitude": 6.785000095000001 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Berlin \u2192 Warsaw)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 623.015, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 52.390000349999994, |  | ||||||
|           "longitude": 17.199997749999998 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Brussels \u2192 London)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 381.913, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 51.17500125, |  | ||||||
|           "longitude": 2.115000852 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Bucharest \u2192 Istanbul)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 528.58, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 42.769999999999996, |  | ||||||
|           "longitude": 27.55 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Bucharest \u2192 Warsaw)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 1136.2, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 48.34999935, |  | ||||||
|           "longitude": 23.56000025 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Frankfurt \u2192 Vienna)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 717.001, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 49.1700008, |  | ||||||
|           "longitude": 12.52500077 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Istanbul \u2192 Rome)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 1650.406, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 41.4949998, |  | ||||||
|           "longitude": 20.7500002 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (London \u2192 Paris)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 411.692, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 50.19000025, |  | ||||||
|           "longitude": 1.1199996019999998 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Madrid \u2192 Paris)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 1263.619, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 44.639999, |  | ||||||
|           "longitude": -0.6850003500000001 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Madrid \u2192 Zurich)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 1497.358, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 43.89999975, |  | ||||||
|           "longitude": 2.4149997 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Rome \u2192 Vienna)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 920.026, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 45.055001000000004, |  | ||||||
|           "longitude": 14.43500045 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Rome \u2192 Zurich)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 823.4, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 44.63500055, |  | ||||||
|           "longitude": 10.52 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "uid": "fiber (Vienna \u2192 Warsaw)", |  | ||||||
|       "metadata": { |  | ||||||
|         "length": 669.297, |  | ||||||
|         "units": "km", |  | ||||||
|         "location": { |  | ||||||
|           "latitude": 50.24000055, |  | ||||||
|           "longitude": 18.6950005 |  | ||||||
|         } |  | ||||||
|       }, |  | ||||||
|       "type": "Fiber" |  | ||||||
|     } |  | ||||||
|   ], |  | ||||||
|   "connections": [ |  | ||||||
|     { |  | ||||||
|       "from_node": "Amsterdam", |  | ||||||
|       "to_node": "fiber (Amsterdam \u2192 Berlin)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Amsterdam \u2192 Berlin)", |  | ||||||
|       "to_node": "Amsterdam" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Amsterdam", |  | ||||||
|       "to_node": "fiber (Amsterdam \u2192 Brussels)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Amsterdam \u2192 Brussels)", |  | ||||||
|       "to_node": "Amsterdam" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Amsterdam", |  | ||||||
|       "to_node": "fiber (Amsterdam \u2192 Frankfurt)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Amsterdam \u2192 Frankfurt)", |  | ||||||
|       "to_node": "Amsterdam" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Berlin", |  | ||||||
|       "to_node": "fiber (Berlin \u2192 Warsaw)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Berlin \u2192 Warsaw)", |  | ||||||
|       "to_node": "Berlin" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Brussels", |  | ||||||
|       "to_node": "fiber (Brussels \u2192 London)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Brussels \u2192 London)", |  | ||||||
|       "to_node": "Brussels" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Bucharest", |  | ||||||
|       "to_node": "fiber (Bucharest \u2192 Istanbul)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Bucharest \u2192 Istanbul)", |  | ||||||
|       "to_node": "Bucharest" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Bucharest", |  | ||||||
|       "to_node": "fiber (Bucharest \u2192 Warsaw)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Bucharest \u2192 Warsaw)", |  | ||||||
|       "to_node": "Bucharest" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Frankfurt", |  | ||||||
|       "to_node": "fiber (Frankfurt \u2192 Vienna)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Frankfurt \u2192 Vienna)", |  | ||||||
|       "to_node": "Frankfurt" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Istanbul", |  | ||||||
|       "to_node": "fiber (Istanbul \u2192 Rome)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Istanbul \u2192 Rome)", |  | ||||||
|       "to_node": "Istanbul" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "London", |  | ||||||
|       "to_node": "fiber (London \u2192 Paris)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (London \u2192 Paris)", |  | ||||||
|       "to_node": "London" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Madrid", |  | ||||||
|       "to_node": "fiber (Madrid \u2192 Paris)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Madrid \u2192 Paris)", |  | ||||||
|       "to_node": "Madrid" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Madrid", |  | ||||||
|       "to_node": "fiber (Madrid \u2192 Zurich)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Madrid \u2192 Zurich)", |  | ||||||
|       "to_node": "Madrid" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Rome", |  | ||||||
|       "to_node": "fiber (Rome \u2192 Vienna)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Rome \u2192 Vienna)", |  | ||||||
|       "to_node": "Rome" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Rome", |  | ||||||
|       "to_node": "fiber (Rome \u2192 Zurich)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Rome \u2192 Zurich)", |  | ||||||
|       "to_node": "Rome" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Vienna", |  | ||||||
|       "to_node": "fiber (Vienna \u2192 Warsaw)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Vienna \u2192 Warsaw)", |  | ||||||
|       "to_node": "Vienna" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Amsterdam \u2192 Berlin)", |  | ||||||
|       "to_node": "Berlin" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Berlin", |  | ||||||
|       "to_node": "fiber (Amsterdam \u2192 Berlin)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Amsterdam \u2192 Brussels)", |  | ||||||
|       "to_node": "Brussels" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Brussels", |  | ||||||
|       "to_node": "fiber (Amsterdam \u2192 Brussels)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Amsterdam \u2192 Frankfurt)", |  | ||||||
|       "to_node": "Frankfurt" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Frankfurt", |  | ||||||
|       "to_node": "fiber (Amsterdam \u2192 Frankfurt)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Berlin \u2192 Warsaw)", |  | ||||||
|       "to_node": "Warsaw" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Warsaw", |  | ||||||
|       "to_node": "fiber (Berlin \u2192 Warsaw)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Brussels \u2192 London)", |  | ||||||
|       "to_node": "London" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "London", |  | ||||||
|       "to_node": "fiber (Brussels \u2192 London)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Bucharest \u2192 Istanbul)", |  | ||||||
|       "to_node": "Istanbul" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Istanbul", |  | ||||||
|       "to_node": "fiber (Bucharest \u2192 Istanbul)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Bucharest \u2192 Warsaw)", |  | ||||||
|       "to_node": "Warsaw" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Warsaw", |  | ||||||
|       "to_node": "fiber (Bucharest \u2192 Warsaw)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Frankfurt \u2192 Vienna)", |  | ||||||
|       "to_node": "Vienna" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Vienna", |  | ||||||
|       "to_node": "fiber (Frankfurt \u2192 Vienna)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Istanbul \u2192 Rome)", |  | ||||||
|       "to_node": "Rome" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Rome", |  | ||||||
|       "to_node": "fiber (Istanbul \u2192 Rome)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (London \u2192 Paris)", |  | ||||||
|       "to_node": "Paris" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Paris", |  | ||||||
|       "to_node": "fiber (London \u2192 Paris)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Madrid \u2192 Paris)", |  | ||||||
|       "to_node": "Paris" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Paris", |  | ||||||
|       "to_node": "fiber (Madrid \u2192 Paris)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Madrid \u2192 Zurich)", |  | ||||||
|       "to_node": "Zurich" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Zurich", |  | ||||||
|       "to_node": "fiber (Madrid \u2192 Zurich)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Rome \u2192 Vienna)", |  | ||||||
|       "to_node": "Vienna" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Vienna", |  | ||||||
|       "to_node": "fiber (Rome \u2192 Vienna)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Rome \u2192 Zurich)", |  | ||||||
|       "to_node": "Zurich" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Zurich", |  | ||||||
|       "to_node": "fiber (Rome \u2192 Zurich)" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "fiber (Vienna \u2192 Warsaw)", |  | ||||||
|       "to_node": "Warsaw" |  | ||||||
|     }, |  | ||||||
|     { |  | ||||||
|       "from_node": "Warsaw", |  | ||||||
|       "to_node": "fiber (Vienna \u2192 Warsaw)" |  | ||||||
|     } |  | ||||||
|   ] |  | ||||||
| } |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -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,9 +0,0 @@ | |||||||
| { |  | ||||||
|     "params": { |  | ||||||
|         "dfg": [25.13596985, 25.11822814, 25.09542133, 25.06245771, 25.02602765, 24.99637953, 24.98167255, 24.97530668, 24.98320726, 24.99718565, 25.01757247, 25.03832781, 25.05495585, 25.0670719, 25.07091411, 25.07094365, 25.07114324, 25.07533627, 25.08731018, 25.10313936, 25.12276204, 25.14239479, 25.15945633, 25.17392704, 25.17673767, 25.17037141, 25.15216254, 25.1311431, 25.10802335, 25.08548777, 25.06916675, 25.05848176, 25.05447313, 25.05154441, 25.04946059, 25.04717849, 25.04551656, 25.04467649, 25.0407292, 25.03285408, 25.0234883, 25.01659234, 25.01332136, 25.01123434, 25.01030015, 25.00936548, 25.00873964, 25.00842535, 25.00696466, 25.0040431, 25.00070998, 24.9984232, 24.99306332, 24.98352421, 24.97125103, 24.96038108, 24.94888721, 24.93531489, 24.92131927, 24.90898697, 24.89896514, 24.88958463, 24.8808387, 24.87210092, 24.86462026, 24.85839773, 24.85445838, 24.85155443, 24.85176601, 24.85408014, 24.85909624, 24.86474458, 24.87203486, 24.8803652, 24.88910669, 24.89721313, 24.90282604, 24.9065669, 24.9086508, 24.91093944, 24.91343079, 24.91592344, 24.92155351, 24.93031861, 24.94052812, 24.94904669, 24.95757123, 24.96781845, 24.98180093, 24.99782686, 25.01393183, 25.02809846, 25.04032575, 25.05256981, 25.06479701, 25.07704697], |  | ||||||
|         "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_fit_coeff": [0.000168241, 0.0469961, 0.0359549, 5.82851], |  | ||||||
|         "nf_ripple": [-0.315374332, -0.315374332, -0.3154009157100272, -0.3184914611751095, -0.32158358425400546, -0.3246772861549999, -0.32762368641496226, -0.3205413846123276, -0.31345546385118733, -0.3063659213569748, -0.29920267890990127, -0.27061972852631744, -0.24202215770774693, -0.21340995523361256, -0.18478227130158695, -0.14809761118389625, -0.11139416731807622, -0.07467192527357988, -0.038026748965679924, -0.019958469399422092, -0.0018809287980157928, 0.01620587996057356, 0.03430196400570967, 0.05240733047405406, 0.07052198650959736, 0.079578036683472, 0.08854664736190952, 0.0975198632319653, 0.10649768784154924, 0.0977413804499074, 0.08880343717266004, 0.07986089973284587, 0.0709137645874038, 0.06333589274056531, 0.055756212252058776, 0.04817263174786321, 0.04058514821716236, 0.03338159167571013, 0.026178308595650738, 0.018971315351761126, 0.011760609076833628, 0.01695029492275999, 0.02227499135770144, 0.02760243318910433, 0.03293262254079026, 0.038265561538776145, 0.04360125231127117, 0.03485699074348155, 0.025991055149117932, 0.017120541224980364, 0.008275758735920322, 0.0019423214065246042, -0.004394389017104359, -0.010734375072893196, -0.017077639301414434, -0.02467970289957285, -0.03229797040382168, -0.03992018009047725, -0.04753456632753024, -0.049234003141433724, -0.05093432003654719, -0.05263551769669225, -0.05433759680640246, -0.0560405580509193, -0.057718452237076875, -0.056840590379175944, -0.055962273198734966, -0.05508350034141658, -0.054204271452516814, -0.05839608872695511, -0.06262733016971533, -0.0668607690892037, -0.07090173625606945, -0.05209609730905224, -0.03328068412141294, -0.014455489070928059, 0.004315038757905716, 0.014839202394482527, 0.025368841662503576, 0.03590396083646565, 0.0464445641953214, 0.05699065602246746, 0.06754224060577406, 0.10002709623672751, 0.13258013095133617, 0.1651501336277331, 0.1977371175359939, 0.23194802687829724, 0.26618779883837107, 0.3004454365808535, 0.33472095409250663, 0.35929034770587287, 0.38384389188855605, 0.40841026111391787, 0.43298946543290784, 0.43298946543290784], |  | ||||||
|         "frequencies": [] |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,72 +0,0 @@ | |||||||
| #!/usr/bin/env python3 |  | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
|  |  | ||||||
| import matplotlib.pyplot as plt |  | ||||||
| import numpy as np |  | ||||||
|  |  | ||||||
| from gnpy.core.utils import (load_json, |  | ||||||
|                              itufs, |  | ||||||
|                              freq2wavelength, |  | ||||||
|                              lin2db, |  | ||||||
|                              db2lin) |  | ||||||
| from gnpy.core import network |  | ||||||
|  |  | ||||||
| topology = load_json('edfa_example_network.json') |  | ||||||
| nw = network.network_from_json(topology) |  | ||||||
| pch2d_legend_data = np.loadtxt('Pchan2DLegend.txt') |  | ||||||
| pch2d = np.loadtxt('Pchan2D.txt') |  | ||||||
|  |  | ||||||
| ch_spacing = 0.05 |  | ||||||
| fc = itufs(ch_spacing) |  | ||||||
| lc = freq2wavelength(fc) / 1000 |  | ||||||
| nchan = np.arange(len(lc)) |  | ||||||
| df = np.ones(len(lc)) * ch_spacing |  | ||||||
|  |  | ||||||
| edfa1 = [n for n in nw.nodes() if n.uid == 'Edfa1'][0] |  | ||||||
| edfa1.gain_target = 20.0 |  | ||||||
| edfa1.tilt_target = -0.7 |  | ||||||
| edfa1.calc_nf() |  | ||||||
|  |  | ||||||
| results = [] |  | ||||||
| for Pin in pch2d: |  | ||||||
|     chgain = edfa1.gain_profile(Pin) |  | ||||||
|     pase = edfa1.noise_profile(chgain, fc, df) |  | ||||||
|     pout = lin2db(db2lin(Pin + chgain) + db2lin(pase)) |  | ||||||
|     results.append(pout) |  | ||||||
|  |  | ||||||
| # Generate legend text |  | ||||||
|  |  | ||||||
| pch2d_legend = [] |  | ||||||
| for ea in pch2d_legend_data: |  | ||||||
|     s = ''.join([chr(xx) for xx in ea.astype(dtype=int)]).strip() |  | ||||||
|     pch2d_legend.append(s) |  | ||||||
|  |  | ||||||
| # 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() |  | ||||||
| for result in results: |  | ||||||
|     plt.plot(nchan, result, '.-', lw=2) |  | ||||||
| plt.title('Output Power w/ 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() |  | ||||||
| @@ -1,15 +0,0 @@ | |||||||
| { |  | ||||||
|     "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 |  | ||||||
|         }      |  | ||||||
| } |  | ||||||
| @@ -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() |  | ||||||
| @@ -1,164 +0,0 @@ | |||||||
| #!/usr/bin/env python3 |  | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| """ |  | ||||||
| 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 |  | ||||||
| returns an updated amplifier json file: output_json_file_name = 'edfa_config.json' |  | ||||||
| """ |  | ||||||
| import re |  | ||||||
| import sys |  | ||||||
| import json |  | ||||||
| import numpy as np |  | ||||||
| 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_ripple: NF ripple excursion txt file |  | ||||||
| dfg: gain 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 |  | ||||||
|         } |  | ||||||
| } |  | ||||||
| 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 = "edfa_config.json" |  | ||||||
| param_field  ="params" |  | ||||||
| gain_min_field = "gain_min" |  | ||||||
| gain_max_field = "gain_flatmax" |  | ||||||
| gain_ripple_field = "dfg" |  | ||||||
| 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 |  | ||||||
|         convert dfg into gain ripple by removing the mean component |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     #with open(path + file_name,'r') as this_file: |  | ||||||
|     #   data = this_file.read() |  | ||||||
|     #data.strip() |  | ||||||
|     #data = re.sub(r"([0-9])([ ]{1,3})([0-9-+])",r"\1,\3",data) |  | ||||||
|     #data = list(data.split(",")) |  | ||||||
|     #data = [float(x) for x in data] |  | ||||||
|     data = np.loadtxt(file_name) |  | ||||||
|     if field == gain_ripple_field or field == nf_ripple_field: |  | ||||||
|         #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 |  | ||||||
|         data = data - data.mean() |  | ||||||
|     data = data.tolist() |  | ||||||
|     return data |  | ||||||
|  |  | ||||||
| def nf_model(amp_dict): |  | ||||||
|     if amp_dict[nf_model_field][nf_model_enabled_field] == True: |  | ||||||
|         gain_min = amp_dict[gain_min_field] |  | ||||||
|         gain_max = amp_dict[gain_max_field] |  | ||||||
|         nf_min = amp_dict[nf_model_field][nf_min_field] |  | ||||||
|         nf_max = amp_dict[nf_model_field][nf_max_field] |  | ||||||
|         #use NF estimation model based on NFmin and NFmax in json OA file |  | ||||||
|         delta_p = 5 #max power dB difference between 1st and 2nd stage coils |  | ||||||
|         #dB g1a = (1st stage gain) - (internal voa attenuation) |  | ||||||
|         g1a_min = gain_min - (gain_max-gain_min) - delta_p |  | ||||||
|         g1a_max = gain_max - delta_p |  | ||||||
|         #nf1 and nf2 are the nf of the 1st and 2nd stage coils |  | ||||||
|         #calculate nf1 and nf2 values that solve nf_[min/max] = nf1 + nf2 / g1a[min/max] |  | ||||||
|         nf2 = lin2db((db2lin(nf_min) - db2lin(nf_max)) / (1/db2lin(g1a_max)-1/db2lin(g1a_min))) |  | ||||||
|         nf1 = lin2db(db2lin(nf_min)- db2lin(nf2)/db2lin(g1a_max)) #expression (1) |  | ||||||
|  |  | ||||||
|         """ now checking and recalculating the results: |  | ||||||
|         recalculate delta_p to check it is within [1-6] boundaries |  | ||||||
|         This is to check that the nf_min and nf_max values from the json file |  | ||||||
|         make sense. If not a warning is printed """ |  | ||||||
|         if nf1 < 4: |  | ||||||
|             print('1st coil nf calculated value {} is too low: revise inputs'.format(nf1)) |  | ||||||
|         if nf2 < nf1 + 0.3 or nf2 > nf1 + 2:  |  | ||||||
|             """nf2 should be with [nf1+0.5 - nf1 +2] boundaries |  | ||||||
|             there shouldn't be very high nf differences between 2 coils |  | ||||||
|             => recalculate delta_p  |  | ||||||
|             """             |  | ||||||
|             nf2 = max(nf2, nf1+0.3) |  | ||||||
|             nf2 = min(nf2, nf1+2) |  | ||||||
|             g1a_max = lin2db(db2lin(nf2) / (db2lin(nf_min) - db2lin(nf1))) #use expression (1) |  | ||||||
|             delta_p = gain_max - g1a_max |  | ||||||
|             g1a_min = gain_min - (gain_max-gain_min) - delta_p |  | ||||||
|             if delta_p < 1 or delta_p > 6: |  | ||||||
|                 #delta_p should be > 1dB and < 6dB => consider user warning if not |  | ||||||
|                 print('1st coil vs 2nd coil calculated DeltaP {} is not valid: revise inputs' |  | ||||||
|                             .format(delta_p)) |  | ||||||
|         #check the calculated values for nf1 & nf2: |  | ||||||
|         nf_min_calc = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_max)) |  | ||||||
|         nf_max_calc = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_min)) |  | ||||||
|         if (abs(nf_min_calc-nf_min) > 0.01) or (abs(nf_max_calc-nf_max) > 0.01): |  | ||||||
|             print('nf model calculation failed with nf_min {} and nf_max {} calculated' |  | ||||||
|                     .format(nf_min_calc, nf_max_calc)) |  | ||||||
|             print('do not use the generated edfa_config.json file') |  | ||||||
|     else : |  | ||||||
|         (nf1, nf2, delta_p) = (0, 0, 0) |  | ||||||
|  |  | ||||||
|     return (nf1, nf2, delta_p) |  | ||||||
|  |  | ||||||
| def input_json(path): |  | ||||||
|     """read the json input file and add all the 96 channels txt files |  | ||||||
|     create the output json file with output_json_file_name""" |  | ||||||
|     with open(path,'r') as edfa_json_file: |  | ||||||
|         amp_text = edfa_json_file.read() |  | ||||||
|     amp_dict = json.loads(amp_text) |  | ||||||
|  |  | ||||||
|     for k, v in amp_dict.items(): |  | ||||||
|         if re.search(r'.txt$',str(v)) : |  | ||||||
|             amp_dict[k] = read_file(k, v) |  | ||||||
|  |  | ||||||
|     #calculate nf of 1st and 2nd coil for the nf_model if 'enabled'==true |  | ||||||
|     (nf1, nf2, delta_p) = nf_model(amp_dict) |  | ||||||
|     #rename nf_min and nf_max in nf1 and nf2 after the nf model calculation: |  | ||||||
|     del amp_dict[nf_model_field][nf_min_field] |  | ||||||
|     del amp_dict[nf_model_field][nf_max_field] |  | ||||||
|     amp_dict[nf_model_field]['nf1'] = nf1 |  | ||||||
|     amp_dict[nf_model_field]['nf2'] = nf2 |  | ||||||
|     amp_dict[nf_model_field]['delta_p'] = delta_p |  | ||||||
|     #rename dfg into gain_ripple after removing the average part: |  | ||||||
|     amp_dict['gain_ripple'] = amp_dict.pop(gain_ripple_field) |  | ||||||
|  |  | ||||||
|     new_amp_dict = {} |  | ||||||
|     new_amp_dict[param_field] = amp_dict |  | ||||||
|     amp_text = json.dumps(new_amp_dict, indent=4) |  | ||||||
|     #print(amp_text) |  | ||||||
|     with  open(output_json_file_name,'w') as edfa_json_file: |  | ||||||
|         edfa_json_file.write(amp_text) |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     if len(sys.argv) == 2: |  | ||||||
|         path = sys.argv[1] |  | ||||||
|     else: |  | ||||||
|         path = input_json_file_name |  | ||||||
|     input_json(path) |  | ||||||
| @@ -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,90 +0,0 @@ | |||||||
| #!/usr/bin/env  |  | ||||||
| """ |  | ||||||
| @author: briantaylor |  | ||||||
| @author: giladgoldfarb |  | ||||||
| @author: jeanluc-auge |  | ||||||
|  |  | ||||||
| Transmission setup example:  |  | ||||||
| reads from network json (default = examples/edfa/edfa_example_network.json) |  | ||||||
| propagates a 96 channels comb  |  | ||||||
| """ |  | ||||||
| from argparse import ArgumentParser |  | ||||||
| from json import load |  | ||||||
| from sys import exit |  | ||||||
| from pathlib import Path |  | ||||||
| from logging import getLogger, basicConfig, INFO, ERROR, DEBUG |  | ||||||
|  |  | ||||||
| from matplotlib.pyplot import show, axis, figure, title |  | ||||||
| from networkx import (draw_networkx_nodes, draw_networkx_edges, |  | ||||||
|                       draw_networkx_labels, dijkstra_path) |  | ||||||
|  |  | ||||||
| from gnpy.core import network_from_json, build_network |  | ||||||
| from gnpy.core.elements import Transceiver, Fiber, Edfa |  | ||||||
| from gnpy.core.info import SpectralInformation, Channel, Power |  | ||||||
| #from gnpy.core.algorithms import closed_paths |  | ||||||
|  |  | ||||||
| logger = getLogger(__package__ or __file__) |  | ||||||
|  |  | ||||||
| def format_si(spectral_infos): |  | ||||||
|     return '\n'.join([ |  | ||||||
|         f'#{idx} Carrier(frequency={c.frequency},\n  power=Power(signal={c.power.signal}, nli={c.power.nli}, ase={c.power.ase}))' |  | ||||||
|         for idx, si in sorted(set(spectral_infos)) |  | ||||||
|         for c in set(si.carriers) |  | ||||||
|     ]) |  | ||||||
|  |  | ||||||
| logger = getLogger('gnpy.core') |  | ||||||
|  |  | ||||||
| def main(args): |  | ||||||
|     with open(args.filename) as f: |  | ||||||
|         json_data = load(f) |  | ||||||
|  |  | ||||||
|     network = network_from_json(json_data) |  | ||||||
|     build_network(network) |  | ||||||
|  |  | ||||||
|     spacing = 0.05 #THz |  | ||||||
|     si = SpectralInformation() # !! SI units W, Hz |  | ||||||
|     si = si.update(carriers=tuple(Channel(f, (191.3+spacing*f)*1e12,  |  | ||||||
|             32e9, 0.15, Power(1e-3, 0, 0)) for f in range(1,97))) |  | ||||||
|  |  | ||||||
|     trx = [n for n in network.nodes() if isinstance(n, Transceiver)] |  | ||||||
|     source, sink = trx[0], trx[1] |  | ||||||
|   |  | ||||||
|     path = dijkstra_path(network, source, sink) |  | ||||||
|     print(f'There are {len(path)} network elements between {source} and {sink}') |  | ||||||
|  |  | ||||||
|     for el in path: |  | ||||||
|         si = el(si) |  | ||||||
|         print(el) |  | ||||||
|  |  | ||||||
|     nodelist = [n for n in network.nodes() if isinstance(n, (Transceiver, Fiber))] |  | ||||||
|     pathnodes = [n for n in path if isinstance(n, (Transceiver, Fiber))] |  | ||||||
|     edgelist = [(u, v) for u, v in zip(pathnodes, pathnodes[1:])] |  | ||||||
|     node_color = ['#ff0000' if n is source or n is sink else |  | ||||||
|                   '#900000' if n in path else '#ffdfdf' |  | ||||||
|                   for n in nodelist] |  | ||||||
|     edge_color = ['#ff9090' if u in path and v in path else '#ababab' |  | ||||||
|                   for u, v in edgelist] |  | ||||||
|     labels = {n: n.location.city if isinstance(n, Transceiver) else '' |  | ||||||
|               for n in pathnodes} |  | ||||||
|  |  | ||||||
|     fig = figure() |  | ||||||
|     pos = {n: (n.lng, n.lat) for n in nodelist} |  | ||||||
|     kwargs = {'figure': fig, 'pos': pos} |  | ||||||
|     plot = draw_networkx_nodes(network, nodelist=nodelist, node_color=node_color, **kwargs) |  | ||||||
|     draw_networkx_edges(network, edgelist=edgelist, edge_color=edge_color, **kwargs) |  | ||||||
|     draw_networkx_labels(network, labels=labels, font_size=14, **kwargs) |  | ||||||
|     title(f'Propagating from {source.loc.city} to {sink.loc.city}') |  | ||||||
|     axis('off') |  | ||||||
|     show() |  | ||||||
|  |  | ||||||
| parser = ArgumentParser() |  | ||||||
| parser.add_argument('filename', nargs='?', type=Path, |  | ||||||
|   default= Path(__file__).parent / 'edfa/edfa_example_network.json') |  | ||||||
| parser.add_argument('-v', '--verbose', action='count') |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': |  | ||||||
|     args = parser.parse_args() |  | ||||||
|     level = {1: INFO, 2: DEBUG}.get(args.verbose, ERROR) |  | ||||||
|     logger.setLevel(level) |  | ||||||
|     basicConfig() |  | ||||||
|     exit(main(args)) |  | ||||||
| @@ -0,0 +1,8 @@ | |||||||
|  | ''' | ||||||
|  | GNPy is an open-source, community-developed library for building route planning and optimization tools in real-world mesh optical networks. It is based on the Gaussian Noise Model. | ||||||
|  |  | ||||||
|  | Signal propagation is implemented in :py:mod:`.core`. | ||||||
|  | Path finding and spectrum assignment is in :py:mod:`.topology`. | ||||||
|  | Various tools and auxiliary code, including the JSON I/O handling, is in | ||||||
|  | :py:mod:`.tools`. | ||||||
|  | ''' | ||||||
|   | |||||||
							
								
								
									
										9
									
								
								gnpy/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								gnpy/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  | from flask import Flask | ||||||
|  |  | ||||||
|  | app = Flask(__name__) | ||||||
|  |  | ||||||
|  | import gnpy.api.route.path_request_route | ||||||
|  | import gnpy.api.route.status_route | ||||||
|  | import gnpy.api.route.topology_route | ||||||
|  | import gnpy.api.route.equipments_route | ||||||
							
								
								
									
										1
									
								
								gnpy/api/exception/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								gnpy/api/exception/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | # coding: utf-8 | ||||||
							
								
								
									
										14
									
								
								gnpy/api/exception/config_error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								gnpy/api/exception/config_error.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ConfigError(Exception): | ||||||
|  |     """ Exception raise for configuration file error | ||||||
|  |     Attributes: | ||||||
|  |         message -- explanation of the error | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def __init__(self, message): | ||||||
|  |         self.message = message | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.message | ||||||
							
								
								
									
										14
									
								
								gnpy/api/exception/equipment_error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								gnpy/api/exception/equipment_error.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class EquipmentError(Exception): | ||||||
|  |     """ Exception raise for equipment error | ||||||
|  |     Attributes: | ||||||
|  |         message -- explanation of the error | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def __init__(self, message): | ||||||
|  |         self.message = message | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.message | ||||||
							
								
								
									
										33
									
								
								gnpy/api/exception/exception_handler.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								gnpy/api/exception/exception_handler.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  | import json | ||||||
|  | import re | ||||||
|  |  | ||||||
|  | import werkzeug | ||||||
|  |  | ||||||
|  | from gnpy.api.model.error import Error | ||||||
|  |  | ||||||
|  | _reaesc = re.compile(r'\x1b[^m]*m') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def common_error_handler(exception): | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     :type exception: Exception | ||||||
|  |  | ||||||
|  |     """ | ||||||
|  |     status_code = 500 | ||||||
|  |     if not isinstance(exception, werkzeug.exceptions.HTTPException): | ||||||
|  |         exception = werkzeug.exceptions.InternalServerError() | ||||||
|  |         exception.description = "Something went wrong on our side." | ||||||
|  |     else: | ||||||
|  |         status_code = exception.code | ||||||
|  |     response = Error(message=exception.name, description=exception.description, | ||||||
|  |                      code=status_code) | ||||||
|  |  | ||||||
|  |     return werkzeug.Response(response=json.dumps(response.__dict__), status=status_code, mimetype='application/json') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def bad_request_handler(exception): | ||||||
|  |     response = Error(message='bad request', description=_reaesc.sub('', str(exception)), | ||||||
|  |                      code=400) | ||||||
|  |     return werkzeug.Response(response=json.dumps(response.__dict__), status=400, mimetype='application/json') | ||||||
							
								
								
									
										14
									
								
								gnpy/api/exception/path_computation_error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								gnpy/api/exception/path_computation_error.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PathComputationError(Exception): | ||||||
|  |     """ Exception raise for path computation error error | ||||||
|  |     Attributes: | ||||||
|  |         message -- explanation of the error | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def __init__(self, message): | ||||||
|  |         self.message = message | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.message | ||||||
							
								
								
									
										14
									
								
								gnpy/api/exception/topology_error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								gnpy/api/exception/topology_error.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TopologyError(Exception): | ||||||
|  |     """ Exception raise for topology error | ||||||
|  |     Attributes: | ||||||
|  |         message -- explanation of the error | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def __init__(self, message): | ||||||
|  |         self.message = message | ||||||
|  |  | ||||||
|  |     def __str__(self): | ||||||
|  |         return self.message | ||||||
							
								
								
									
										1
									
								
								gnpy/api/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								gnpy/api/model/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | # coding: utf-8 | ||||||
							
								
								
									
										17
									
								
								gnpy/api/model/error.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								gnpy/api/model/error.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Error: | ||||||
|  |  | ||||||
|  |     def __init__(self, code: int = None, message: str = None, description: str = None): | ||||||
|  |         """Error | ||||||
|  |         :param code: The code of this Error. | ||||||
|  |         :type code: int | ||||||
|  |         :param message: The message of this Error. | ||||||
|  |         :type message: str | ||||||
|  |         :param description: The description of this Error. | ||||||
|  |         :type description: str | ||||||
|  |         """ | ||||||
|  |         self.code = code | ||||||
|  |         self.message = message | ||||||
|  |         self.description = description | ||||||
							
								
								
									
										8
									
								
								gnpy/api/model/result.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								gnpy/api/model/result.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Result: | ||||||
|  |  | ||||||
|  |     def __init__(self, message: str = None, description: str = None): | ||||||
|  |         self.message = message | ||||||
|  |         self.description = description | ||||||
							
								
								
									
										83
									
								
								gnpy/api/rest_example.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								gnpy/api/rest_example.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
|  | ''' | ||||||
|  | gnpy.tools.rest_example | ||||||
|  | ======================= | ||||||
|  |  | ||||||
|  | GNPy as a rest API example | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | import logging | ||||||
|  | from logging.handlers import RotatingFileHandler | ||||||
|  |  | ||||||
|  | import werkzeug | ||||||
|  | from flask_injector import FlaskInjector | ||||||
|  | from injector import singleton | ||||||
|  | from werkzeug.exceptions import InternalServerError | ||||||
|  |  | ||||||
|  | import gnpy.core.exceptions as exceptions | ||||||
|  | from gnpy.api import app | ||||||
|  | from gnpy.api.exception.exception_handler import bad_request_handler, common_error_handler | ||||||
|  | from gnpy.api.exception.path_computation_error import PathComputationError | ||||||
|  | from gnpy.api.exception.topology_error import TopologyError | ||||||
|  | from gnpy.api.service import config_service | ||||||
|  | from gnpy.api.service.encryption_service import EncryptionService | ||||||
|  | from gnpy.api.service.equipment_service import EquipmentService | ||||||
|  | from gnpy.api.service.path_request_service import PathRequestService | ||||||
|  |  | ||||||
|  | _logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _init_logger(): | ||||||
|  |     handler = RotatingFileHandler('api.log', maxBytes=1024 * 1024, backupCount=5, encoding='utf-8') | ||||||
|  |     ch = logging.StreamHandler() | ||||||
|  |     logging.basicConfig(level=logging.INFO, handlers=[handler, ch], | ||||||
|  |                         format="%(asctime)s %(levelname)s %(name)s(%(lineno)s) [%(threadName)s - %(thread)d] - %(" | ||||||
|  |                                "message)s") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _init_app(key): | ||||||
|  |     app.register_error_handler(KeyError, bad_request_handler) | ||||||
|  |     app.register_error_handler(TypeError, bad_request_handler) | ||||||
|  |     app.register_error_handler(ValueError, bad_request_handler) | ||||||
|  |     app.register_error_handler(exceptions.ConfigurationError, bad_request_handler) | ||||||
|  |     app.register_error_handler(exceptions.DisjunctionError, bad_request_handler) | ||||||
|  |     app.register_error_handler(exceptions.EquipmentConfigError, bad_request_handler) | ||||||
|  |     app.register_error_handler(exceptions.NetworkTopologyError, bad_request_handler) | ||||||
|  |     app.register_error_handler(exceptions.ServiceError, bad_request_handler) | ||||||
|  |     app.register_error_handler(exceptions.SpectrumError, bad_request_handler) | ||||||
|  |     app.register_error_handler(exceptions.ParametersError, bad_request_handler) | ||||||
|  |     app.register_error_handler(AssertionError, bad_request_handler) | ||||||
|  |     app.register_error_handler(InternalServerError, common_error_handler) | ||||||
|  |     app.register_error_handler(TopologyError, bad_request_handler) | ||||||
|  |     app.register_error_handler(PathComputationError, bad_request_handler) | ||||||
|  |     for error_code in werkzeug.exceptions.default_exceptions: | ||||||
|  |         app.register_error_handler(error_code, common_error_handler) | ||||||
|  |     config = config_service.init_config() | ||||||
|  |     config.add_section('SECRET') | ||||||
|  |     config.set('SECRET', 'equipment', key) | ||||||
|  |     app.config['properties'] = config | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _configure(binder): | ||||||
|  |     binder.bind(EquipmentService, | ||||||
|  |                 to=EquipmentService(EncryptionService(app.config['properties'].get('SECRET', 'equipment'))), | ||||||
|  |                 scope=singleton) | ||||||
|  |     binder.bind(PathRequestService, | ||||||
|  |                 to=PathRequestService(EncryptionService(app.config['properties'].get('SECRET', 'equipment'))), | ||||||
|  |                 scope=singleton) | ||||||
|  |     app.config['properties'].pop('SECRET', None) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def main(): | ||||||
|  |     key = input('Enter encryption/decryption key: ') | ||||||
|  |     _init_logger() | ||||||
|  |     _init_app(key) | ||||||
|  |     FlaskInjector(app=app, modules=[_configure]) | ||||||
|  |  | ||||||
|  |     app.run(host='0.0.0.0', port=8080, ssl_context='adhoc') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     main() | ||||||
							
								
								
									
										2
									
								
								gnpy/api/route/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								gnpy/api/route/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  |  | ||||||
							
								
								
									
										38
									
								
								gnpy/api/route/equipments_route.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								gnpy/api/route/equipments_route.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  | import http | ||||||
|  | import json | ||||||
|  |  | ||||||
|  | from flask import request | ||||||
|  |  | ||||||
|  | from gnpy.api import app | ||||||
|  | from gnpy.api.exception.equipment_error import EquipmentError | ||||||
|  | from gnpy.api.model.result import Result | ||||||
|  | from gnpy.api.service.equipment_service import EquipmentService | ||||||
|  |  | ||||||
|  | EQUIPMENT_BASE_PATH = '/api/v1/equipments' | ||||||
|  | EQUIPMENT_ID_PATH = EQUIPMENT_BASE_PATH + '/<equipment_id>' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(EQUIPMENT_BASE_PATH, methods=['POST']) | ||||||
|  | def create_equipment(equipment_service: EquipmentService): | ||||||
|  |     if not request.is_json: | ||||||
|  |         raise EquipmentError('Request body is not json') | ||||||
|  |     equipment_identifier = equipment_service.save_equipment(request.json) | ||||||
|  |     response = Result(message='Equipment creation ok', description=equipment_identifier) | ||||||
|  |     return json.dumps(response.__dict__), 201, {'location': EQUIPMENT_BASE_PATH + '/' + equipment_identifier} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(EQUIPMENT_ID_PATH, methods=['PUT']) | ||||||
|  | def update_equipment(equipment_id, equipment_service: EquipmentService): | ||||||
|  |     if not request.is_json: | ||||||
|  |         raise EquipmentError('Request body is not json') | ||||||
|  |     equipment_identifier = equipment_service.update_equipment(request.json, equipment_id) | ||||||
|  |     response = Result(message='Equipment update ok', description=equipment_identifier) | ||||||
|  |     return json.dumps(response.__dict__), http.HTTPStatus.OK, { | ||||||
|  |         'location': EQUIPMENT_BASE_PATH + '/' + equipment_identifier} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(EQUIPMENT_ID_PATH, methods=['DELETE']) | ||||||
|  | def delete_equipment(equipment_id, equipment_service: EquipmentService): | ||||||
|  |     equipment_service.delete_equipment(equipment_id) | ||||||
|  |     return '', http.HTTPStatus.NO_CONTENT | ||||||
							
								
								
									
										63
									
								
								gnpy/api/route/path_request_route.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								gnpy/api/route/path_request_route.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  | import http | ||||||
|  | import os | ||||||
|  | from pathlib import Path | ||||||
|  |  | ||||||
|  | from flask import request | ||||||
|  |  | ||||||
|  | from gnpy.api import app | ||||||
|  | from gnpy.api.exception.equipment_error import EquipmentError | ||||||
|  | from gnpy.api.exception.topology_error import TopologyError | ||||||
|  | from gnpy.api.service import topology_service | ||||||
|  | from gnpy.api.service.equipment_service import EquipmentService | ||||||
|  | from gnpy.api.service.path_request_service import PathRequestService | ||||||
|  | from gnpy.tools.json_io import _equipment_from_json, network_from_json | ||||||
|  | from gnpy.topology.request import ResultElement | ||||||
|  |  | ||||||
|  | PATH_COMPUTATION_BASE_PATH = '/api/v1/path-computation' | ||||||
|  | AUTODESIGN_PATH = PATH_COMPUTATION_BASE_PATH + '/<path_computation_id>/autodesign' | ||||||
|  |  | ||||||
|  | _examples_dir = Path(__file__).parent.parent.parent / 'example-data' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(PATH_COMPUTATION_BASE_PATH, methods=['POST']) | ||||||
|  | def compute_path(equipment_service: EquipmentService, path_request_service: PathRequestService): | ||||||
|  |     data = request.json | ||||||
|  |     service = data['gnpy-api:service'] | ||||||
|  |     if 'gnpy-api:topology' in data: | ||||||
|  |         topology = data['gnpy-api:topology'] | ||||||
|  |     elif 'gnpy-api:topology_id' in data: | ||||||
|  |         topology = topology_service.get_topology(data['gnpy-api:topology_id']) | ||||||
|  |     else: | ||||||
|  |         raise TopologyError('No topology found in request') | ||||||
|  |     if 'gnpy-api:equipment' in data: | ||||||
|  |         equipment = data['gnpy-api:equipment'] | ||||||
|  |     elif 'gnpy-api:equipment_id' in data: | ||||||
|  |         equipment = equipment_service.get_equipment(data['gnpy-api:equipment_id']) | ||||||
|  |     else: | ||||||
|  |         raise EquipmentError('No equipment found in request') | ||||||
|  |     equipment = _equipment_from_json(equipment, | ||||||
|  |                                      os.path.join(_examples_dir, 'std_medium_gain_advanced_config.json')) | ||||||
|  |     network = network_from_json(topology, equipment) | ||||||
|  |  | ||||||
|  |     propagatedpths, reversed_propagatedpths, rqs, path_computation_id = path_request_service.path_requests_run(service, | ||||||
|  |                                                                                                                network, | ||||||
|  |                                                                                                                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(ResultElement(rqs[i], pth, reversed_propagatedpths[i])) | ||||||
|  |     return {"result": {"response": [n.json for n in result]}}, 201, { | ||||||
|  |         'location': AUTODESIGN_PATH.replace('<path_computation_id>', path_computation_id)} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(AUTODESIGN_PATH, methods=['GET']) | ||||||
|  | def get_autodesign(path_computation_id, path_request_service: PathRequestService): | ||||||
|  |     return path_request_service.get_autodesign(path_computation_id), http.HTTPStatus.OK | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(AUTODESIGN_PATH, methods=['DELETE']) | ||||||
|  | def delete_autodesign(path_computation_id, path_request_service: PathRequestService): | ||||||
|  |     path_request_service.delete_autodesign(path_computation_id) | ||||||
|  |     return '', http.HTTPStatus.NO_CONTENT | ||||||
							
								
								
									
										7
									
								
								gnpy/api/route/status_route.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								gnpy/api/route/status_route.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  | from gnpy.api import app | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route('/api/v1/status', methods=['GET']) | ||||||
|  | def api_status(): | ||||||
|  |     return {"version": "v1", "status": "ok"}, 200 | ||||||
							
								
								
									
										43
									
								
								gnpy/api/route/topology_route.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								gnpy/api/route/topology_route.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  | import http | ||||||
|  | import json | ||||||
|  |  | ||||||
|  | from flask import request | ||||||
|  |  | ||||||
|  | from gnpy.api import app | ||||||
|  | from gnpy.api.exception.topology_error import TopologyError | ||||||
|  | from gnpy.api.model.result import Result | ||||||
|  | from gnpy.api.service import topology_service | ||||||
|  |  | ||||||
|  | TOPOLOGY_BASE_PATH = '/api/v1/topologies' | ||||||
|  | TOPOLOGY_ID_PATH = TOPOLOGY_BASE_PATH + '/<topology_id>' | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(TOPOLOGY_BASE_PATH, methods=['POST']) | ||||||
|  | def create_topology(): | ||||||
|  |     if not request.is_json: | ||||||
|  |         raise TopologyError('Request body is not json') | ||||||
|  |     topology_identifier = topology_service.save_topology(request.json) | ||||||
|  |     response = Result(message='Topology creation ok', description=topology_identifier) | ||||||
|  |     return json.dumps(response.__dict__), 201, {'location': TOPOLOGY_BASE_PATH + '/' + topology_identifier} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(TOPOLOGY_ID_PATH, methods=['PUT']) | ||||||
|  | def update_topology(topology_id): | ||||||
|  |     if not request.is_json: | ||||||
|  |         raise TopologyError('Request body is not json') | ||||||
|  |     topology_identifier = topology_service.update_topology(request.json, topology_id) | ||||||
|  |     response = Result(message='Topology update ok', description=topology_identifier) | ||||||
|  |     return json.dumps(response.__dict__), http.HTTPStatus.OK, { | ||||||
|  |         'location': TOPOLOGY_BASE_PATH + '/' + topology_identifier} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(TOPOLOGY_ID_PATH, methods=['GET']) | ||||||
|  | def get_topology(topology_id): | ||||||
|  |     return topology_service.get_topology(topology_id), http.HTTPStatus.OK | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @app.route(TOPOLOGY_ID_PATH, methods=['DELETE']) | ||||||
|  | def delete_topology(topology_id): | ||||||
|  |     topology_service.delete_topology(topology_id) | ||||||
|  |     return '', http.HTTPStatus.NO_CONTENT | ||||||
							
								
								
									
										1
									
								
								gnpy/api/service/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								gnpy/api/service/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | # coding: utf-8 | ||||||
							
								
								
									
										45
									
								
								gnpy/api/service/config_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								gnpy/api/service/config_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  | import configparser | ||||||
|  | import os | ||||||
|  |  | ||||||
|  | from flask import current_app | ||||||
|  |  | ||||||
|  | from gnpy.api.exception.config_error import ConfigError | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def init_config(properties_file_path: str = os.path.join(os.path.dirname(__file__), | ||||||
|  |                                                          'properties.ini')) -> configparser.ConfigParser: | ||||||
|  |     """ | ||||||
|  |     Read config from properties_file_path | ||||||
|  |     @param properties_file_path: the properties file to read | ||||||
|  |     @return: config parser | ||||||
|  |     """ | ||||||
|  |     if not os.path.exists(properties_file_path): | ||||||
|  |         raise ConfigError('Properties file does not exist ' + properties_file_path) | ||||||
|  |     config = configparser.ConfigParser() | ||||||
|  |     config.read(properties_file_path) | ||||||
|  |     return config | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_topology_dir() -> str: | ||||||
|  |     """ | ||||||
|  |     Get the base dir where topologies are saved | ||||||
|  |     @return: the directory of topologies | ||||||
|  |     """ | ||||||
|  |     return current_app.config['properties'].get('DIRECTORY', 'topology') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_equipment_dir() -> str: | ||||||
|  |     """ | ||||||
|  |     Get the base dir where equipments are saved | ||||||
|  |     @return: the directory of equipments | ||||||
|  |     """ | ||||||
|  |     return current_app.config['properties'].get('DIRECTORY', 'equipment') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_autodesign_dir() -> str: | ||||||
|  |     """ | ||||||
|  |     Get the base dir where autodesign are saved | ||||||
|  |     @return: the directory of equipments | ||||||
|  |     """ | ||||||
|  |     return current_app.config['properties'].get('DIRECTORY', 'autodesign') | ||||||
							
								
								
									
										13
									
								
								gnpy/api/service/encryption_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								gnpy/api/service/encryption_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | # coding: utf-8 | ||||||
|  | from cryptography.fernet import Fernet | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class EncryptionService: | ||||||
|  |     def __init__(self, key): | ||||||
|  |         self._fernet = Fernet(key) | ||||||
|  |  | ||||||
|  |     def encrypt(self, data): | ||||||
|  |         return self._fernet.encrypt(data) | ||||||
|  |  | ||||||
|  |     def decrypt(self, data): | ||||||
|  |         return self._fernet.decrypt(data) | ||||||
							
								
								
									
										66
									
								
								gnpy/api/service/equipment_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								gnpy/api/service/equipment_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | |||||||
|  | # coding: utf- | ||||||
|  | import json | ||||||
|  | import os | ||||||
|  | import uuid | ||||||
|  |  | ||||||
|  | from injector import Inject | ||||||
|  |  | ||||||
|  | from gnpy.api.exception.equipment_error import EquipmentError | ||||||
|  | from gnpy.api.service import config_service | ||||||
|  | from gnpy.api.service.encryption_service import EncryptionService | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class EquipmentService: | ||||||
|  |  | ||||||
|  |     def __init__(self, encryption_service: EncryptionService): | ||||||
|  |         self.encryption = encryption_service | ||||||
|  |  | ||||||
|  |     def save_equipment(self, equipment): | ||||||
|  |         """ | ||||||
|  |         Save equipment to file. | ||||||
|  |         @param equipment: json content | ||||||
|  |         @return: a UUID identifier to identify the equipment | ||||||
|  |         """ | ||||||
|  |         equipment_identifier = str(uuid.uuid4()) | ||||||
|  |         # TODO: validate json content | ||||||
|  |         self._write_equipment(equipment, equipment_identifier) | ||||||
|  |         return equipment_identifier | ||||||
|  |  | ||||||
|  |     def update_equipment(self, equipment, equipment_identifier): | ||||||
|  |         """ | ||||||
|  |         Update equipment with identifier equipment_identifier. | ||||||
|  |         @param equipment_identifier: the identifier of the equipment to be updated | ||||||
|  |         @param equipment: json content | ||||||
|  |         @return: a UUID identifier to identify the equipment | ||||||
|  |         """ | ||||||
|  |         # TODO: validate json content | ||||||
|  |         self._write_equipment(equipment, equipment_identifier) | ||||||
|  |         return equipment_identifier | ||||||
|  |  | ||||||
|  |     def _write_equipment(self, equipment, equipment_identifier): | ||||||
|  |         equipment_dir = config_service.get_equipment_dir() | ||||||
|  |         with(open(os.path.join(equipment_dir, '.'.join([equipment_identifier, 'json'])), 'wb')) as file: | ||||||
|  |             file.write(self.encryption.encrypt(json.dumps(equipment).encode())) | ||||||
|  |  | ||||||
|  |     def get_equipment(self, equipment_id: str) -> dict: | ||||||
|  |         """ | ||||||
|  |         Get the equipment with id equipment_id | ||||||
|  |         @param equipment_id: | ||||||
|  |         @return: the equipment in json format | ||||||
|  |         """ | ||||||
|  |         equipment_dir = config_service.get_equipment_dir() | ||||||
|  |         equipment_file = os.path.join(equipment_dir, '.'.join([equipment_id, 'json'])) | ||||||
|  |         if not os.path.exists(equipment_file): | ||||||
|  |             raise EquipmentError('Equipment with id {} does not exist '.format(equipment_id)) | ||||||
|  |         with(open(equipment_file, 'rb')) as file: | ||||||
|  |             return json.loads(self.encryption.decrypt(file.read())) | ||||||
|  |  | ||||||
|  |     def delete_equipment(self, equipment_id: str): | ||||||
|  |         """ | ||||||
|  |         Delete equipment with id equipment_id | ||||||
|  |         @param equipment_id: | ||||||
|  |         """ | ||||||
|  |         equipment_dir = config_service.get_equipment_dir() | ||||||
|  |         equipment_file = os.path.join(equipment_dir, '.'.join([equipment_id, 'json'])) | ||||||
|  |         if os.path.exists(equipment_file): | ||||||
|  |             os.remove(equipment_file) | ||||||
							
								
								
									
										100
									
								
								gnpy/api/service/path_request_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								gnpy/api/service/path_request_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | import json | ||||||
|  | import logging | ||||||
|  | import os | ||||||
|  | import uuid | ||||||
|  |  | ||||||
|  | import gnpy.core.ansi_escapes as ansi_escapes | ||||||
|  | from gnpy.api.exception.path_computation_error import PathComputationError | ||||||
|  | from gnpy.api.service import config_service | ||||||
|  | from gnpy.api.service.encryption_service import EncryptionService | ||||||
|  | from gnpy.core.network import build_network | ||||||
|  | from gnpy.core.utils import lin2db, automatic_nch | ||||||
|  | from gnpy.tools.json_io import requests_from_json, disjunctions_from_json, network_to_json | ||||||
|  | from gnpy.topology.request import (compute_path_dsjctn, requests_aggregation, | ||||||
|  |                                    correct_json_route_list, | ||||||
|  |                                    deduplicate_disjunctions, compute_path_with_disjunction) | ||||||
|  | from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum | ||||||
|  |  | ||||||
|  | _logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PathRequestService: | ||||||
|  |  | ||||||
|  |     def __init__(self, encryption_service: EncryptionService): | ||||||
|  |         self.encryption = encryption_service | ||||||
|  |  | ||||||
|  |     def path_requests_run(self, service, network, equipment): | ||||||
|  |         # 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 | ||||||
|  |  | ||||||
|  |         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) | ||||||
|  |         path_computation_identifier = str(uuid.uuid4()) | ||||||
|  |         autodesign_dir = config_service.get_autodesign_dir() | ||||||
|  |         with(open(os.path.join(autodesign_dir, '.'.join([path_computation_identifier, 'json'])), 'wb')) as file: | ||||||
|  |             file.write(self.encryption.encrypt(json.dumps(network_to_json(network)).encode())) | ||||||
|  |         oms_list = build_oms_list(network, equipment) | ||||||
|  |         rqs = requests_from_json(service, equipment) | ||||||
|  |  | ||||||
|  |         # 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 ValueError('Requests id ' + all_ids + ' are not unique') | ||||||
|  |         rqs = correct_json_route_list(network, rqs) | ||||||
|  |  | ||||||
|  |         # pths = compute_path(network, equipment, rqs) | ||||||
|  |         dsjn = disjunctions_from_json(service) | ||||||
|  |  | ||||||
|  |         # need to warn or correct in case of wrong disjunction form | ||||||
|  |         # disjunction must not be repeated with same or different ids | ||||||
|  |         dsjn = deduplicate_disjunctions(dsjn) | ||||||
|  |  | ||||||
|  |         rqs, dsjn = requests_aggregation(rqs, dsjn) | ||||||
|  |         # TODO export novel set of aggregated demands in a json file | ||||||
|  |  | ||||||
|  |         _logger.info(f'{ansi_escapes.blue}The following services have been requested:{ansi_escapes.reset}' + str(rqs)) | ||||||
|  |  | ||||||
|  |         _logger.info(f'{ansi_escapes.blue}Computing all paths with constraints{ansi_escapes.reset}') | ||||||
|  |         pths = compute_path_dsjctn(network, equipment, rqs, dsjn) | ||||||
|  |  | ||||||
|  |         _logger.info(f'{ansi_escapes.blue}Propagating on selected path{ansi_escapes.reset}') | ||||||
|  |         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) | ||||||
|  |         return propagatedpths, reversed_propagatedpths, rqs, path_computation_identifier | ||||||
|  |  | ||||||
|  |     def get_autodesign(self, path_computation_id): | ||||||
|  |         """ | ||||||
|  |         Get the autodesign with id topology_id | ||||||
|  |         @param path_computation_id: | ||||||
|  |         @return: the autodesign in json format | ||||||
|  |         """ | ||||||
|  |         autodesign_dir = config_service.get_autodesign_dir() | ||||||
|  |         autodesign_file = os.path.join(autodesign_dir, '.'.join([path_computation_id, 'json'])) | ||||||
|  |         if not os.path.exists(autodesign_file): | ||||||
|  |             raise PathComputationError('Autodesign with id {} does not exist '.format(path_computation_id)) | ||||||
|  |         with(open(autodesign_file, 'rb')) as file: | ||||||
|  |             return json.loads(self.encryption.decrypt(file.read())) | ||||||
|  |  | ||||||
|  |     def delete_autodesign(self, path_computation_id: str): | ||||||
|  |         """ | ||||||
|  |         Delete autodesign with id equipment_id | ||||||
|  |         @param path_computation_id: | ||||||
|  |         """ | ||||||
|  |         autodesign_dir = config_service.get_autodesign_dir() | ||||||
|  |         autodesign_file = os.path.join(autodesign_dir, '.'.join([path_computation_id, 'json'])) | ||||||
|  |         if os.path.exists(autodesign_file): | ||||||
|  |             os.remove(autodesign_file) | ||||||
							
								
								
									
										4
									
								
								gnpy/api/service/properties.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								gnpy/api/service/properties.ini
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | [DIRECTORY] | ||||||
|  | topology: /opt/application/oopt-gnpy/topology | ||||||
|  | equipment: /opt/application/oopt-gnpy/equipment | ||||||
|  | autodesign: /opt/application/oopt-gnpy/autodesign | ||||||
							
								
								
									
										62
									
								
								gnpy/api/service/topology_service.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								gnpy/api/service/topology_service.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | # coding: utf- | ||||||
|  | import json | ||||||
|  | import os | ||||||
|  | import uuid | ||||||
|  |  | ||||||
|  | from gnpy.api.exception.topology_error import TopologyError | ||||||
|  | from gnpy.api.service import config_service | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def save_topology(topology): | ||||||
|  |     """ | ||||||
|  |     Save topology to file. | ||||||
|  |     @param topology: json content | ||||||
|  |     @return: a UUID identifier to identify the topology | ||||||
|  |     """ | ||||||
|  |     topology_identifier = str(uuid.uuid4()) | ||||||
|  |     # TODO: validate json content | ||||||
|  |     _write_topology(topology, topology_identifier) | ||||||
|  |     return topology_identifier | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def update_topology(topology, topology_identifier): | ||||||
|  |     """ | ||||||
|  |     Update topology with identifier topology_identifier. | ||||||
|  |     @param topology_identifier: the identifier of the topology to be updated | ||||||
|  |     @param topology: json content | ||||||
|  |     @return: a UUID identifier to identify the topology | ||||||
|  |     """ | ||||||
|  |     # TODO: validate json content | ||||||
|  |     _write_topology(topology, topology_identifier) | ||||||
|  |     return topology_identifier | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _write_topology(topology, topology_identifier): | ||||||
|  |     topology_dir = config_service.get_topology_dir() | ||||||
|  |     with(open(os.path.join(topology_dir, '.'.join([topology_identifier, 'json'])), 'w')) as file: | ||||||
|  |         json.dump(topology, file) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_topology(topology_id: str) -> dict: | ||||||
|  |     """ | ||||||
|  |     Get the topology with id topology_id | ||||||
|  |     @param topology_id: | ||||||
|  |     @return: the topology in json format | ||||||
|  |     """ | ||||||
|  |     topology_dir = config_service.get_topology_dir() | ||||||
|  |     topology_file = os.path.join(topology_dir, '.'.join([topology_id, 'json'])) | ||||||
|  |     if not os.path.exists(topology_file): | ||||||
|  |         raise TopologyError('Topology with id {} does not exist '.format(topology_id)) | ||||||
|  |     with(open(topology_file, 'r')) as file: | ||||||
|  |         return json.load(file) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def delete_topology(topology_id: str): | ||||||
|  |     """ | ||||||
|  |     Delete topology with id topology_id | ||||||
|  |     @param topology_id: | ||||||
|  |     """ | ||||||
|  |     topology_dir = config_service.get_topology_dir() | ||||||
|  |     topology_file = os.path.join(topology_dir, '.'.join([topology_id, 'json'])) | ||||||
|  |     if os.path.exists(topology_file): | ||||||
|  |         os.remove(topology_file) | ||||||
| @@ -1,6 +1,9 @@ | |||||||
| #!/usr/bin/env python3 | ''' | ||||||
|  | Simulation of signal propagation in the DWDM network | ||||||
|  |  | ||||||
| from . import elements | Optical signals, as defined via :class:`.info.SpectralInformation`, enter | ||||||
| from .execute import * | :py:mod:`.elements` which compute how these signals are affected as they travel | ||||||
| from .network import * | through the :py:mod:`.network`. | ||||||
| from .utils import * | The simulation is controlled via :py:mod:`.parameters` and implemented mainly | ||||||
|  | via :py:mod:`.science_utils`. | ||||||
|  | ''' | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								gnpy/core/ansi_escapes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								gnpy/core/ansi_escapes.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | #!/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' | ||||||
|  | blue = '\x1b[1;34;40m' | ||||||
|  | cyan = '\x1b[1;36;40m' | ||||||
|  | yellow = '\x1b[1;33;40m' | ||||||
|  | reset = '\x1b[0m' | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										73
									
								
								gnpy/core/equipment.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								gnpy/core/equipment.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,73 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
|  | ''' | ||||||
|  | gnpy.core.equipment | ||||||
|  | =================== | ||||||
|  |  | ||||||
|  | This module contains functionality for specifying equipment. | ||||||
|  | ''' | ||||||
|  |  | ||||||
|  | from gnpy.core.utils import automatic_nch, db2lin | ||||||
|  | from gnpy.core.exceptions import EquipmentConfigError | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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'] | ||||||
|  |         # 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']) | ||||||
|  |         # temp = trx_params['spacing'] | ||||||
|  |         # print(f'spacing {temp}') | ||||||
|  |     except StopIteration: | ||||||
|  |         if error_message: | ||||||
|  |             raise EquipmentConfigError(f'Could not find transponder "{trx_type_variety}" with mode "{trx_mode}" in equipment library') | ||||||
|  |         else: | ||||||
|  |             # default transponder charcteristics | ||||||
|  |             # 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'] = 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 | ||||||
|  |  | ||||||
|  |     return trx_params | ||||||
							
								
								
									
										37
									
								
								gnpy/core/exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								gnpy/core/exceptions.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | #!/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""" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class ParametersError(ConfigurationError): | ||||||
|  |     """Incomplete or wrong configurations within parameters json""" | ||||||
| @@ -1,2 +0,0 @@ | |||||||
| #!/usr/bin/env python3 |  | ||||||
|  |  | ||||||
| @@ -1,50 +1,56 @@ | |||||||
| #!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | gnpy.core.info | ||||||
|  | ============== | ||||||
|  |  | ||||||
|  | This module contains classes for modelling :class:`SpectralInformation`. | ||||||
|  | """ | ||||||
|  |  | ||||||
| from collections import namedtuple | from collections import namedtuple | ||||||
|  | from gnpy.core.utils import automatic_nch, lin2db | ||||||
|  |  | ||||||
| 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): | class Power(namedtuple('Power', 'signal nli ase')): | ||||||
|         for abbrev, field in getattr(self, '_ABBREVS', {}).items(): |     """carriers power in W""" | ||||||
|             if abbrev in kwargs: |  | ||||||
|                 kwargs[field] = kwargs.pop(abbrev) |  | ||||||
|         return self._replace(**kwargs) |  | ||||||
|  |  | ||||||
| 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): | class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power chromatic_dispersion pmd')): | ||||||
|     _ABBREVS = {'channel': 'channel_number', |     """ Class containing the parameters of a WDM signal. | ||||||
|                 'num_chan': 'channel_number', |  | ||||||
|                 'ffs':     'frequency', |  | ||||||
|                 'freq':    'frequency',} |  | ||||||
|  |  | ||||||
| class SpectralInformation(namedtuple('SpectralInformation', 'carriers'), ConvenienceAccess): |         :param channel_number: channel number in the WDM grid | ||||||
|     def __new__(cls, *carriers): |         :param frequency: central frequency of the signal (Hz) | ||||||
|         return super().__new__(cls, carriers) |         :param baud_rate: the symbol rate of the signal (Baud) | ||||||
|  |         :param roll_off: the roll off of the signal. It is a pure number between 0 and 1 | ||||||
|  |         :param power (gnpy.core.info.Power): power of signal, ASE noise and NLI (W) | ||||||
|  |         :param chromatic_dispersion: chromatic dispersion (s/m) | ||||||
|  |         :param pmd: polarization mode dispersion (s) | ||||||
|  |     """ | ||||||
|  |  | ||||||
| if __name__ == '__main__': |  | ||||||
|  | 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) | ||||||
|  |     nb_channel = automatic_nch(f_min, f_max, spacing) | ||||||
|     si = SpectralInformation( |     si = SpectralInformation( | ||||||
|         Channel(1, 193.95e12, 32e9, 0.15,  # 193.95 THz, 32 Gbaud |         pref=Pref(pref, pref, lin2db(nb_channel)), | ||||||
|             Power(1e-3, 1e-6, 1e-6)),             # 1 mW, 1uW, 1uW |         carriers=[ | ||||||
|         Channel(1, 195.95e12, 32e9, 0.15,  # 195.95 THz, 32 Gbaud |             Channel(f, (f_min + spacing * f), | ||||||
|             Power(1.2e-3, 1e-6, 1e-6)),           # 1.2 mW, 1uW, 1uW |                     baud_rate, roll_off, Power(power, 0, 0), 0, 0) for f in range(1, nb_channel + 1) | ||||||
|  |         ] | ||||||
|     ) |     ) | ||||||
|  |     return si | ||||||
|     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))) |  | ||||||
|      |  | ||||||
|     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)) |  | ||||||
|                               for c in si.carriers)) |  | ||||||
|     print(f'si2 = {si2}') |  | ||||||
|     """ |  | ||||||
|   | |||||||
| @@ -1,114 +1,503 @@ | |||||||
| #!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
| from networkx import DiGraph | ''' | ||||||
|  | gnpy.core.network | ||||||
|  | ================= | ||||||
|  |  | ||||||
| from gnpy.core import elements | Working with networks which consist of network elements | ||||||
| from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm | ''' | ||||||
| from gnpy.core.units import UNITS |  | ||||||
|  |  | ||||||
| MAX_SPAN_LENGTH = 125000 | from operator import attrgetter | ||||||
| TARGET_SPAN_LENGTH = 100000 | from gnpy.core import ansi_escapes, elements | ||||||
| MIN_SPAN_LENGTH = 75000 | from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError | ||||||
|  | from gnpy.core.utils import round2float, convert_length | ||||||
|  | from collections import namedtuple | ||||||
|  |  | ||||||
| def network_from_json(json_data): |  | ||||||
|     # NOTE|dutc: we could use the following, but it would tie our data format |  | ||||||
|     #            too closely to the graph library |  | ||||||
|     # from networkx import node_link_graph |  | ||||||
|     g = DiGraph() |  | ||||||
|     for el_config in json_data['elements']: |  | ||||||
|         g.add_node(getattr(elements, el_config['type'])(el_config)) |  | ||||||
|  |  | ||||||
|     nodes = {k.uid: k for k in g.nodes()} | def edfa_nf(gain_target, variety_type, equipment): | ||||||
|  |     amp_params = equipment['Edfa'][variety_type] | ||||||
|  |     amp = elements.Edfa( | ||||||
|  |         uid='calc_NF', | ||||||
|  |         params=amp_params.__dict__, | ||||||
|  |         operational={ | ||||||
|  |             'gain_target': gain_target, | ||||||
|  |             'tilt_target': 0 | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     amp.pin_db = 0 | ||||||
|  |     amp.nch = 88 | ||||||
|  |     return amp._calc_nf(True) | ||||||
|  |  | ||||||
|     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]) |  | ||||||
|  |  | ||||||
|     return g | 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_min nf') | ||||||
|  |     TARGET_EXTENDED_GAIN = equipment['Span']['default'].target_extended_gain | ||||||
|  |  | ||||||
| def calculate_new_length(fiber_length): |     # for roadm restriction only: create a dict including not allowed for design amps | ||||||
|     result = (fiber_length, 1) |     # because main use case is to have specific radm amp which are not allowed for ILA | ||||||
|     if fiber_length > MAX_SPAN_LENGTH: |     # with the auto design | ||||||
|         n_spans = int(fiber_length // TARGET_SPAN_LENGTH) |     edfa_dict = {name: amp for (name, amp) in equipment['Edfa'].items() | ||||||
|  |                  if restrictions is None or name in restrictions} | ||||||
|  |  | ||||||
|         length1 = fiber_length / (n_spans+1) |     pin = power_target - gain_target | ||||||
|         result1 = (length1, n_spans+1) |  | ||||||
|         delta1 = TARGET_SPAN_LENGTH-length1 |  | ||||||
|  |  | ||||||
|         length2 = fiber_length / n_spans |     # create 2 list of available amplifiers with relevant attributes for their selection | ||||||
|         delta2 = length2-TARGET_SPAN_LENGTH |  | ||||||
|         result2 = (length2, n_spans) |  | ||||||
|  |  | ||||||
|         if length1<MIN_SPAN_LENGTH and length2<MAX_SPAN_LENGTH: |     # edfa list with: | ||||||
|             result = result2 |     # extended gain min allowance of 3dB: could be parametrized, but a bit complex | ||||||
|         elif length2>MAX_SPAN_LENGTH and length1>MIN_SPAN_LENGTH: |     # extended gain max allowance TARGET_EXTENDED_GAIN is coming from eqpt_config.json | ||||||
|             result = result1 |     # power attribut include power AND gain limitations | ||||||
|  |     edfa_list = [Edfa_list( | ||||||
|  |         variety=edfa_variety, | ||||||
|  |         power=min( | ||||||
|  |             pin | ||||||
|  |             + edfa.gain_flatmax | ||||||
|  |             + TARGET_EXTENDED_GAIN, | ||||||
|  |             edfa.p_max | ||||||
|  |         ) | ||||||
|  |         - power_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 or restrictions is not None) and not edfa.raman)] | ||||||
|  |  | ||||||
|  |     # 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: |         else: | ||||||
|             if delta1 < delta2:  |             # TODO: convert to logging | ||||||
|                 result = result1 |             print( | ||||||
|  |                 f'{ansi_escapes.red}WARNING:{ansi_escapes.reset} target gain in node {uid} is below all available amplifiers min gain: \ | ||||||
|  |                   amplifier input padding will be assumed, consider increase span fiber padding instead' | ||||||
|  |             ) | ||||||
|  |             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(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: | ||||||
|  |     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'{ansi_escapes.red}WARNING:{ansi_escapes.reset} 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' | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     return selected_edfa.variety, power_reduction | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def target_power(network, node, equipment):  # get_fiber_dp | ||||||
|  |     if isinstance(node, elements.Roadm): | ||||||
|  |         return 0 | ||||||
|  |  | ||||||
|  |     SPAN_LOSS_REF = 20 | ||||||
|  |     POWER_SLOPE = 0.3 | ||||||
|  |     dp_range = list(equipment['Span']['default'].delta_power_range_db) | ||||||
|  |     node_loss = span_loss(network, node) | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         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 IndexError: | ||||||
|  |         raise ConfigurationError(f'invalid delta_power_range_db definition in eqpt_config[Span]' | ||||||
|  |                                  f'delta_power_range_db: [lower_bound, upper_bound, step]') | ||||||
|  |  | ||||||
|  |     return dp | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def prev_node_generator(network, node): | ||||||
|  |     """fused spans interest: | ||||||
|  |     iterate over all predecessors while they are Fused or Fiber type""" | ||||||
|  |     try: | ||||||
|  |         prev_node = next(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, elements.Fused) or isinstance(node, elements.Fused): | ||||||
|  |         yield prev_node | ||||||
|  |         yield from prev_node_generator(network, prev_node) | ||||||
|     else: |     else: | ||||||
|                 result = result2 |         StopIteration | ||||||
|  |  | ||||||
|     return result |  | ||||||
|  |  | ||||||
| def split_fiber(network, fiber): | def next_node_generator(network, node): | ||||||
|     new_length, n_spans = calculate_new_length(fiber.length) |     """fused spans interest: | ||||||
|     prev_node = fiber |     iterate over all successors while they are Fused or Fiber type""" | ||||||
|     if n_spans > 1: |     try: | ||||||
|         next_nodes = [_ for _ in network.successors(fiber)] |         next_node = next(network.successors(node)) | ||||||
|         for next_node in next_nodes: |     except StopIteration: | ||||||
|             network.remove_edge(fiber, next_node) |         raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology') | ||||||
|  |     # yield and re-iterate | ||||||
|  |     if isinstance(next_node, elements.Fused) or isinstance(node, elements.Fused): | ||||||
|  |         yield next_node | ||||||
|  |         yield from next_node_generator(network, next_node) | ||||||
|  |     else: | ||||||
|  |         StopIteration | ||||||
|  |  | ||||||
|         new_params_length = new_length / UNITS[fiber.params.length_units] |  | ||||||
|         config = {'uid':fiber.uid, 'type': 'Fiber', 'metadata': fiber.__dict__['metadata'], \ |  | ||||||
|             'params': fiber.__dict__['params']} |  | ||||||
|         fiber.uid = config['uid'] + '_1' |  | ||||||
|         fiber.length = new_length |  | ||||||
|         fiber.loss = fiber.loss_coef * fiber.length |  | ||||||
|  |  | ||||||
|         for i in range(2, n_spans+1): | def span_loss(network, node): | ||||||
|             new_config = dict(config) |     """Fused span interest: | ||||||
|             new_config['uid'] = new_config['uid'] + '_' + str(i) |     return the total span loss of all the fibers spliced by a Fused node""" | ||||||
|             new_config['params'].length = new_params_length |     loss = node.loss if node.passive else 0 | ||||||
|             new_node = Fiber(new_config) |     try: | ||||||
|             network.add_node(new_node) |         prev_node = next(network.predecessors(node)) | ||||||
|             network.add_edge(prev_node, new_node) |         if isinstance(prev_node, elements.Fused): | ||||||
|             network = add_egress_amplifier(network, prev_node) |             loss += sum(n.loss for n in prev_node_generator(network, node)) | ||||||
|             prev_node = new_node |     except StopIteration: | ||||||
|  |         pass | ||||||
|  |     try: | ||||||
|  |         next_node = next(network.successors(node)) | ||||||
|  |         if isinstance(next_node, elements.Fused): | ||||||
|  |             loss += sum(n.loss for n in next_node_generator(network, node)) | ||||||
|  |     except StopIteration: | ||||||
|  |         pass | ||||||
|  |     return loss | ||||||
|  |  | ||||||
|         for next_node in next_nodes: |  | ||||||
|             network.add_edge(prev_node, next_node) |  | ||||||
|  |  | ||||||
|     network = add_egress_amplifier(network, prev_node) | def find_first_node(network, node): | ||||||
|     return network |     """Fused node interest: | ||||||
|  |     returns the 1st node at the origin of a succession of fused nodes | ||||||
|  |     (aka no amp in between)""" | ||||||
|  |     this_node = node | ||||||
|  |     for this_node in prev_node_generator(network, node): | ||||||
|  |         pass | ||||||
|  |     return this_node | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def find_last_node(network, node): | ||||||
|  |     """Fused node interest: | ||||||
|  |     returns the last node in a succession of fused nodes | ||||||
|  |     (aka no amp in between)""" | ||||||
|  |     this_node = node | ||||||
|  |     for this_node in next_node_generator(network, node): | ||||||
|  |         pass | ||||||
|  |     return this_node | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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 and amp.params.out_voa_auto: | ||||||
|  |             voa = min(amp.params.p_max - power_target, | ||||||
|  |                       amp.params.gain_flatmax - amp.effective_gain) | ||||||
|  |             voa = max(round2float(voa, 0.5) - VOA_MARGIN, 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.out_voa = voa | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def set_egress_amplifier(network, this_node, equipment, pref_ch_db, pref_total_db): | ||||||
|  |     """ this node can be a transceiver or a ROADM (same function called in both cases) | ||||||
|  |     """ | ||||||
|  |     power_mode = equipment['Span']['default'].power_mode | ||||||
|  |     next_oms = (n for n in network.successors(this_node) if not isinstance(n, elements.Transceiver)) | ||||||
|  |     this_node_degree = {k: v for k, v in this_node.per_degree_pch_out_db.items()} if hasattr(this_node, 'per_degree_pch_out_db') else {} | ||||||
|  |     for oms in next_oms: | ||||||
|  |         # go through all the OMS departing from the ROADM | ||||||
|  |         prev_node = this_node | ||||||
|  |         node = oms | ||||||
|  |         # if isinstance(next_node, elements.Fused): #support ROADM wo egress amp for metro applications | ||||||
|  |         #     node = find_last_node(next_node) | ||||||
|  |         #     next_node = next(n for n in network.successors(node)) | ||||||
|  |         #     next_node = find_last_node(next_node) | ||||||
|  |         if node.uid not in this_node_degree: | ||||||
|  |             # if no target power is defined on this degree or no per degree target power is given use the global one | ||||||
|  |             # if target_pch_out_db  is not an attribute, then the element must be a transceiver | ||||||
|  |             this_node_degree[node.uid] = getattr(this_node.params, 'target_pch_out_db', 0) | ||||||
|  |         # use the target power on this degree | ||||||
|  |         prev_dp = this_node_degree[node.uid] - pref_ch_db | ||||||
|  |         dp = prev_dp | ||||||
|  |         prev_voa = 0 | ||||||
|  |         voa = 0 | ||||||
|  |         visited_nodes = [] | ||||||
|  |         while not (isinstance(node, elements.Roadm) or isinstance(node, elements.Transceiver)): | ||||||
|  |             # go through all nodes in the OMS (loop until next Roadm instance) | ||||||
|  |             try: | ||||||
|  |                 next_node = next(network.successors(node)) | ||||||
|  |             except StopIteration: | ||||||
|  |                 raise NetworkTopologyError(f'{type(node).__name__} {node.uid} is not properly connected, please check network topology') | ||||||
|  |             visited_nodes.append(node) | ||||||
|  |             if next_node in visited_nodes: | ||||||
|  |                 raise NetworkTopologyError(f'Loop detected for {type(node).__name__} {node.uid}, please check network topology') | ||||||
|  |             if isinstance(node, elements.Edfa): | ||||||
|  |                 node_loss = span_loss(network, prev_node) | ||||||
|  |                 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 | ||||||
|  |                 if node.effective_gain is None or power_mode: | ||||||
|  |                     gain_target = node_loss + dp - prev_dp + prev_voa | ||||||
|  |                 else:  # gain mode with effective_gain | ||||||
|  |                     gain_target = node.effective_gain | ||||||
|  |                     dp = prev_dp - node_loss - prev_voa + gain_target | ||||||
|  |  | ||||||
|  |                 power_target = pref_total_db + dp | ||||||
|  |  | ||||||
|  |                 if isinstance(prev_node, elements.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 | ||||||
|  |                 else: | ||||||
|  |                     raman_allowed = False | ||||||
|  |  | ||||||
|  |                 # implementation of restrictions on roadm boosters | ||||||
|  |                 if isinstance(prev_node, elements.Roadm): | ||||||
|  |                     if prev_node.restrictions['booster_variety_list']: | ||||||
|  |                         restrictions = prev_node.restrictions['booster_variety_list'] | ||||||
|  |                     else: | ||||||
|  |                         restrictions = None | ||||||
|  |                 elif isinstance(next_node, elements.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.__dict__) | ||||||
|  |                     dp += power_reduction | ||||||
|  |                     gain_target += power_reduction | ||||||
|  |                 elif node.params.raman and not raman_allowed: | ||||||
|  |                     print(f'{ansi_escapes.red}WARNING{ansi_escapes.reset}: raman is used in node {node.uid}\n but fiber lineic loss is above threshold\n') | ||||||
|  |                 else: | ||||||
|  |                     # if variety is imposed by user, and if the gain_target (computed or imposed) is also above | ||||||
|  |                     # variety max gain + extended range, then warn that gain > max_gain + extended range | ||||||
|  |                     if gain_target - equipment['Edfa'][node.params.type_variety].gain_flatmax - \ | ||||||
|  |                             equipment['Span']['default'].target_extended_gain > 1e-2: | ||||||
|  |                         # 1e-2 to allow a small margin according to round2float min step | ||||||
|  |                         print(f'{ansi_escapes.red}WARNING{ansi_escapes.reset}: ' | ||||||
|  |                               f'WARNING: effective gain in Node {node.uid} is above user ' | ||||||
|  |                               f'specified amplifier {node.params.type_variety}\n' | ||||||
|  |                               f'max flat gain: {equipment["Edfa"][node.params.type_variety].gain_flatmax}dB ; ' | ||||||
|  |                               f'required gain: {gain_target}dB. Please check amplifier type.') | ||||||
|  |  | ||||||
|  |                 node.delta_p = dp if power_mode else None | ||||||
|  |                 node.effective_gain = gain_target | ||||||
|  |                 set_amplifier_voa(node, power_target, power_mode) | ||||||
|  |  | ||||||
|  |             prev_dp = dp | ||||||
|  |             prev_voa = voa | ||||||
|  |             prev_node = node | ||||||
|  |             node = next_node | ||||||
|  |             # print(f'{node.uid}') | ||||||
|  |  | ||||||
|  |     if isinstance(this_node, elements.Roadm): | ||||||
|  |         this_node.per_degree_pch_out_db = {k: v for k, v in this_node_degree.items()} | ||||||
|  |  | ||||||
| def add_egress_amplifier(network, node): | def add_egress_amplifier(network, node): | ||||||
|     next_nodes = [n for n in network.successors(node) |     next_nodes = [n for n in network.successors(node) | ||||||
|         if not (isinstance(n, Edfa) or isinstance(n, Transceiver))] |                   if not (isinstance(n, elements.Transceiver) or isinstance(n, elements.Fused) or isinstance(n, elements.Edfa))] | ||||||
|     i = 1 |     # no amplification for fused spans or TRX | ||||||
|     for next_node in next_nodes: |     for i, next_node in enumerate(next_nodes): | ||||||
|         network.remove_edge(node, next_node) |         network.remove_edge(node, next_node) | ||||||
|  |         amp = elements.Edfa( | ||||||
|  |             uid=f'Edfa{i}_{node.uid}', | ||||||
|  |             params={}, | ||||||
|  |             metadata={ | ||||||
|  |                 'location': { | ||||||
|  |                     'latitude': (node.lat + next_node.lat) / 2, | ||||||
|  |                     'longitude': (node.lng + next_node.lng) / 2, | ||||||
|  |                     'city': node.loc.city, | ||||||
|  |                     'region': node.loc.region, | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |             operational={ | ||||||
|  |                 'gain_target': None, | ||||||
|  |                 'tilt_target': 0, | ||||||
|  |             }) | ||||||
|  |         network.add_node(amp) | ||||||
|  |         if isinstance(node, elements.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) | ||||||
|  |  | ||||||
|         uid = 'Edfa' + str(i)+ '_' + str(node.uid) |  | ||||||
|         metadata = next_node.metadata |  | ||||||
|         operational = {'gain_target': node.loss, 'tilt_target': 0} |  | ||||||
|         edfa_config_json = 'edfa_config.json' |  | ||||||
|         config = {'uid':uid, 'type': 'Edfa', 'metadata': metadata, \ |  | ||||||
|                     'config_from_json': edfa_config_json, 'operational': operational} |  | ||||||
|         new_edfa = Edfa(config) |  | ||||||
|         network.add_node(new_edfa) |  | ||||||
|         network.add_edge(node,new_edfa) |  | ||||||
|         network.add_edge(new_edfa, next_node) |  | ||||||
|         i +=1 |  | ||||||
|  |  | ||||||
|     return network | def calculate_new_length(fiber_length, bounds, target_length): | ||||||
|  |     if fiber_length < bounds.stop: | ||||||
|  |         return fiber_length, 1 | ||||||
|  |  | ||||||
| def build_network(network): |     n_spans2 = int(fiber_length // target_length) | ||||||
|     fibers = [f for f in network.nodes() if isinstance(f, Fiber)] |     n_spans1 = n_spans2 + 1 | ||||||
|  |  | ||||||
|  |     length1 = fiber_length / n_spans1 | ||||||
|  |     length2 = fiber_length / n_spans2 | ||||||
|  |  | ||||||
|  |     if (bounds.start <= length1 <= bounds.stop) and not(bounds.start <= length2 <= bounds.stop): | ||||||
|  |         return (length1, n_spans1) | ||||||
|  |     elif (bounds.start <= length2 <= bounds.stop) and not(bounds.start <= length1 <= bounds.stop): | ||||||
|  |         return (length2, n_spans2) | ||||||
|  |     elif target_length - length1 < length2 - target_length: | ||||||
|  |         return (length1, n_spans1) | ||||||
|  |     else: | ||||||
|  |         return (length2, n_spans2) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def split_fiber(network, fiber, bounds, target_length, equipment): | ||||||
|  |     new_length, n_spans = calculate_new_length(fiber.params.length, bounds, target_length) | ||||||
|  |     if n_spans == 1: | ||||||
|  |         return | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         next_node = next(network.successors(fiber)) | ||||||
|  |         prev_node = next(network.predecessors(fiber)) | ||||||
|  |     except StopIteration: | ||||||
|  |         raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology') | ||||||
|  |  | ||||||
|  |     network.remove_node(fiber) | ||||||
|  |  | ||||||
|  |     fiber.params.length = new_length | ||||||
|  |  | ||||||
|  |     xpos = [prev_node.lng + (next_node.lng - prev_node.lng) * (n + 1) / (n_spans + 1) for n in range(n_spans)] | ||||||
|  |     ypos = [prev_node.lat + (next_node.lat - prev_node.lat) * (n + 1) / (n_spans + 1) for n in range(n_spans)] | ||||||
|  |     for span, lng, lat in zip(range(n_spans), xpos, ypos): | ||||||
|  |         new_span = elements.Fiber(uid=f'{fiber.uid}_({span+1}/{n_spans})', | ||||||
|  |                          type_variety=fiber.type_variety, | ||||||
|  |                          metadata={ | ||||||
|  |                               'location': { | ||||||
|  |                                   'latitude': lat, | ||||||
|  |                                   'longitude': lng, | ||||||
|  |                                   'city': fiber.loc.city, | ||||||
|  |                                   'region': fiber.loc.region, | ||||||
|  |                               } | ||||||
|  |                          }, | ||||||
|  |                          params=fiber.params.asdict()) | ||||||
|  |         if isinstance(prev_node, elements.Fiber): | ||||||
|  |             edgeweight = prev_node.params.length | ||||||
|  |         else: | ||||||
|  |             edgeweight = 0.01 | ||||||
|  |         network.add_edge(prev_node, new_span, weight=edgeweight) | ||||||
|  |         prev_node = new_span | ||||||
|  |     if isinstance(prev_node, elements.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: |     for fiber in fibers: | ||||||
|         network = split_fiber(network, 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 fiber.params.con_in is None: | ||||||
|  |             fiber.params.con_in = default_con_in | ||||||
|  |         if fiber.params.con_out is None: | ||||||
|  |             fiber.params.con_out = default_con_out | ||||||
|  |         if not isinstance(next_node, elements.Fused): | ||||||
|  |             fiber.params.con_out += EOL | ||||||
|  |  | ||||||
|     roadms = [r for r in network.nodes() if isinstance(r, Roadm)] |  | ||||||
|  | def add_fiber_padding(network, fibers, padding): | ||||||
|  |     """last_fibers = (fiber for n in network.nodes() | ||||||
|  |                          if not (isinstance(n, elements.Fiber) or isinstance(n, elements.Fused)) | ||||||
|  |                          for fiber in network.predecessors(n) | ||||||
|  |                          if isinstance(fiber, elements.Fiber))""" | ||||||
|  |     for fiber in fibers: | ||||||
|  |         try: | ||||||
|  |             next_node = next(network.successors(fiber)) | ||||||
|  |         except StopIteration: | ||||||
|  |             raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology') | ||||||
|  |         if isinstance(next_node, elements.Fused): | ||||||
|  |             continue | ||||||
|  |         this_span_loss = span_loss(network, fiber) | ||||||
|  |         if this_span_loss < padding: | ||||||
|  |             # 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) | ||||||
|  |             # 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, elements.Fiber): | ||||||
|  |                 first_fiber.params.att_in = first_fiber.params.att_in + padding - this_span_loss | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def build_network(network, equipment, pref_ch_db, pref_total_db): | ||||||
|  |     default_span_data = equipment['Span']['default'] | ||||||
|  |     max_length = int(convert_length(default_span_data.max_length, 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) | ||||||
|  |  | ||||||
|  |     # set roadm loss for gain_mode before to build network | ||||||
|  |     fibers = [f for f in network.nodes() if isinstance(f, elements.Fiber)] | ||||||
|  |     add_connector_loss(network, fibers, default_span_data.con_in, default_span_data.con_out, default_span_data.EOL) | ||||||
|  |     add_fiber_padding(network, fibers, default_span_data.padding) | ||||||
|  |     # don't group split fiber and add amp in the same loop | ||||||
|  |     # =>for code clarity (at the expense of speed): | ||||||
|  |     for fiber in fibers: | ||||||
|  |         split_fiber(network, fiber, bounds, target_length, equipment) | ||||||
|  |  | ||||||
|  |     amplified_nodes = [n for n in network.nodes() if isinstance(n, elements.Fiber) or isinstance(n, elements.Roadm)] | ||||||
|  |  | ||||||
|  |     for node in amplified_nodes: | ||||||
|  |         add_egress_amplifier(network, node) | ||||||
|  |  | ||||||
|  |     roadms = [r for r in amplified_nodes if isinstance(r, elements.Roadm)] | ||||||
|     for roadm in roadms: |     for roadm in roadms: | ||||||
|         add_egress_amplifier(network, roadm) |         set_egress_amplifier(network, roadm, equipment, pref_ch_db, pref_total_db) | ||||||
|  |  | ||||||
|  |     trx = [t for t in network.nodes() if isinstance(t, elements.Transceiver)] | ||||||
|  |     for t in trx: | ||||||
|  |         next_node = next(network.successors(t), None) | ||||||
|  |         if next_node and not isinstance(next_node, elements.Roadm): | ||||||
|  |             set_egress_amplifier(network, t, equipment, 0, pref_total_db) | ||||||
|   | |||||||
| @@ -1,60 +0,0 @@ | |||||||
| #! /bin/usr/python3 |  | ||||||
|  |  | ||||||
| from uuid import uuid4 |  | ||||||
| from gnpy.core.utils import load_json |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ConfigStruct: |  | ||||||
|  |  | ||||||
|     def __init__(self, **config): |  | ||||||
|         if config is None: |  | ||||||
|             return None |  | ||||||
|         if 'config_from_json' in config: |  | ||||||
|             json_config = load_json(config['config_from_json']) |  | ||||||
|             self.set_config_attr(json_config) |  | ||||||
|  |  | ||||||
|         self.set_config_attr(config) |  | ||||||
|  |  | ||||||
|     def set_config_attr(self, config): |  | ||||||
|         for k, v in config.items(): |  | ||||||
|             setattr(self, k, ConfigStruct(**v) |  | ||||||
|                     if isinstance(v, dict) else v) |  | ||||||
|  |  | ||||||
|     def __repr__(self): |  | ||||||
|         return f'{self.__dict__}' |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Node: |  | ||||||
|  |  | ||||||
|     def __init__(self, config=None): |  | ||||||
|         self.config = ConfigStruct(**config) |  | ||||||
|         if self.config is None or not hasattr(self.config, 'uid'): |  | ||||||
|             self.uid = uuid4() |  | ||||||
|         else: |  | ||||||
|             self.uid = self.config.uid |  | ||||||
|         if hasattr(self.config, 'params'): |  | ||||||
|             self.params = self.config.params      |  | ||||||
|         if hasattr(self.config, 'metadata'): |  | ||||||
|             self.metadata = self.config.metadata |  | ||||||
|         if hasattr(self.config, 'operational'): |  | ||||||
|             self.operational = self.config.operational             |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def coords(self): |  | ||||||
|         return tuple(self.lng, self.lat) |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def location(self): |  | ||||||
|         return self.config.metadata.location |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def loc(self):  # Aliases .location |  | ||||||
|         return self.location |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def lng(self): |  | ||||||
|         return self.config.metadata.location.longitude |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def lat(self): |  | ||||||
|         return self.config.metadata.location.latitude |  | ||||||
							
								
								
									
										285
									
								
								gnpy/core/parameters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										285
									
								
								gnpy/core/parameters.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,285 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | gnpy.core.parameters | ||||||
|  | ==================== | ||||||
|  |  | ||||||
|  | This module contains all parameters to configure standard network elements. | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | from scipy.constants import c, pi | ||||||
|  | from numpy import squeeze, log10, exp | ||||||
|  |  | ||||||
|  | from gnpy.core.utils import db2lin, convert_length | ||||||
|  | from gnpy.core.exceptions import ParametersError | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Parameters: | ||||||
|  |     def asdict(self): | ||||||
|  |         class_dict = self.__class__.__dict__ | ||||||
|  |         instance_dict = self.__dict__ | ||||||
|  |         new_dict = {} | ||||||
|  |         for key in class_dict: | ||||||
|  |             if isinstance(class_dict[key], property): | ||||||
|  |                 new_dict[key] = instance_dict['_' + key] | ||||||
|  |         return new_dict | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class PumpParams(Parameters): | ||||||
|  |     def __init__(self, power, frequency, propagation_direction): | ||||||
|  |         self._power = power | ||||||
|  |         self._frequency = frequency | ||||||
|  |         self._propagation_direction = propagation_direction | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def power(self): | ||||||
|  |         return self._power | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def frequency(self): | ||||||
|  |         return self._frequency | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def propagation_direction(self): | ||||||
|  |         return self._propagation_direction | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class RamanParams(Parameters): | ||||||
|  |     def __init__(self, **kwargs): | ||||||
|  |         self._flag_raman = kwargs['flag_raman'] | ||||||
|  |         self._space_resolution = kwargs['space_resolution'] if 'space_resolution' in kwargs else None | ||||||
|  |         self._tolerance = kwargs['tolerance'] if 'tolerance' in kwargs else None | ||||||
|  |  | ||||||
|  |     @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(Parameters): | ||||||
|  |     def __init__(self, **kwargs): | ||||||
|  |         self._nli_method_name = kwargs['nli_method_name'] | ||||||
|  |         self._wdm_grid_size = kwargs['wdm_grid_size'] | ||||||
|  |         self._dispersion_tolerance = kwargs['dispersion_tolerance'] | ||||||
|  |         self._phase_shift_tolerance = kwargs['phase_shift_tolerance'] | ||||||
|  |         self._f_cut_resolution = None | ||||||
|  |         self._f_pump_resolution = None | ||||||
|  |         self._computed_channels = kwargs['computed_channels'] if 'computed_channels' in kwargs else 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_tolerance(self): | ||||||
|  |         return self._phase_shift_tolerance | ||||||
|  |  | ||||||
|  |     @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 | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def computed_channels(self): | ||||||
|  |         return self._computed_channels | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class SimParams(Parameters): | ||||||
|  |     def __init__(self, **kwargs): | ||||||
|  |         try: | ||||||
|  |             if 'nli_parameters' in kwargs: | ||||||
|  |                 self._nli_params = NLIParams(**kwargs['nli_parameters']) | ||||||
|  |             else: | ||||||
|  |                 self._nli_params = None | ||||||
|  |             if 'raman_parameters' in kwargs: | ||||||
|  |                 self._raman_params = RamanParams(**kwargs['raman_parameters']) | ||||||
|  |             else: | ||||||
|  |                 self._raman_params = None | ||||||
|  |         except KeyError as e: | ||||||
|  |             raise ParametersError(f'Simulation parameters must include {e}. Configuration: {kwargs}') | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def nli_params(self): | ||||||
|  |         return self._nli_params | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def raman_params(self): | ||||||
|  |         return self._raman_params | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class FiberParams(Parameters): | ||||||
|  |     def __init__(self, **kwargs): | ||||||
|  |         try: | ||||||
|  |             self._length = convert_length(kwargs['length'], kwargs['length_units']) | ||||||
|  |             # fixed attenuator for padding | ||||||
|  |             self._att_in = kwargs['att_in'] if 'att_in' in kwargs else 0 | ||||||
|  |             # 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] | ||||||
|  |             self._con_in = kwargs['con_in'] if 'con_in' in kwargs else None | ||||||
|  |             self._con_out = kwargs['con_out'] if 'con_out' in kwargs else None | ||||||
|  |             if 'ref_wavelength' in kwargs: | ||||||
|  |                 self._ref_wavelength = kwargs['ref_wavelength'] | ||||||
|  |                 self._ref_frequency = c / self.ref_wavelength | ||||||
|  |             elif 'ref_frequency' in kwargs: | ||||||
|  |                 self._ref_frequency = kwargs['ref_frequency'] | ||||||
|  |                 self._ref_wavelength = c / self.ref_frequency | ||||||
|  |             else: | ||||||
|  |                 self._ref_wavelength = 1550e-9 | ||||||
|  |                 self._ref_frequency = c / self.ref_wavelength | ||||||
|  |             self._dispersion = kwargs['dispersion']  # s/m/m | ||||||
|  |             self._dispersion_slope = kwargs['dispersion_slope'] if 'dispersion_slope' in kwargs else \ | ||||||
|  |                 -2 * self._dispersion/self.ref_wavelength  # s/m/m/m | ||||||
|  |             self._beta2 = -(self.ref_wavelength ** 2) * self.dispersion / (2 * pi * c)  # 1/(m * Hz^2) | ||||||
|  |             # Eq. (3.23) in  Abramczyk, Halina. "Dispersion phenomena in optical fibers." Virtual European University | ||||||
|  |             # on Lasers. Available online: http://mitr.p.lodz.pl/evu/lectures/Abramczyk3.pdf | ||||||
|  |             # (accessed on 25 March 2018) (2005). | ||||||
|  |             self._beta3 = ((self.dispersion_slope - (4*pi*c/self.ref_wavelength**3) * self.beta2) / | ||||||
|  |                            (2*pi*c/self.ref_wavelength**2)**2) | ||||||
|  |             self._gamma = kwargs['gamma']  # 1/W/m | ||||||
|  |             self._pmd_coef = kwargs['pmd_coef']  # s/sqrt(m) | ||||||
|  |             if type(kwargs['loss_coef']) == dict: | ||||||
|  |                 self._loss_coef = squeeze(kwargs['loss_coef']['loss_coef_power']) * 1e-3  # lineic loss dB/m | ||||||
|  |                 self._f_loss_ref = squeeze(kwargs['loss_coef']['frequency'])  # Hz | ||||||
|  |             else: | ||||||
|  |                 self._loss_coef = kwargs['loss_coef'] * 1e-3  # lineic loss dB/m | ||||||
|  |                 self._f_loss_ref = 193.5e12  # Hz | ||||||
|  |             self._lin_attenuation = db2lin(self.length * self.loss_coef) | ||||||
|  |             self._lin_loss_exp = self.loss_coef / (10 * log10(exp(1)))  # linear power exponent loss Neper/m | ||||||
|  |             self._effective_length = (1 - exp(- self.lin_loss_exp * self.length)) / self.lin_loss_exp | ||||||
|  |             self._asymptotic_length = 1 / self.lin_loss_exp | ||||||
|  |             # raman parameters (not compulsory) | ||||||
|  |             self._raman_efficiency = kwargs['raman_efficiency'] if 'raman_efficiency' in kwargs else None | ||||||
|  |             self._pumps_loss_coef = kwargs['pumps_loss_coef'] if 'pumps_loss_coef' in kwargs else None | ||||||
|  |         except KeyError as e: | ||||||
|  |             raise ParametersError(f'Fiber configurations json must include {e}. Configuration: {kwargs}') | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def length(self): | ||||||
|  |         return self._length | ||||||
|  |  | ||||||
|  |     @length.setter | ||||||
|  |     def length(self, length): | ||||||
|  |         """length must be in m""" | ||||||
|  |         self._length = length | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def att_in(self): | ||||||
|  |         return self._att_in | ||||||
|  |  | ||||||
|  |     @att_in.setter | ||||||
|  |     def att_in(self, att_in): | ||||||
|  |         self._att_in = att_in | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def con_in(self): | ||||||
|  |         return self._con_in | ||||||
|  |  | ||||||
|  |     @con_in.setter | ||||||
|  |     def con_in(self, con_in): | ||||||
|  |         self._con_in = con_in | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def con_out(self): | ||||||
|  |         return self._con_out | ||||||
|  |  | ||||||
|  |     @con_out.setter | ||||||
|  |     def con_out(self, con_out): | ||||||
|  |         self._con_out = con_out | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def dispersion(self): | ||||||
|  |         return self._dispersion | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def dispersion_slope(self): | ||||||
|  |         return self._dispersion_slope | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def gamma(self): | ||||||
|  |         return self._gamma | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def pmd_coef(self): | ||||||
|  |         return self._pmd_coef | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def ref_wavelength(self): | ||||||
|  |         return self._ref_wavelength | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def ref_frequency(self): | ||||||
|  |         return self._ref_frequency | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def beta2(self): | ||||||
|  |         return self._beta2 | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def beta3(self): | ||||||
|  |         return self._beta3 | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def loss_coef(self): | ||||||
|  |         return self._loss_coef | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def f_loss_ref(self): | ||||||
|  |         return self._f_loss_ref | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def lin_loss_exp(self): | ||||||
|  |         return self._lin_loss_exp | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def lin_attenuation(self): | ||||||
|  |         return self._lin_attenuation | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def effective_length(self): | ||||||
|  |         return self._effective_length | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def asymptotic_length(self): | ||||||
|  |         return self._asymptotic_length | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def raman_efficiency(self): | ||||||
|  |         return self._raman_efficiency | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def pumps_loss_coef(self): | ||||||
|  |         return self._pumps_loss_coef | ||||||
|  |  | ||||||
|  |     def asdict(self): | ||||||
|  |         dictionary = super().asdict() | ||||||
|  |         dictionary['loss_coef'] = self.loss_coef * 1e3 | ||||||
|  |         dictionary['length_units'] = 'm' | ||||||
|  |         return dictionary | ||||||
							
								
								
									
										742
									
								
								gnpy/core/science_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										742
									
								
								gnpy/core/science_utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,742 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | gnpy.core.science_utils | ||||||
|  | ======================= | ||||||
|  |  | ||||||
|  | Solver definitions to calculate the Raman effect and the nonlinear interference noise | ||||||
|  |  | ||||||
|  | The solvers take as input instances of the spectral information, the fiber and the simulation parameters | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | from numpy import interp, pi, zeros, shape, where, cos, reshape, array, append, ones, argsort, nan, exp, arange, sqrt, \ | ||||||
|  |     empty, vstack, trapz, arcsinh, clip, abs, sum | ||||||
|  | from operator import attrgetter | ||||||
|  | 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 math import isclose | ||||||
|  |  | ||||||
|  | from gnpy.core.utils import db2lin, lin2db | ||||||
|  | from gnpy.core.exceptions import EquipmentConfigError | ||||||
|  |  | ||||||
|  | logger = getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def propagate_raman_fiber(fiber, *carriers): | ||||||
|  |     simulation = Simulation.get_simulation() | ||||||
|  |     sim_params = simulation.sim_params | ||||||
|  |     raman_params = sim_params.raman_params | ||||||
|  |     nli_params = sim_params.nli_params | ||||||
|  |     # apply input attenuation to carriers | ||||||
|  |     attenuation_in = db2lin(fiber.params.con_in + fiber.params.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) | ||||||
|  |  | ||||||
|  |     # evaluate fiber attenuation involving also SRS if required by sim_params | ||||||
|  |     raman_solver = fiber.raman_solver | ||||||
|  |     raman_solver.carriers = carriers | ||||||
|  |     raman_solver.raman_pumps = fiber.raman_pumps | ||||||
|  |     stimulated_raman_scattering = raman_solver.stimulated_raman_scattering | ||||||
|  |  | ||||||
|  |     fiber_attenuation = (stimulated_raman_scattering.rho[:, -1])**-2 | ||||||
|  |     if not raman_params.flag_raman: | ||||||
|  |         fiber_attenuation = tuple(fiber.params.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 fiber.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.params.con_out) | ||||||
|  |     nli_solver = fiber.nli_solver | ||||||
|  |     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.nli_params.computed_channels): | ||||||
|  |         resolution_param = frequency_resolution(carrier, carriers, sim_params, fiber) | ||||||
|  |         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 = 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): | ||||||
|  |     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 * 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 * pi ** 2 * grid_size) | ||||||
|  |  | ||||||
|  |     grid_size = sim_params.nli_params.wdm_grid_size | ||||||
|  |     delta_z = sim_params.raman_params.space_resolution | ||||||
|  |     alpha0 = fiber.alpha0() | ||||||
|  |     beta2 = fiber.params.beta2 | ||||||
|  |     k_tol = sim_params.nli_params.dispersion_tolerance | ||||||
|  |     phi_tol = sim_params.nli_params.phase_shift_tolerance | ||||||
|  |     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 = zeros(shape(f)) | ||||||
|  |     for carrier in carriers: | ||||||
|  |         f_nch = carrier.frequency | ||||||
|  |         g_ch = carrier.power.signal / carrier.baud_rate | ||||||
|  |         ts = 1 / carrier.baud_rate | ||||||
|  |         pass_band = (1 - carrier.roll_off) / (2 / carrier.baud_rate) | ||||||
|  |         stop_band = (1 + carrier.roll_off) / (2 / carrier.baud_rate) | ||||||
|  |         ff = abs(f - f_nch) | ||||||
|  |         tf = ff - pass_band | ||||||
|  |         if carrier.roll_off == 0: | ||||||
|  |             psd = where(tf <= 0, g_ch, 0.) + psd | ||||||
|  |         else: | ||||||
|  |             psd = g_ch * (where(tf <= 0, 1., 0.) + 1 / 2 * (1 + cos(pi * ts / carrier.roll_off * tf)) * | ||||||
|  |                           where(tf > 0, 1., 0.) * where(abs(ff) <= stop_band, 1., 0.)) + psd | ||||||
|  |     return psd | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Simulation: | ||||||
|  |     _shared_dict = {} | ||||||
|  |  | ||||||
|  |     def __init__(self): | ||||||
|  |         if type(self) == Simulation: | ||||||
|  |             raise NotImplementedError('Simulation cannot be instatiated') | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def set_params(cls, sim_params): | ||||||
|  |         cls._shared_dict['sim_params'] = sim_params | ||||||
|  |  | ||||||
|  |     @classmethod | ||||||
|  |     def get_simulation(cls): | ||||||
|  |         self = cls.__new__(cls) | ||||||
|  |         return self | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def sim_params(self): | ||||||
|  |         return self._shared_dict['sim_params'] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class SpontaneousRamanScattering: | ||||||
|  |     def __init__(self, frequency, z, power): | ||||||
|  |         self.frequency = frequency | ||||||
|  |         self.z = z | ||||||
|  |         self.power = power | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StimulatedRamanScattering: | ||||||
|  |     def __init__(self, frequency, z, rho, power): | ||||||
|  |         self.frequency = frequency | ||||||
|  |         self.z = z | ||||||
|  |         self.rho = rho | ||||||
|  |         self.power = power | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class RamanSolver: | ||||||
|  |     def __init__(self, fiber=None): | ||||||
|  |         """ Initialize the Raman solver object. | ||||||
|  |         :param fiber: instance of elements.py/Fiber. | ||||||
|  |         :param carriers: tuple of carrier objects | ||||||
|  |         :param raman_pumps: tuple containing pumps characteristics | ||||||
|  |         """ | ||||||
|  |         self._fiber = fiber | ||||||
|  |         self._carriers = None | ||||||
|  |         self._raman_pumps = None | ||||||
|  |         self._stimulated_raman_scattering = None | ||||||
|  |         self._spontaneous_raman_scattering = None | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def fiber(self): | ||||||
|  |         return self._fiber | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def carriers(self): | ||||||
|  |         return self._carriers | ||||||
|  |  | ||||||
|  |     @carriers.setter | ||||||
|  |     def carriers(self, carriers): | ||||||
|  |         self._carriers = carriers | ||||||
|  |         self._spontaneous_raman_scattering = None | ||||||
|  |         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 stimulated_raman_scattering(self): | ||||||
|  |         if self._stimulated_raman_scattering is None: | ||||||
|  |             self.calculate_stimulated_raman_scattering(self.carriers, self.raman_pumps) | ||||||
|  |         return self._stimulated_raman_scattering | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def spontaneous_raman_scattering(self): | ||||||
|  |         if self._spontaneous_raman_scattering is None: | ||||||
|  |             self.calculate_spontaneous_raman_scattering(self.carriers, self.raman_pumps) | ||||||
|  |         return self._spontaneous_raman_scattering | ||||||
|  |  | ||||||
|  |     def calculate_spontaneous_raman_scattering(self, carriers, raman_pumps): | ||||||
|  |         raman_efficiency = self.fiber.params.raman_efficiency | ||||||
|  |         temperature = self.fiber.operational['temperature'] | ||||||
|  |  | ||||||
|  |         logger.debug('Start computing fiber Spontaneous Raman Scattering') | ||||||
|  |         power_spectrum, freq_array, prop_direct, bn_array = self._compute_power_spectrum(carriers, raman_pumps) | ||||||
|  |  | ||||||
|  |         alphap_fiber = self.fiber.alpha(freq_array) | ||||||
|  |  | ||||||
|  |         freq_diff = abs(freq_array - 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 = zeros(freq_array.shape) | ||||||
|  |  | ||||||
|  |         # calculate ase power | ||||||
|  |         int_spontaneous_raman = self._int_spontaneous_raman(z_array, self._stimulated_raman_scattering.power, | ||||||
|  |                                                             alphap_fiber, freq_array, cr, freq_diff, ase_bc, | ||||||
|  |                                                             bn_array, temperature) | ||||||
|  |  | ||||||
|  |         spontaneous_raman_scattering = SpontaneousRamanScattering(freq_array, z_array, int_spontaneous_raman.x) | ||||||
|  |         logger.debug("Spontaneous Raman Scattering evaluated successfully") | ||||||
|  |         self._spontaneous_raman_scattering = 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 = array([]) | ||||||
|  |         f_array = array([]) | ||||||
|  |         noise_bandwidth_array = array([]) | ||||||
|  |         for carrier in sorted(carriers, key=attrgetter('frequency')): | ||||||
|  |             f_array = append(f_array, carrier.frequency) | ||||||
|  |             pow_array = append(pow_array, carrier.power.signal) | ||||||
|  |             ref_bw = carrier.baud_rate | ||||||
|  |             noise_bandwidth_array = append(noise_bandwidth_array, ref_bw) | ||||||
|  |  | ||||||
|  |         propagation_direction = ones(len(f_array)) | ||||||
|  |  | ||||||
|  |         # Raman pump power spectrum | ||||||
|  |         if raman_pumps: | ||||||
|  |             for pump in raman_pumps: | ||||||
|  |                 pow_array = append(pow_array, pump.power) | ||||||
|  |                 f_array = append(f_array, pump.frequency) | ||||||
|  |                 direction = +1 if pump.propagation_direction.lower() == 'coprop' else -1 | ||||||
|  |                 propagation_direction = append(propagation_direction, direction) | ||||||
|  |                 noise_bandwidth_array = append(noise_bandwidth_array, ref_bw) | ||||||
|  |  | ||||||
|  |         # Final sorting | ||||||
|  |         ind = 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() | ||||||
|  |  | ||||||
|  |         simulation = Simulation.get_simulation() | ||||||
|  |         sim_params = simulation.sim_params | ||||||
|  |  | ||||||
|  |         dx = sim_params.raman_params.space_resolution | ||||||
|  |         h = ph.value('Planck constant') | ||||||
|  |         kb = ph.value('Boltzmann constant') | ||||||
|  |  | ||||||
|  |         power_ase = nan * 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 / (exp((h * freq_diff[f_ind, f_ind + 1:]) / (kb * temperature)) - 1) | ||||||
|  |  | ||||||
|  |             int_fiber_loss = -alphap_fiber[f_ind] * z_array | ||||||
|  |             int_raman_loss = sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(), | ||||||
|  |                                     axis=0) | ||||||
|  |             int_raman_gain = 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 = 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] * exp(int_gain_loss) | ||||||
|  |             ase_evolution = exp(int_gain_loss) * cumtrapz(new_ase * 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 | ||||||
|  |         return spontaneous_raman_scattering | ||||||
|  |  | ||||||
|  |     def calculate_stimulated_raman_scattering(self, carriers, raman_pumps): | ||||||
|  |         """ Returns stimulated Raman scattering solution including | ||||||
|  |         fiber gain/loss profile. | ||||||
|  |         :return: None | ||||||
|  |         """ | ||||||
|  |         # fiber parameters | ||||||
|  |         fiber_length = self.fiber.params.length | ||||||
|  |         raman_efficiency = self.fiber.params.raman_efficiency | ||||||
|  |         simulation = Simulation.get_simulation() | ||||||
|  |         sim_params = simulation.sim_params | ||||||
|  |  | ||||||
|  |         if not sim_params.raman_params.flag_raman: | ||||||
|  |             raman_efficiency['cr'] = zeros(len(raman_efficiency['cr'])) | ||||||
|  |         # raman solver parameters | ||||||
|  |         z_resolution = sim_params.raman_params.space_resolution | ||||||
|  |         tolerance = sim_params.raman_params.tolerance | ||||||
|  |  | ||||||
|  |         logger.debug('Start computing fiber Stimulated Raman Scattering') | ||||||
|  |  | ||||||
|  |         power_spectrum, freq_array, prop_direct, _ = self._compute_power_spectrum(carriers, raman_pumps) | ||||||
|  |  | ||||||
|  |         alphap_fiber = self.fiber.alpha(freq_array) | ||||||
|  |  | ||||||
|  |         freq_diff = abs(freq_array - 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 = arange(0, fiber_length + 1, z_resolution) | ||||||
|  |  | ||||||
|  |         def ode_function(z, p): | ||||||
|  |             return self._ode_stimulated_raman(z, p, alphap_fiber, freq_array, cr, prop_direct) | ||||||
|  |  | ||||||
|  |         def boundary_residual(ya, yb): | ||||||
|  |             return 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 | ||||||
|  |         bvp_solution = solve_bvp(ode_function, boundary_residual, z, initial_guess_conditions, tol=tolerance) | ||||||
|  |  | ||||||
|  |         rho = (bvp_solution.y.transpose() / power_spectrum).transpose() | ||||||
|  |         rho = sqrt(rho)    # From power attenuation to field attenuation | ||||||
|  |         stimulated_raman_scattering = StimulatedRamanScattering(freq_array, bvp_solution.x, rho, bvp_solution.y) | ||||||
|  |  | ||||||
|  |         self._stimulated_raman_scattering = stimulated_raman_scattering | ||||||
|  |  | ||||||
|  |     def _residuals_stimulated_raman(self, ya, yb, power_spectrum, prop_direct): | ||||||
|  |  | ||||||
|  |         computed_boundary_value = 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 = 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, :] = exp(-alphap_fiber[f_index] * z) * power_slice | ||||||
|  |             else: | ||||||
|  |                 power_guess[f_index, :] = 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 = nan * 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 = sum(cr_raman[f_ind + 1:] * power_spectrum[f_ind + 1:, z_ind]) | ||||||
|  |                 raman_loss = 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 vstack(dpdz) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class NliSolver: | ||||||
|  |     """ This class implements the NLI models. | ||||||
|  |         Model and method can be specified in `sim_params.nli_params.method`. | ||||||
|  |         List of implemented methods: | ||||||
|  |         'gn_model_analytic': eq. 120 from arXiv:1209.0394 | ||||||
|  |         'ggn_spectrally_separated_xpm_spm': XPM plus SPM | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def __init__(self, fiber=None): | ||||||
|  |         """ Initialize the Nli solver object. | ||||||
|  |         :param fiber: instance of elements.py/Fiber. | ||||||
|  |         """ | ||||||
|  |         self._fiber = fiber | ||||||
|  |         self._stimulated_raman_scattering = None | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def fiber(self): | ||||||
|  |         return self._fiber | ||||||
|  |  | ||||||
|  |     @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 | ||||||
|  |  | ||||||
|  |     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. | ||||||
|  |         """ | ||||||
|  |         simulation = Simulation.get_simulation() | ||||||
|  |         sim_params = simulation.sim_params | ||||||
|  |         if 'gn_model_analytic' == sim_params.nli_params.nli_method_name.lower(): | ||||||
|  |             carrier_nli = self._gn_analytic(carrier, *carriers) | ||||||
|  |         elif 'ggn_spectrally_separated' in sim_params.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 {sim_params.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, cut_carrier, *carriers): | ||||||
|  |         cut_index = cut_carrier.channel_number - 1 | ||||||
|  |         simulation = Simulation.get_simulation() | ||||||
|  |         sim_params = simulation.sim_params | ||||||
|  |         # Matrix initialization | ||||||
|  |         matrix_size = max(carriers, key=lambda x: getattr(x, 'channel_number')).channel_number | ||||||
|  |         eta_matrix = zeros(shape=(matrix_size, matrix_size)) | ||||||
|  |  | ||||||
|  |         # SPM | ||||||
|  |         logger.debug(f'Start computing SPM on channel #{cut_carrier.channel_number}') | ||||||
|  |         # SPM GGN | ||||||
|  |         if 'ggn' in sim_params.nli_params.nli_method_name.lower(): | ||||||
|  |             partial_nli = self._generalized_spectrally_separated_spm(cut_carrier) | ||||||
|  |         # SPM GN | ||||||
|  |         elif 'gn' in sim_params.nli_params.nli_method_name.lower(): | ||||||
|  |             partial_nli = self._gn_analytic(cut_carrier, *[cut_carrier]) | ||||||
|  |         eta_matrix[cut_index, cut_index] = partial_nli / (cut_carrier.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 #{cut_carrier.channel_number} ' | ||||||
|  |                              f'from channel #{pump_carrier.channel_number}') | ||||||
|  |                 # XPM GGN | ||||||
|  |                 if 'ggn' in sim_params.nli_params.nli_method_name.lower(): | ||||||
|  |                     partial_nli = self._generalized_spectrally_separated_xpm(cut_carrier, pump_carrier) | ||||||
|  |                 # XPM GGN | ||||||
|  |                 elif 'gn' in sim_params.nli_params.nli_method_name.lower(): | ||||||
|  |                     partial_nli = self._gn_analytic(cut_carrier, *[pump_carrier]) | ||||||
|  |                 eta_matrix[pump_index, pump_index] = \ | ||||||
|  |                     partial_nli / (cut_carrier.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 | ||||||
|  |         """ | ||||||
|  |         beta2 = self.fiber.params.beta2 | ||||||
|  |         gamma = self.fiber.params.gamma | ||||||
|  |         effective_length = self.fiber.params.effective_length | ||||||
|  |         asymptotic_length = self.fiber.params.asymptotic_length | ||||||
|  |  | ||||||
|  |         g_nli = 0 | ||||||
|  |         for interfering_carrier in carriers: | ||||||
|  |             g_interfering = interfering_carrier.power.signal / interfering_carrier.baud_rate | ||||||
|  |             g_signal = carrier.power.signal / carrier.baud_rate | ||||||
|  |             g_nli += g_interfering**2 * g_signal \ | ||||||
|  |                 * _psi(carrier, interfering_carrier, beta2=beta2, asymptotic_length=asymptotic_length) | ||||||
|  |         g_nli *= (16.0 / 27.0) * (gamma * effective_length) ** 2 /\ | ||||||
|  |                  (2 * 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): | ||||||
|  |         gamma = self.fiber.params.gamma | ||||||
|  |         simulation = Simulation.get_simulation() | ||||||
|  |         sim_params = simulation.sim_params | ||||||
|  |         f_cut_resolution = sim_params.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) * 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, cut_carrier, pump_carrier): | ||||||
|  |         gamma = self.fiber.params.gamma | ||||||
|  |         simulation = Simulation.get_simulation() | ||||||
|  |         sim_params = simulation.sim_params | ||||||
|  |         delta_index = pump_carrier.channel_number - cut_carrier.channel_number | ||||||
|  |         f_cut_resolution = sim_params.nli_params.f_cut_resolution[f'delta_{delta_index}'] | ||||||
|  |         f_pump_resolution = sim_params.nli_params.f_pump_resolution | ||||||
|  |         f_eval = cut_carrier.frequency | ||||||
|  |         g_pump = (pump_carrier.power.signal / pump_carrier.baud_rate) | ||||||
|  |         g_cut = (cut_carrier.power.signal / cut_carrier.baud_rate) | ||||||
|  |         frequency_offset_threshold = self._frequency_offset_threshold(pump_carrier.baud_rate) | ||||||
|  |         if abs(cut_carrier.frequency - pump_carrier.frequency) <= frequency_offset_threshold: | ||||||
|  |             xpm_nli = cut_carrier.baud_rate * (16.0 / 27.0) * gamma ** 2 * g_pump**2 * g_cut * \ | ||||||
|  |                 2 * self._generalized_psi(cut_carrier, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution) | ||||||
|  |         else: | ||||||
|  |             xpm_nli = cut_carrier.baud_rate * (16.0 / 27.0) * gamma ** 2 * g_pump**2 * g_cut * \ | ||||||
|  |                 2 * self._fast_generalized_psi(cut_carrier, pump_carrier, f_eval, f_cut_resolution) | ||||||
|  |         return xpm_nli | ||||||
|  |  | ||||||
|  |     def _fast_generalized_psi(self, cut_carrier, 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.alpha0(f_eval) | ||||||
|  |         beta2 = self.fiber.params.beta2 | ||||||
|  |         beta3 = self.fiber.params.beta3 | ||||||
|  |         f_ref_beta = self.fiber.params.ref_frequency | ||||||
|  |         z = self.stimulated_raman_scattering.z | ||||||
|  |         frequency_rho = self.stimulated_raman_scattering.frequency | ||||||
|  |         rho_norm = self.stimulated_raman_scattering.rho * exp(abs(alpha0) * z / 2) | ||||||
|  |         if len(frequency_rho) == 1: | ||||||
|  |             def rho_function(f): return 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 = 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 = arange(cut_carrier.frequency, | ||||||
|  |                           cut_carrier.frequency + (cut_carrier.baud_rate * (1 + cut_carrier.roll_off) / 2), | ||||||
|  |                           f_cut_resolution)  # Only positive f2 is used since integrand_f2 is symmetric | ||||||
|  |  | ||||||
|  |         integrand_f1 = zeros(len(f1_array)) | ||||||
|  |         for f1_index, f1 in enumerate(f1_array): | ||||||
|  |             delta_beta = 4 * pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \ | ||||||
|  |                 (beta2 + 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 * 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, cut_carrier, 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.alpha0(f_eval) | ||||||
|  |         beta2 = self.fiber.params.beta2 | ||||||
|  |         beta3 = self.fiber.params.beta3 | ||||||
|  |         f_ref_beta = self.fiber.params.ref_frequency | ||||||
|  |         z = self.stimulated_raman_scattering.z | ||||||
|  |         frequency_rho = self.stimulated_raman_scattering.frequency | ||||||
|  |         rho_norm = self.stimulated_raman_scattering.rho * exp(abs(alpha0) * z / 2) | ||||||
|  |         if len(frequency_rho) == 1: | ||||||
|  |             def rho_function(f): return 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 = 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 = arange(cut_carrier.frequency - (cut_carrier.baud_rate * (1 + cut_carrier.roll_off) / 2), | ||||||
|  |                           cut_carrier.frequency + (cut_carrier.baud_rate * (1 + cut_carrier.roll_off) / 2), | ||||||
|  |                           f_cut_resolution) | ||||||
|  |         psd1 = raised_cosine_comb(f1_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal) | ||||||
|  |  | ||||||
|  |         integrand_f1 = 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, cut_carrier) * (cut_carrier.baud_rate / cut_carrier.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 * pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \ | ||||||
|  |                 (beta2 + 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] = trapz(integrand_f2, f2_array) | ||||||
|  |         generalized_psi = 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 * exp(w * z[-1]) - rho_norm_pump[0]**2 * 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 * (exp(w * z[z_ind + 1]) - exp(w * z[z_ind])) / (w**2) | ||||||
|  |         generalized_rho_nli = 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 | ||||||
|  |         beta2 = abs(self.fiber.params.beta2) | ||||||
|  |         freq_offset_th = ((k_ref * delta_f_ref) * rs_ref * beta2_ref) / (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 = arcsinh(0.5 * pi**2 * asymptotic_length * abs(beta2) * carrier.baud_rate**2) | ||||||
|  |     else:  # XCI, XPM | ||||||
|  |         delta_f = carrier.frequency - interfering_carrier.frequency | ||||||
|  |         psi = arcsinh(pi**2 * asymptotic_length * abs(beta2) * | ||||||
|  |                       carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate)) | ||||||
|  |         psi -= arcsinh(pi**2 * asymptotic_length * abs(beta2) * | ||||||
|  |                        carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate)) | ||||||
|  |     return psi | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def estimate_nf_model(type_variety, gain_min, gain_max, nf_min, nf_max): | ||||||
|  |     if nf_min < -10: | ||||||
|  |         raise EquipmentConfigError(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}') | ||||||
|  |     if nf_max < -10: | ||||||
|  |         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 | ||||||
|  |     # dB g1a:   first stage gain - internal VOA attenuation | ||||||
|  |     # nf1, nf2: first and second stage coils | ||||||
|  |     #           calculated by solving nf_{min,max} = nf1 + nf2 / g1a{min,max} | ||||||
|  |     delta_p = 5 | ||||||
|  |     g1a_min = gain_min - (gain_max - gain_min) - delta_p | ||||||
|  |     g1a_max = gain_max - delta_p | ||||||
|  |     nf2 = lin2db((db2lin(nf_min) - db2lin(nf_max)) / | ||||||
|  |                  (1 / db2lin(g1a_max) - 1 / db2lin(g1a_min))) | ||||||
|  |     nf1 = lin2db(db2lin(nf_min) - db2lin(nf2) / db2lin(g1a_max)) | ||||||
|  |  | ||||||
|  |     if nf1 < 4: | ||||||
|  |         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: | ||||||
|  |     #    nf2 should be nf1 + 0.3 < nf2 < nf1 + 2 | ||||||
|  |     # If not, recompute and check delta_p | ||||||
|  |     if not nf1 + 0.3 < nf2 < nf1 + 2: | ||||||
|  |         nf2 = clip(nf2, nf1 + 0.3, nf1 + 2) | ||||||
|  |         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 < 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}') | ||||||
|  |     # 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): | ||||||
|  |         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): | ||||||
|  |         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 | ||||||
| @@ -1,2 +0,0 @@ | |||||||
| UNITS = {'m': 1, |  | ||||||
|          'km': 1E3} |  | ||||||
| @@ -1,71 +1,140 @@ | |||||||
| #!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||||
| # -*- coding: utf-8 -*- | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
| import json | """ | ||||||
|  | gnpy.core.utils | ||||||
|  | =============== | ||||||
|  |  | ||||||
| import numpy as np | This module contains utility functions that are used with gnpy. | ||||||
| from numpy import pi, cos, sqrt, log10 | """ | ||||||
|  |  | ||||||
|  | from csv import writer | ||||||
|  | from numpy import pi, cos, sqrt, log10, linspace, zeros, shape, where, logical_and | ||||||
|  | from scipy import constants | ||||||
|  |  | ||||||
|  | from gnpy.core.exceptions import ConfigurationError | ||||||
|  |  | ||||||
|  |  | ||||||
| def load_json(filename): | def write_csv(obj, filename): | ||||||
|     with open(filename, 'r') as f: |  | ||||||
|         data = json.load(f) |  | ||||||
|     return data |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def save_json(obj, filename): |  | ||||||
|     with open(filename, 'w') as f: |  | ||||||
|         json.dump(obj, f) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def c(): |  | ||||||
|     """ |     """ | ||||||
|     Returns the speed of light in meters per second |     Convert dictionary items to a CSV file the dictionary format: | ||||||
|  |     :: | ||||||
|  |  | ||||||
|  |         {'result category 1': | ||||||
|  |                         [ | ||||||
|  |                         # 1st line of results | ||||||
|  |                         {'header 1' : value_xxx, | ||||||
|  |                          'header 2' : value_yyy}, | ||||||
|  |                          # 2nd line of results: same headers, different results | ||||||
|  |                         {'header 1' : value_www, | ||||||
|  |                          'header 2' : value_zzz} | ||||||
|  |                         ], | ||||||
|  |         'result_category 2': | ||||||
|  |                         [ | ||||||
|  |                         {},{} | ||||||
|  |                         ] | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     The generated csv file will be: | ||||||
|  |     :: | ||||||
|  |  | ||||||
|  |         result_category 1 | ||||||
|  |         header 1    header 2 | ||||||
|  |         value_xxx   value_yyy | ||||||
|  |         value_www   value_zzz | ||||||
|  |         result_category 2 | ||||||
|  |         ... | ||||||
|     """ |     """ | ||||||
|     return 299792458.0 |     with open(filename, 'w', encoding='utf-8') as f: | ||||||
|  |         w = writer(f) | ||||||
|  |         for data_key, data_list in obj.items(): | ||||||
|  |             # main header | ||||||
|  |             w.writerow([data_key]) | ||||||
|  |             # sub headers: | ||||||
|  |             headers = [_ for _ in data_list[0].keys()] | ||||||
|  |             w.writerow(headers) | ||||||
|  |             for data_dict in data_list: | ||||||
|  |                 w.writerow([_ for _ in data_dict.values()]) | ||||||
|  |  | ||||||
|  |  | ||||||
| def itufs(spacing, startf=191.35, stopf=196.10): | def arrange_frequencies(length, start, stop): | ||||||
|     """Creates an array of frequencies whose default range is |     """Create an array of frequencies | ||||||
|     191.35-196.10 THz |  | ||||||
|  |  | ||||||
|     :param spacing: Frequency spacing in THz |     :param length: number of elements | ||||||
|     :param starf: Start frequency in THz |     :param start: Start frequency in THz | ||||||
|     :param stopf: Stop frequency in THz |     :param stop: Stop frequency in THz | ||||||
|     :type spacing: float |     :type length: integer | ||||||
|     :type startf: float |     :type start: float | ||||||
|     :type stopf: float |     :type stop: float | ||||||
|     :return an array of frequnecies determined by the spacing parameter |     :return: an array of frequencies determined by the spacing parameter | ||||||
|     :rtype: numpy.ndarray |     :rtype: numpy.ndarray | ||||||
|     """ |     """ | ||||||
|     return np.arange(startf, stopf + spacing / 2, spacing) |     return linspace(start, stop, length) | ||||||
|  |  | ||||||
|  |  | ||||||
| def h(): |  | ||||||
|     """ |  | ||||||
|     Returns plank's constant in J*s |  | ||||||
|     """ |  | ||||||
|     return 6.62607004e-34 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def lin2db(value): | def lin2db(value): | ||||||
|  |     """Convert linear unit to logarithmic (dB) | ||||||
|  |  | ||||||
|  |     >>> lin2db(0.001) | ||||||
|  |     -30.0 | ||||||
|  |     >>> round(lin2db(1.0), 2) | ||||||
|  |     0.0 | ||||||
|  |     >>> round(lin2db(1.26), 2) | ||||||
|  |     1.0 | ||||||
|  |     >>> round(lin2db(10.0), 2) | ||||||
|  |     10.0 | ||||||
|  |     >>> round(lin2db(100.0), 2) | ||||||
|  |     20.0 | ||||||
|  |     """ | ||||||
|     return 10 * log10(value) |     return 10 * log10(value) | ||||||
|  |  | ||||||
|  |  | ||||||
| def db2lin(value): | def db2lin(value): | ||||||
|  |     """Convert logarithimic units to linear | ||||||
|  |  | ||||||
|  |     >>> round(db2lin(10.0), 2) | ||||||
|  |     10.0 | ||||||
|  |     >>> round(db2lin(20.0), 2) | ||||||
|  |     100.0 | ||||||
|  |     >>> round(db2lin(1.0), 2) | ||||||
|  |     1.26 | ||||||
|  |     >>> round(db2lin(0.0), 2) | ||||||
|  |     1.0 | ||||||
|  |     >>> round(db2lin(-10.0), 2) | ||||||
|  |     0.1 | ||||||
|  |     """ | ||||||
|     return 10**(value / 10) |     return 10**(value / 10) | ||||||
|  |  | ||||||
|  |  | ||||||
| def wavelength2freq(value): | def round2float(number, step): | ||||||
|     """ Converts wavelength units to frequeuncy units. |     step = round(step, 1) | ||||||
|     """ |     if step >= 0.01: | ||||||
|     return c() / value |         number = round(number / step, 0) | ||||||
|  |         number = round(number * step, 1) | ||||||
|  |     else: | ||||||
|  |         number = round(number, 2) | ||||||
|  |     return number | ||||||
|  |  | ||||||
|  |  | ||||||
|  | wavelength2freq = constants.lambda2nu | ||||||
|  | freq2wavelength = constants.nu2lambda | ||||||
|  |  | ||||||
|  |  | ||||||
| def freq2wavelength(value): | def freq2wavelength(value): | ||||||
|     """ Converts frequency units to wavelength units. |     """ Converts frequency units to wavelength units. | ||||||
|  |  | ||||||
|  |     >>> round(freq2wavelength(191.35e12) * 1e9, 3) | ||||||
|  |     1566.723 | ||||||
|  |     >>> round(freq2wavelength(196.1e12) * 1e9, 3) | ||||||
|  |     1528.773 | ||||||
|     """ |     """ | ||||||
|     return c() / value |     return constants.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): | def deltawl2deltaf(delta_wl, wavelength): | ||||||
| @@ -120,11 +189,109 @@ def rrc(ffs, baud_rate, alpha): | |||||||
|     Ts = 1 / baud_rate |     Ts = 1 / baud_rate | ||||||
|     l_lim = (1 - alpha) / (2 * Ts) |     l_lim = (1 - alpha) / (2 * Ts) | ||||||
|     r_lim = (1 + alpha) / (2 * Ts) |     r_lim = (1 + alpha) / (2 * Ts) | ||||||
|     hf = np.zeros(np.shape(ffs)) |     hf = zeros(shape(ffs)) | ||||||
|     slope_inds = np.where( |     slope_inds = where( | ||||||
|         np.logical_and(np.abs(ffs) > l_lim, np.abs(ffs) < r_lim)) |         logical_and(abs(ffs) > l_lim, abs(ffs) < r_lim)) | ||||||
|     hf[slope_inds] = 0.5 * (1 + cos((pi * Ts / alpha) * |     hf[slope_inds] = 0.5 * (1 + cos((pi * Ts / alpha) * | ||||||
|                                     (np.abs(ffs[slope_inds]) - l_lim))) |                                     (abs(ffs[slope_inds]) - l_lim))) | ||||||
|     p_inds = np.where(np.logical_and(np.abs(ffs) > 0, np.abs(ffs) < l_lim)) |     p_inds = where(logical_and(abs(ffs) > 0, abs(ffs) < l_lim)) | ||||||
|     hf[p_inds] = 1 |     hf[p_inds] = 1 | ||||||
|     return sqrt(hf) |     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 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def automatic_nch(f_min, f_max, spacing): | ||||||
|  |     """How many channels are available in the spectrum | ||||||
|  |  | ||||||
|  |     :param f_min Lowest frequenecy [Hz] | ||||||
|  |     :param f_max Highest frequency [Hz] | ||||||
|  |     :param spacing Channel width [Hz] | ||||||
|  |     :return Number of uniform channels | ||||||
|  |  | ||||||
|  |     >>> automatic_nch(191.325e12, 196.125e12, 50e9) | ||||||
|  |     96 | ||||||
|  |     >>> automatic_nch(193.475e12, 193.525e12, 50e9) | ||||||
|  |     1 | ||||||
|  |     """ | ||||||
|  |     return int((f_max - f_min) // spacing) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def automatic_fmax(f_min, spacing, nch): | ||||||
|  |     """Find the high-frequenecy boundary of a spectrum | ||||||
|  |  | ||||||
|  |     :param f_min Start of the spectrum (lowest frequency edge) [Hz] | ||||||
|  |     :param spacing Grid/channel spacing [Hz] | ||||||
|  |     :param nch Number of channels | ||||||
|  |     :return End of the spectrum (highest frequency) [Hz] | ||||||
|  |  | ||||||
|  |     >>> automatic_fmax(191.325e12, 50e9, 96) | ||||||
|  |     196125000000000.0 | ||||||
|  |     """ | ||||||
|  |     return f_min + spacing * nch | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def convert_length(value, units): | ||||||
|  |     """Convert length into basic SI units | ||||||
|  |  | ||||||
|  |     >>> convert_length(1, 'km') | ||||||
|  |     1000.0 | ||||||
|  |     >>> convert_length(2.0, 'km') | ||||||
|  |     2000.0 | ||||||
|  |     >>> convert_length(123, 'm') | ||||||
|  |     123.0 | ||||||
|  |     >>> convert_length(123.0, 'm') | ||||||
|  |     123.0 | ||||||
|  |     >>> convert_length(42.1, 'km') | ||||||
|  |     42100.0 | ||||||
|  |     >>> convert_length(666, 'yards') | ||||||
|  |     Traceback (most recent call last): | ||||||
|  |         ... | ||||||
|  |     gnpy.core.exceptions.ConfigurationError: Cannot convert length in "yards" into meters | ||||||
|  |     """ | ||||||
|  |     if units == 'm': | ||||||
|  |         return value * 1e0 | ||||||
|  |     elif units == 'km': | ||||||
|  |         return value * 1e3 | ||||||
|  |     else: | ||||||
|  |         raise ConfigurationError(f'Cannot convert length in "{units}" into meters') | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10278
									
								
								gnpy/example-data/CORONET_Global_Topology.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10278
									
								
								gnpy/example-data/CORONET_Global_Topology.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								gnpy/example-data/CORONET_Global_Topology.xls
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								gnpy/example-data/CORONET_Global_Topology.xls
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										160
									
								
								gnpy/example-data/Juniper-BoosterHG.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								gnpy/example-data/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 | ||||||
|  |     ] | ||||||
|  | } | ||||||
							
								
								
									
										104
									
								
								gnpy/example-data/create_eqpt_sheet.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								gnpy/example-data/create_eqpt_sheet.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | |||||||
|  | #!/usr/bin/env python3 | ||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | create_eqpt_sheet.py | ||||||
|  | ==================== | ||||||
|  |  | ||||||
|  | XLS parser that can be called to create a "City" column in the "Eqpt" sheet. | ||||||
|  |  | ||||||
|  | If not present in the "Nodes" sheet, the "Type" column will be implicitly | ||||||
|  | determined based on the topology. | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | from xlrd import open_workbook | ||||||
|  | from argparse import ArgumentParser | ||||||
|  |  | ||||||
|  | PARSER = ArgumentParser() | ||||||
|  | PARSER.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls', | ||||||
|  |                     help='create the mandatory columns in Eqpt sheet') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def ALL_ROWS(sh, start=0): | ||||||
|  |     return (sh.row(x) for x in range(start, sh.nrows)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  |     def __repr__(self): | ||||||
|  |         return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n' | ||||||
|  |  | ||||||
|  |     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): | ||||||
|  |     """ 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 = 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]) | ||||||
|  |  | ||||||
|  |         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 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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', 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') | ||||||
|  |  | ||||||
|  |         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.') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     ARGS = PARSER.parse_args() | ||||||
|  |     create_eqt_template(read_excel(ARGS.workbook), ARGS.workbook) | ||||||
							
								
								
									
										106
									
								
								gnpy/example-data/default_edfa_config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								gnpy/example-data/default_edfa_config.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | { | ||||||
|  |     "nf_ripple": [ | ||||||
|  |         0.0 | ||||||
|  |     ], | ||||||
|  |     "gain_ripple": [ | ||||||
|  |         0.0 | ||||||
|  |     ], | ||||||
|  |     "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 | ||||||
|  |     ] | ||||||
|  | } | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user