mirror of
				https://github.com/Telecominfraproject/oopt-gnpy.git
				synced 2025-11-03 19:47:46 +00:00 
			
		
		
		
	Compare commits
	
		
			388 Commits
		
	
	
		
			v1.2
			...
			experiment
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					049b077ee4 | ||
| 
						 | 
					ab2080a805 | ||
| 
						 | 
					8ab54e76df | ||
| 
						 | 
					0465397b1d | ||
| 
						 | 
					d3ec39d506 | ||
| 
						 | 
					bfe68a5948 | ||
| 
						 | 
					2ea3363613 | ||
| 
						 | 
					89cce6e6a3 | ||
| 
						 | 
					0f10ac706c | ||
| 
						 | 
					66d26f0ffa | ||
| 
						 | 
					3c96914482 | ||
| 
						 | 
					61b1e73362 | ||
| 
						 | 
					03435079cc | ||
| 
						 | 
					6661907c1d | ||
| 
						 | 
					fe811f725c | ||
| 
						 | 
					f015c6abed | ||
| 
						 | 
					8598e6591f | ||
| 
						 | 
					5e2259062c | ||
| 
						 | 
					d483802a86 | ||
| 
						 | 
					9a0eece69c | ||
| 
						 | 
					1657bfd05f | ||
| 
						 | 
					49bf558916 | ||
| 
						 | 
					99f44a597b | ||
| 
						 | 
					71293c1c18 | ||
| 
						 | 
					bd7c70f902 | ||
| 
						 | 
					20c92d4338 | ||
| 
						 | 
					f0158e7202 | ||
| 
						 | 
					62408ddc98 | ||
| 
						 | 
					b4f87b36db | ||
| 
						 | 
					9f49a115a1 | ||
| 
						 | 
					c7d2305589 | ||
| 
						 | 
					5826a649de | ||
| 
						 | 
					fa826391f6 | ||
| 
						 | 
					3481ba8ee3 | ||
| 
						 | 
					b4ab0b55de | ||
| 
						 | 
					0370b45d8a | ||
| 
						 | 
					468e689094 | ||
| 
						 | 
					aafd82b16d | ||
| 
						 | 
					60ee331153 | ||
| 
						 | 
					3a8ce74355 | ||
| 
						 | 
					fd44463238 | ||
| 
						 | 
					a21f3fe6ee | ||
| 
						 | 
					0ccbb2960c | ||
| 
						 | 
					84ba2da553 | ||
| 
						 | 
					e693d96ca1 | ||
| 
						 | 
					81cb7f8133 | ||
| 
						 | 
					3471969956 | ||
| 
						 | 
					7a0985c362 | ||
| 
						 | 
					b79a9e2e67 | ||
| 
						 | 
					1e037fe6f5 | ||
| 
						 | 
					0897be57c1 | ||
| 
						 | 
					4172b06b19 | ||
| 
						 | 
					32a4875e46 | ||
| 
						 | 
					c577a75725 | ||
| 
						 | 
					8827e0cf6f | ||
| 
						 | 
					b0012fe399 | ||
| 
						 | 
					31e634615b | ||
| 
						 | 
					8300a55e39 | ||
| 
						 | 
					5b939bc57a | ||
| 
						 | 
					2f1ab9cc50 | ||
| 
						 | 
					42ba3eb98d | ||
| 
						 | 
					9eb87fc8e1 | ||
| 
						 | 
					8fab9bb945 | ||
| 
						 | 
					1ead232a78 | ||
| 
						 | 
					b15c8c60ab | ||
| 
						 | 
					66bdeb0e4d | ||
| 
						 | 
					1a2e090104 | ||
| 
						 | 
					a8e280e29b | ||
| 
						 | 
					edb54b02ac | ||
| 
						 | 
					83d3f32fe0 | ||
| 
						 | 
					085a379592 | ||
| 
						 | 
					37bd5d0404 | ||
| 
						 | 
					f788b81d21 | ||
| 
						 | 
					2ff1ce6b34 | ||
| 
						 | 
					41a1e40d14 | ||
| 
						 | 
					921e8d2d3c | ||
| 
						 | 
					c009d28f7d | ||
| 
						 | 
					898eada097 | ||
| 
						 | 
					bdfc55e801 | ||
| 
						 | 
					57f264bedb | ||
| 
						 | 
					fbe4fa3cf0 | ||
| 
						 | 
					b2ef345f35 | ||
| 
						 | 
					471ea7dfba | ||
| 
						 | 
					1b52f638ff | ||
| 
						 | 
					84ab38a75f | ||
| 
						 | 
					916e5377f8 | ||
| 
						 | 
					534bfd881e | ||
| 
						 | 
					7c4015324d | ||
| 
						 | 
					8499ee52f4 | ||
| 
						 | 
					cc1123863c | ||
| 
						 | 
					ca382806f6 | ||
| 
						 | 
					3559fc61c2 | ||
| 
						 | 
					33581cdcc9 | ||
| 
						 | 
					991eb02964 | ||
| 
						 | 
					286e321a2d | ||
| 
						 | 
					56f158113d | ||
| 
						 | 
					024f6ff963 | ||
| 
						 | 
					8118a0f4f4 | ||
| 
						 | 
					eb89d8fd86 | ||
| 
						 | 
					a938c1738b | ||
| 
						 | 
					4f88882513 | ||
| 
						 | 
					4e8d8b7ddd | ||
| 
						 | 
					afb7d75749 | ||
| 
						 | 
					488d0e1fe8 | ||
| 
						 | 
					708442e4cd | ||
| 
						 | 
					a2d905dfb1 | ||
| 
						 | 
					d564fe3e2a | ||
| 
						 | 
					ea21cce1c0 | ||
| 
						 | 
					aa9b4aefbe | ||
| 
						 | 
					8655030e59 | ||
| 
						 | 
					8107ddeb79 | ||
| 
						 | 
					76c8e55f06 | ||
| 
						 | 
					a7b1ab47d8 | ||
| 
						 | 
					879f587ab9 | ||
| 
						 | 
					8af2d80219 | ||
| 
						 | 
					315eea1f55 | ||
| 
						 | 
					b61e541e15 | ||
| 
						 | 
					81f88e78c7 | ||
| 
						 | 
					87cc3dac00 | ||
| 
						 | 
					89e28cc7be | ||
| 
						 | 
					2ba29a78c5 | ||
| 
						 | 
					f990a6c1be | ||
| 
						 | 
					424e5a4786 | ||
| 
						 | 
					6a7a04ebb1 | ||
| 
						 | 
					0366fc2956 | ||
| 
						 | 
					48b7d71f02 | ||
| 
						 | 
					715baf2a1c | ||
| 
						 | 
					e55cea776e | ||
| 
						 | 
					b388d143fd | ||
| 
						 | 
					c592c572d8 | ||
| 
						 | 
					dfa0a26a28 | ||
| 
						 | 
					609cd94798 | ||
| 
						 | 
					022f743db1 | ||
| 
						 | 
					1957beb1b6 | ||
| 
						 | 
					9ca72d6105 | ||
| 
						 | 
					e8e126a6ce | ||
| 
						 | 
					7849782173 | ||
| 
						 | 
					149a0da8c9 | ||
| 
						 | 
					1e7c70a59b | ||
| 
						 | 
					c9d8282e7f | ||
| 
						 | 
					e7084a2c29 | ||
| 
						 | 
					d79d2e0724 | ||
| 
						 | 
					402155c225 | ||
| 
						 | 
					e5ec669419 | ||
| 
						 | 
					8f424e8c9d | ||
| 
						 | 
					fea2b84bb9 | ||
| 
						 | 
					0c918940c4 | ||
| 
						 | 
					a63a6ac0ec | ||
| 
						 | 
					9f58b914d2 | ||
| 
						 | 
					029bac4b03 | ||
| 
						 | 
					a27ad57220 | ||
| 
						 | 
					8d31d924f2 | ||
| 
						 | 
					8c3b514f90 | ||
| 
						 | 
					3df27fe315 | ||
| 
						 | 
					a6087ce354 | ||
| 
						 | 
					aae0382523 | ||
| 
						 | 
					b0c2acb1b5 | ||
| 
						 | 
					a52c96ae2e | ||
| 
						 | 
					bf28821b5b | ||
| 
						 | 
					328bd6ea71 | ||
| 
						 | 
					ec9eb8d054 | ||
| 
						 | 
					f8c8526045 | ||
| 
						 | 
					d8c236bb44 | ||
| 
						 | 
					33ff0910b8 | ||
| 
						 | 
					faa69917d9 | ||
| 
						 | 
					d9f5ca9827 | ||
| 
						 | 
					c817ef7335 | ||
| 
						 | 
					07de489d6b | ||
| 
						 | 
					acafc78456 | ||
| 
						 | 
					325721545e | ||
| 
						 | 
					dbe2bf560c | ||
| 
						 | 
					7872cc2203 | ||
| 
						 | 
					25b4d0e755 | ||
| 
						 | 
					9af1c90664 | ||
| 
						 | 
					6b4d44a3f1 | ||
| 
						 | 
					2faf8d2cdd | ||
| 
						 | 
					676c94ddf2 | ||
| 
						 | 
					6f93b64f84 | ||
| 
						 | 
					54bf426472 | ||
| 
						 | 
					1862ce9104 | ||
| 
						 | 
					3771c13d32 | ||
| 
						 | 
					f1d0230dad | ||
| 
						 | 
					182929cc96 | ||
| 
						 | 
					81585c5a86 | ||
| 
						 | 
					2f52c11589 | ||
| 
						 | 
					0f4d8573cf | ||
| 
						 | 
					660b8b3c6e | ||
| 
						 | 
					71d6a1138c | ||
| 
						 | 
					a6e741d8fe | ||
| 
						 | 
					58bcf65cf6 | ||
| 
						 | 
					27ce55de38 | ||
| 
						 | 
					36ca22db9b | ||
| 
						 | 
					33a8de9b39 | ||
| 
						 | 
					22b76e36db | ||
| 
						 | 
					528ff31590 | ||
| 
						 | 
					4d6966cbd3 | ||
| 
						 | 
					9c9e3be967 | ||
| 
						 | 
					2dd4745ef7 | ||
| 
						 | 
					4e786a32b5 | ||
| 
						 | 
					6ecb2c85e2 | ||
| 
						 | 
					cd234a909b | ||
| 
						 | 
					c249f44ea1 | ||
| 
						 | 
					ed1f51393a | ||
| 
						 | 
					8bd43130ab | ||
| 
						 | 
					6c975a53a1 | ||
| 
						 | 
					8a1001cd40 | ||
| 
						 | 
					beb2b576aa | ||
| 
						 | 
					8f3923046b | ||
| 
						 | 
					88c68d2065 | ||
| 
						 | 
					8bcde72a10 | ||
| 
						 | 
					4653dbcf4b | ||
| 
						 | 
					cde08b32a4 | ||
| 
						 | 
					2eed891f8d | ||
| 
						 | 
					c0b84e84c8 | ||
| 
						 | 
					2c20fd3f9f | ||
| 
						 | 
					f4db56ca29 | ||
| 
						 | 
					5b6d58ac7d | ||
| 
						 | 
					ecb8bd9fbe | ||
| 
						 | 
					2f9385451f | ||
| 
						 | 
					1a1346461b | ||
| 
						 | 
					27dcd29074 | ||
| 
						 | 
					93986f36c3 | ||
| 
						 | 
					a6ab8055b1 | ||
| 
						 | 
					31ea479d7f | ||
| 
						 | 
					89fb2e047b | ||
| 
						 | 
					8f705e6173 | ||
| 
						 | 
					8f735316f5 | ||
| 
						 | 
					0d7a1871a1 | ||
| 
						 | 
					33832b3d25 | ||
| 
						 | 
					4da7f0cc38 | ||
| 
						 | 
					e29f8485ea | ||
| 
						 | 
					2da344a563 | ||
| 
						 | 
					2a0cb8e14f | ||
| 
						 | 
					e1dc3dc357 | ||
| 
						 | 
					8259124f73 | ||
| 
						 | 
					0422956ac6 | ||
| 
						 | 
					ff82c5171b | ||
| 
						 | 
					f9bd6310f1 | ||
| 
						 | 
					471eab126e | ||
| 
						 | 
					6ad011d12d | ||
| 
						 | 
					561c8aff85 | ||
| 
						 | 
					5cf5dd2234 | ||
| 
						 | 
					fb9915d301 | ||
| 
						 | 
					7ab67194d6 | ||
| 
						 | 
					603ac9d8c5 | ||
| 
						 | 
					a3c7811e9d | ||
| 
						 | 
					a3778dfe8b | ||
| 
						 | 
					2dff934612 | ||
| 
						 | 
					89d666948e | ||
| 
						 | 
					c3499142b0 | ||
| 
						 | 
					d8feccc715 | ||
| 
						 | 
					16173355f3 | ||
| 
						 | 
					f46134fda5 | ||
| 
						 | 
					bfecff0412 | ||
| 
						 | 
					168f1891cf | ||
| 
						 | 
					862845b4ac | ||
| 
						 | 
					b7a5dbff49 | ||
| 
						 | 
					5be30d89a7 | ||
| 
						 | 
					d94dc51d88 | ||
| 
						 | 
					22acd88d44 | ||
| 
						 | 
					fd406c106b | ||
| 
						 | 
					16134b5caf | ||
| 
						 | 
					2c485efced | ||
| 
						 | 
					279d08a0e8 | ||
| 
						 | 
					1d4a8998e1 | ||
| 
						 | 
					47a41e7980 | ||
| 
						 | 
					ecfc4a8cb2 | ||
| 
						 | 
					2d66b6266b | ||
| 
						 | 
					b7afb5f9d2 | ||
| 
						 | 
					58c16a59ac | ||
| 
						 | 
					f09789f5ef | ||
| 
						 | 
					b2e12cd3e0 | ||
| 
						 | 
					71b157a8ba | ||
| 
						 | 
					cb8affe9b2 | ||
| 
						 | 
					3f7180c706 | ||
| 
						 | 
					f0bc2dc62f | ||
| 
						 | 
					9c95fd6b69 | ||
| 
						 | 
					c0fda8c3a2 | ||
| 
						 | 
					bac20af381 | ||
| 
						 | 
					626211a320 | ||
| 
						 | 
					783aaa8cb4 | ||
| 
						 | 
					768bd8af19 | ||
| 
						 | 
					3894f52194 | ||
| 
						 | 
					dcfa9edb1c | ||
| 
						 | 
					4ebdb5629c | ||
| 
						 | 
					75b0668fc2 | ||
| 
						 | 
					5fe94ed463 | ||
| 
						 | 
					db21b97603 | ||
| 
						 | 
					8074d0c548 | ||
| 
						 | 
					2d611afbb0 | ||
| 
						 | 
					bc42507724 | ||
| 
						 | 
					ff82ab5718 | ||
| 
						 | 
					62fe374e15 | ||
| 
						 | 
					e519a3bc39 | ||
| 
						 | 
					4f146d12ee | ||
| 
						 | 
					46d6074ad5 | ||
| 
						 | 
					cbb61f1240 | ||
| 
						 | 
					0e9f3c3576 | ||
| 
						 | 
					92f11dc075 | ||
| 
						 | 
					3aa0a0999b | ||
| 
						 | 
					e86fbcfa5b | ||
| 
						 | 
					0b2ee6fdaf | ||
| 
						 | 
					35f3866882 | ||
| 
						 | 
					d86bea80d3 | ||
| 
						 | 
					13b4b5072f | ||
| 
						 | 
					efae43f122 | ||
| 
						 | 
					45e8c8692b | ||
| 
						 | 
					f8fc2a5050 | ||
| 
						 | 
					3c20d57cc4 | ||
| 
						 | 
					2cb3858330 | ||
| 
						 | 
					925d36a561 | ||
| 
						 | 
					0ffaca91cc | ||
| 
						 | 
					d3a0f1d969 | ||
| 
						 | 
					37704db583 | ||
| 
						 | 
					aadd038bbe | ||
| 
						 | 
					6d601b4267 | ||
| 
						 | 
					194798d881 | ||
| 
						 | 
					1a10495645 | ||
| 
						 | 
					0e2316513e | ||
| 
						 | 
					72ce4e2fad | ||
| 
						 | 
					4ad7311e18 | ||
| 
						 | 
					fa2b0e8fad | ||
| 
						 | 
					78eb926693 | ||
| 
						 | 
					3613efbaab | ||
| 
						 | 
					2e732854b3 | ||
| 
						 | 
					f9560d6b1d | ||
| 
						 | 
					51b0826398 | ||
| 
						 | 
					af0adb454d | ||
| 
						 | 
					cdd4c571b0 | ||
| 
						 | 
					9c440764c7 | ||
| 
						 | 
					6e94834033 | ||
| 
						 | 
					1720ed23c9 | ||
| 
						 | 
					137fab1d92 | ||
| 
						 | 
					0fee63fa81 | ||
| 
						 | 
					d5f0d80eed | ||
| 
						 | 
					b7d4d43f56 | ||
| 
						 | 
					c0379a1981 | ||
| 
						 | 
					e5db8e42d1 | ||
| 
						 | 
					27cf9806f0 | ||
| 
						 | 
					5dbb5cd112 | ||
| 
						 | 
					af75569eb8 | ||
| 
						 | 
					aebf2ff270 | ||
| 
						 | 
					3bcdeda3e9 | ||
| 
						 | 
					7433667243 | ||
| 
						 | 
					d7c009167f | ||
| 
						 | 
					79f198d6fe | ||
| 
						 | 
					315a12b9df | ||
| 
						 | 
					e14d145f2c | ||
| 
						 | 
					f839da39f0 | ||
| 
						 | 
					45ca7a63ed | ||
| 
						 | 
					5d187255ae | ||
| 
						 | 
					7adf6aed59 | ||
| 
						 | 
					b22a7a0234 | ||
| 
						 | 
					8805723114 | ||
| 
						 | 
					88db4358f5 | ||
| 
						 | 
					a5d9685caf | ||
| 
						 | 
					94ff8e6beb | ||
| 
						 | 
					f3400d9bc1 | ||
| 
						 | 
					6a6591e41d | ||
| 
						 | 
					a3a53f3b06 | ||
| 
						 | 
					2f39abfdb8 | ||
| 
						 | 
					92239d66fc | ||
| 
						 | 
					178813806f | ||
| 
						 | 
					265dbffc53 | ||
| 
						 | 
					a67a08a4d0 | ||
| 
						 | 
					6d15f55304 | ||
| 
						 | 
					0dbcd1f265 | ||
| 
						 | 
					a0f6380f90 | ||
| 
						 | 
					ab69cf5bf4 | ||
| 
						 | 
					3e55a5526a | ||
| 
						 | 
					b6216bb701 | ||
| 
						 | 
					fa3ea3aaa7 | ||
| 
						 | 
					7b56e3a6c3 | ||
| 
						 | 
					00ad542a11 | ||
| 
						 | 
					c3e00eea2c | ||
| 
						 | 
					5c8dd911e3 | ||
| 
						 | 
					407fd62da5 | ||
| 
						 | 
					c92f7ca0d8 | ||
| 
						 | 
					676901e113 | ||
| 
						 | 
					c099b53a03 | ||
| 
						 | 
					fd065e4e7c | ||
| 
						 | 
					fd97527561 | ||
| 
						 | 
					7b9647a063 | ||
| 
						 | 
					bf943f1347 | ||
| 
						 | 
					dbff610d77 | ||
| 
						 | 
					46aae9486e | ||
| 
						 | 
					70066de390 | ||
| 
						 | 
					106af9d444 | 
							
								
								
									
										1
									
								
								.codecov.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.codecov.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
comment: off
 | 
			
		||||
							
								
								
									
										3
									
								
								.docker-entry.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										3
									
								
								.docker-entry.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
cp -nr /oopt-gnpy/examples /shared
 | 
			
		||||
exec "$@"
 | 
			
		||||
							
								
								
									
										52
									
								
								.docker-travis.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										52
									
								
								.docker-travis.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
set -e
 | 
			
		||||
 | 
			
		||||
IMAGE_NAME=telecominfraproject/oopt-gnpy
 | 
			
		||||
IMAGE_TAG=$(git describe --tags)
 | 
			
		||||
 | 
			
		||||
if [[ "${TRAVIS_BRANCH}" == "experimental/2019-summit" ]]; then
 | 
			
		||||
  IMAGE_NAME=telecominfraproject/oopt-gnpy-experimental
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
ALREADY_FOUND=0
 | 
			
		||||
docker pull ${IMAGE_NAME}:${IMAGE_TAG} && ALREADY_FOUND=1
 | 
			
		||||
 | 
			
		||||
if [[ $ALREADY_FOUND == 0 ]]; then
 | 
			
		||||
  docker build . -t ${IMAGE_NAME}
 | 
			
		||||
  docker tag ${IMAGE_NAME} ${IMAGE_NAME}:${IMAGE_TAG}
 | 
			
		||||
else
 | 
			
		||||
  echo "Image ${IMAGE_NAME}:${IMAGE_TAG} already available, will just update the other tags"
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
docker images
 | 
			
		||||
 | 
			
		||||
do_docker_login() {
 | 
			
		||||
  echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if [[ "${TRAVIS_PULL_REQUEST}" == "false" ]]; then
 | 
			
		||||
  if [[ "${TRAVIS_BRANCH}" == "develop" || "${TRAVIS_BRANCH}" == "docker" ]]; then
 | 
			
		||||
    echo "Publishing latest"
 | 
			
		||||
    docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest
 | 
			
		||||
    do_docker_login
 | 
			
		||||
    if [[ $ALREADY_FOUND == 0 ]]; then
 | 
			
		||||
      docker push ${IMAGE_NAME}:${IMAGE_TAG}
 | 
			
		||||
    fi
 | 
			
		||||
    docker push ${IMAGE_NAME}:latest
 | 
			
		||||
  elif [[ "${TRAVIS_BRANCH}" == "master" ]]; then
 | 
			
		||||
    echo "Publishing stable"
 | 
			
		||||
    docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:stable
 | 
			
		||||
    do_docker_login
 | 
			
		||||
    if [[ $ALREADY_FOUND == 0 ]]; then
 | 
			
		||||
      docker push ${IMAGE_NAME}:${IMAGE_TAG}
 | 
			
		||||
    fi
 | 
			
		||||
    docker push ${IMAGE_NAME}:stable
 | 
			
		||||
  elif [[ "${TRAVIS_BRANCH}" == "experimental/2019-summit" ]]; then
 | 
			
		||||
    echo "Publishing ad-hoc image for the TIP Summit demo"
 | 
			
		||||
    do_docker_login
 | 
			
		||||
    if [[ $ALREADY_FOUND == 0 ]]; then
 | 
			
		||||
      docker push ${IMAGE_NAME}:${IMAGE_TAG}
 | 
			
		||||
    fi
 | 
			
		||||
  fi
 | 
			
		||||
fi
 | 
			
		||||
							
								
								
									
										29
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,10 +1,27 @@
 | 
			
		||||
dist: xenial
 | 
			
		||||
sudo: false
 | 
			
		||||
language: python
 | 
			
		||||
services: docker
 | 
			
		||||
python:
 | 
			
		||||
  - "3.6"
 | 
			
		||||
# command to install dependencies
 | 
			
		||||
install:
 | 
			
		||||
  - python setup.py install
 | 
			
		||||
# command to run tests
 | 
			
		||||
before_script:
 | 
			
		||||
  - "3.7"
 | 
			
		||||
install: skip
 | 
			
		||||
script:
 | 
			
		||||
  - pytest
 | 
			
		||||
  - python setup.py install
 | 
			
		||||
  - pip install pytest-cov rstcheck
 | 
			
		||||
  - pytest --cov-report=xml --cov=gnpy
 | 
			
		||||
  - rstcheck --ignore-roles cite --ignore-directives automodule --recursive --ignore-messages '(Duplicate explicit target name.*)' .
 | 
			
		||||
  - ./examples/transmission_main_example.py
 | 
			
		||||
  - ./examples/path_requests_run.py
 | 
			
		||||
  - ./examples/transmission_main_example.py examples/raman_edfa_example_network.json --sim examples/sim_params.json --show-channels
 | 
			
		||||
  - sphinx-build docs/ x-throwaway-location
 | 
			
		||||
after_success:
 | 
			
		||||
  - bash <(curl -s https://codecov.io/bash)
 | 
			
		||||
jobs:
 | 
			
		||||
  include:
 | 
			
		||||
    - stage: test
 | 
			
		||||
      name: Docker image
 | 
			
		||||
      script:
 | 
			
		||||
        - git fetch --unshallow
 | 
			
		||||
        - ./.docker-travis.sh
 | 
			
		||||
        - docker images
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
---
 | 
			
		||||
- project:
 | 
			
		||||
    check:
 | 
			
		||||
      jobs:
 | 
			
		||||
      - noop
 | 
			
		||||
    gate:
 | 
			
		||||
      jobs:
 | 
			
		||||
      - noop
 | 
			
		||||
@@ -6,16 +6,24 @@ To learn how to contribute, please see CONTRIBUTING.md
 | 
			
		||||
(*in alphabetical order*)
 | 
			
		||||
 | 
			
		||||
- Alessio Ferrari (Politecnico di Torino) <alessio.ferrari@polito.it>
 | 
			
		||||
- Anders Lindgren (Telia Company) <Anders.X.Lindgren@teliacompany.com>
 | 
			
		||||
- Andrea d'Amico (Politecnico di Torino) <andrea.damico@polito.it>
 | 
			
		||||
- Brian Taylor (Facebook) <briantaylor@fb.com>
 | 
			
		||||
- David Boertjes (Ciena) <dboertje@ciena.com>
 | 
			
		||||
- Diego Landa (Facebook) <dlanda@fb.com>
 | 
			
		||||
- Esther Le Rouzic (Orange) <esther.lerouzic@orange.com>
 | 
			
		||||
- Gabriele Galimberti (Cisco) <ggalimbe@cisco.com>
 | 
			
		||||
- Gert Grammel (Juniper Networks) <ggrammel@juniper.net>
 | 
			
		||||
- Gilad Goldfarb (Facebook) <giladg@fb.com>
 | 
			
		||||
- James Powell (Telecom Infra Project) <james.powell@telecominfraproject.com>
 | 
			
		||||
- Jan Kundrát (Telecom Infra Project) <jan.kundrat@telecominfraproject.com>
 | 
			
		||||
- Jeanluc Augé (Orange) <jeanluc.auge@orange.com>
 | 
			
		||||
- Jonas Mårtensson (RISE) <jonas.martensson@ri.se>
 | 
			
		||||
- Mattia Cantono (Politecnico di Torino) <mattia.cantono@polito.it>
 | 
			
		||||
- Miguel Garrich (University Catalunya) <miquel.garrich@upct.es>
 | 
			
		||||
- Raj Nagarajan (Lumentum) <raj.nagarajan@lumentum.com>
 | 
			
		||||
- Roberts Miculens (Lattelecom) <roberts.miculens@lattelecom.lv>
 | 
			
		||||
- Shengxiang Zhu (University of Arizona) <szhu@email.arizona.edu>
 | 
			
		||||
- Stefan Melin (Telia Company) <Stefan.Melin@teliacompany.com>
 | 
			
		||||
- Vittorio Curri (Politecnico di Torino) <vittorio.curri@polito.it>
 | 
			
		||||
- Xufeng Liu (Jabil) <xufeng_liu@jabil.com>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
FROM python:3.7-slim
 | 
			
		||||
COPY . /oopt-gnpy
 | 
			
		||||
WORKDIR /oopt-gnpy
 | 
			
		||||
RUN python setup.py install
 | 
			
		||||
WORKDIR /shared
 | 
			
		||||
ENTRYPOINT ["/oopt-gnpy/.docker-entry.sh"]
 | 
			
		||||
CMD ["python", "examples/path_requests_run.py", "examples/2019-demo-topology.json", "examples/2019-demo-services.json", "examples/2019-demo-equipment.json", "--rest"]
 | 
			
		||||
EXPOSE 5000
 | 
			
		||||
@@ -19,8 +19,8 @@ In order to work the excel file MUST contain at least 2 sheets:
 | 
			
		||||
Nodes sheet
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
Nodes sheet contains seven columns.
 | 
			
		||||
Each line represents a 'node' (ROADM site or an in line amplifier site ILA)::
 | 
			
		||||
Nodes sheet contains nine columns.
 | 
			
		||||
Each line represents a 'node' (ROADM site or an in line amplifier site ILA or a Fused)::
 | 
			
		||||
 | 
			
		||||
  City (Mandatory) ; State ; Country ; Region ; Latitude ; Longitude ; Type
 | 
			
		||||
 | 
			
		||||
@@ -38,6 +38,9 @@ Each line represents a 'node' (ROADM site or an in line amplifier site ILA)::
 | 
			
		||||
 | 
			
		||||
- *Longitude*, *Latitude* are not mandatory. If filled they should contain numbers.
 | 
			
		||||
 | 
			
		||||
- **Booster_restriction** and **Preamp_restriction** are not mandatory.
 | 
			
		||||
  If used, they must contain one or several amplifier type_variety names separated by ' | '. This information is used to restrict types of amplifiers used in a ROADM node during autodesign. If a ROADM booster or preamp is already specified in the Eqpt sheet , the field is ignored. The field is also ignored if the node is not a ROADM node.
 | 
			
		||||
 | 
			
		||||
**There MUST NOT be empty line(s) between two nodes lines**
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -166,6 +169,7 @@ This generates a text file meshTopologyExampleV2_eqt_sheet.txt  whose content ca
 | 
			
		||||
- **amp type** is not mandatory. 
 | 
			
		||||
  If filled it must contain types listed in `eqpt_config.json <examples/eqpt_config.json>`_ in "Edfa" list "type_variety".
 | 
			
		||||
  If not filled it takes "std_medium_gain" as default value.
 | 
			
		||||
  If filled with fused, a fused element with 0.0 dB loss will be placed instead of an amplifier. This might be used to avoid booster amplifier on a ROADM direction.
 | 
			
		||||
 | 
			
		||||
- **amp_gain** is not mandatory. It is the value to be set on the amplifier (in dB).
 | 
			
		||||
  If not filled, it will be determined with design rules in the convert.py file.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										502
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										502
									
								
								README.rst
									
									
									
									
									
								
							@@ -1,13 +1,19 @@
 | 
			
		||||
.. image:: docs/images/GNPy-banner.png
 | 
			
		||||
   :width: 100%
 | 
			
		||||
   :align: left
 | 
			
		||||
   :alt: GNPy with an OLS system
 | 
			
		||||
 | 
			
		||||
====================================================================
 | 
			
		||||
`gnpy`: mesh optical network route planning and optimization library
 | 
			
		||||
====================================================================
 | 
			
		||||
 | 
			
		||||
|docs| |build|
 | 
			
		||||
|docs| |build| |doi|
 | 
			
		||||
 | 
			
		||||
**`gnpy` is an open-source, community-developed library for building route
 | 
			
		||||
planning and optimization tools in real-world mesh optical networks.**
 | 
			
		||||
 | 
			
		||||
`gnpy <http://github.com/telecominfraproject/oopt-gnpy>`__ is:
 | 
			
		||||
--------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
- a sponsored project of the `OOPT/PSE <https://telecominfraproject.com/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_
 | 
			
		||||
- fully community-driven, fully open source library
 | 
			
		||||
@@ -18,41 +24,50 @@ planning and optimization tools in real-world mesh optical networks.**
 | 
			
		||||
 | 
			
		||||
Documentation: https://gnpy.readthedocs.io
 | 
			
		||||
 | 
			
		||||
Get In Touch
 | 
			
		||||
~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
There are `weekly calls <https://telecominfraproject.workplace.com/events/702894886867547/>`__ about our progress.
 | 
			
		||||
Newcomers, users and telecom operators are especially welcome there.
 | 
			
		||||
We encourage all interested people outside the TIP to `join the project <https://telecominfraproject.com/apply-for-membership/>`__.
 | 
			
		||||
 | 
			
		||||
Branches and Tagged Releases
 | 
			
		||||
----------------------------
 | 
			
		||||
 | 
			
		||||
- the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, validated code. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group. For more information about the validation process, see: https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality
 | 
			
		||||
- all releases are `available via GitHub <https://github.com/Telecominfraproject/oopt-gnpy/releases>`_
 | 
			
		||||
- the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, `validated code <https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality>`_. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group.
 | 
			
		||||
- the `develop <https://github.com/Telecominfraproject/oopt-gnpy/tree/develop>`_ branch contains the latest code under active development, which may not be fully validated and tested.
 | 
			
		||||
- the `phase-1 <https://github.com/Telecominfraproject/oopt-gnpy/tree/phase-1>`_ branch contains code for Phase I of the OOPT-PSE efforts and is kept only for reference. This branch is unmaintained.
 | 
			
		||||
 | 
			
		||||
A brief outline of major (tagged) `gnpy` releases:
 | 
			
		||||
 | 
			
		||||
+---------------+-------------+-----------------------------------------------+
 | 
			
		||||
| release date  | version tag | notes                                         |
 | 
			
		||||
+===============+=============+===============================================+
 | 
			
		||||
| Mar 5, 2019   | v1.2        | - plotting fixes                              |
 | 
			
		||||
|               |             | - documentation clean-up                      |
 | 
			
		||||
|               |             | - bug fixes                                   |
 | 
			
		||||
+---------------+-------------+-----------------------------------------------+
 | 
			
		||||
| Jan 30, 2019  | v1.1        | - XLS parser enhancements                     |
 | 
			
		||||
|               |             | - Transponder and Roadm add-drop noise        |
 | 
			
		||||
|               |             |   contribution and system margin included     |
 | 
			
		||||
|               |             | - Automatic transponders’ mode selection      |
 | 
			
		||||
|               |             | - Route selection with disjunction constraints|
 | 
			
		||||
|               |             | - Detailed carrier information inspection on  |
 | 
			
		||||
|               |             |   each element  along propagation             |
 | 
			
		||||
|               |             | - OpenRoadm noise models                      | 
 | 
			
		||||
|               |             | - bug fixes                                   |
 | 
			
		||||
+---------------+-------------+-----------------------------------------------+
 | 
			
		||||
| Oct 16, 2018  | v1.0        | - first "production"-ready release            |
 | 
			
		||||
|               |             | - open network element model (EDFA, GN-model) |
 | 
			
		||||
|               |             | - auto-design functionality                   |
 | 
			
		||||
|               |             | - path request functionality                  |
 | 
			
		||||
+---------------+-------------+-----------------------------------------------+
 | 
			
		||||
 | 
			
		||||
How to Install
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
Using prebuilt Docker images
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
Our `Docker images <https://hub.docker.com/r/telecominfraproject/oopt-gnpy>`_ contain everything needed to run all examples from this guide.
 | 
			
		||||
Docker transparently fetches the image over the network upon first use.
 | 
			
		||||
On Linux and Mac, run:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code-block:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ docker run -it --rm --volume $(pwd):/shared telecominfraproject/oopt-gnpy
 | 
			
		||||
    root@bea050f186f7:/shared/examples#
 | 
			
		||||
 | 
			
		||||
On Windows, launch from Powershell as:
 | 
			
		||||
 | 
			
		||||
.. code-block:: powershell
 | 
			
		||||
 | 
			
		||||
    PS C:\> docker run -it --rm --volume ${PWD}:/shared telecominfraproject/oopt-gnpy
 | 
			
		||||
    root@89784e577d44:/shared/examples#
 | 
			
		||||
 | 
			
		||||
In both cases, a directory named ``examples/`` will appear in your current working directory.
 | 
			
		||||
GNPy automaticallly populates it with example files from the current release.
 | 
			
		||||
Remove that directory if you want to start from scratch.
 | 
			
		||||
 | 
			
		||||
Using Python on your computer
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
   **Note**: `gnpy` supports Python 3 only. Python 2 is not supported.
 | 
			
		||||
   `gnpy` requires Python ≥3.6
 | 
			
		||||
 | 
			
		||||
@@ -62,10 +77,9 @@ How to Install
 | 
			
		||||
It is recommended that you use a "virtual environment" when installing `gnpy`.
 | 
			
		||||
Do not install `gnpy` on your system Python.
 | 
			
		||||
 | 
			
		||||
We recommend the use of the Anaconda Python distribution
 | 
			
		||||
(https://www.anaconda.com/download) which comes with many scientific computing
 | 
			
		||||
We recommend the use of the `Anaconda Python distribution <https://www.anaconda.com/download>`_ which comes with many scientific computing
 | 
			
		||||
dependencies pre-installed. Anaconda creates a base "virtual environment" for
 | 
			
		||||
you automatically. You can also create and manage your conda "virtual
 | 
			
		||||
you automatically. You can also create and manage your ``conda`` "virtual
 | 
			
		||||
environments" yourself (see:
 | 
			
		||||
https://conda.io/docs/user-guide/tasks/manage-environments.html)
 | 
			
		||||
 | 
			
		||||
@@ -111,14 +125,13 @@ of the `gnpy` repo and install it with:
 | 
			
		||||
    $ python setup.py install                                    # install
 | 
			
		||||
 | 
			
		||||
To test that `gnpy` was successfully installed, you can run this command. If it
 | 
			
		||||
executes without a `ModuleNotFoundError`, you have successfully installed
 | 
			
		||||
executes without a ``ModuleNotFoundError``, you have successfully installed
 | 
			
		||||
`gnpy`.
 | 
			
		||||
 | 
			
		||||
.. code-block:: shell
 | 
			
		||||
 | 
			
		||||
    $ python -c 'import gnpy' # attempt to import gnpy
 | 
			
		||||
 | 
			
		||||
    $ cd oopt-gnpy
 | 
			
		||||
    $ pytest                  # run tests
 | 
			
		||||
 | 
			
		||||
Instructions for First Use
 | 
			
		||||
@@ -130,20 +143,18 @@ It ships with a number of example programs. Release versions will ship with
 | 
			
		||||
fully-functional programs.
 | 
			
		||||
 | 
			
		||||
    **Note**: *If you are a network operator or involved in route planning and
 | 
			
		||||
    optimization for your organization, please contact project maintainer James
 | 
			
		||||
    Powell <james.powell@telecominfraproject>. gnpy is looking for users with
 | 
			
		||||
    optimization for your organization, please contact project maintainer Jan
 | 
			
		||||
    Kundrát <jan.kundrat@telecominfraproject.com>. gnpy is looking for users with
 | 
			
		||||
    specific, delineated use cases to drive requirements for future
 | 
			
		||||
    development.*
 | 
			
		||||
 | 
			
		||||
**To get started, run the main transmission example:**
 | 
			
		||||
This example demonstrates how GNPy can be used to check the expected SNR at the end of the line by varying the channel input power:
 | 
			
		||||
 | 
			
		||||
    **Note**: *Examples should be run from the examples/ folder.*
 | 
			
		||||
 | 
			
		||||
.. code-block:: shell
 | 
			
		||||
    $ pwd
 | 
			
		||||
    /path/to/oopt-gnpy
 | 
			
		||||
    $ cd examples
 | 
			
		||||
    $ python transmission_main_example.py
 | 
			
		||||
.. image:: https://telecominfraproject.github.io/oopt-gnpy/docs/images/transmission_main_example.svg
 | 
			
		||||
   :width: 100%
 | 
			
		||||
   :align: left
 | 
			
		||||
   :alt: Running a simple simulation example
 | 
			
		||||
   :target: https://asciinema.org/a/252295
 | 
			
		||||
 | 
			
		||||
By default, this script operates on a single span network defined in
 | 
			
		||||
`examples/edfa_example_network.json <examples/edfa_example_network.json>`_
 | 
			
		||||
@@ -152,10 +163,9 @@ You can specify a different network at the command line as follows. For
 | 
			
		||||
example, to use the CORONET Global network defined in
 | 
			
		||||
`examples/CORONET_Global_Topology.json <examples/CORONET_Global_Topology.json>`_:
 | 
			
		||||
 | 
			
		||||
.. code-block:: shell
 | 
			
		||||
.. code-block:: shell-session
 | 
			
		||||
 | 
			
		||||
    $ cd examples
 | 
			
		||||
    $ python transmission_main_example.py CORONET_Global_Topology.json
 | 
			
		||||
    $ ./examples/transmission_main_example.py examples/CORONET_Global_Topology.json
 | 
			
		||||
 | 
			
		||||
It is also possible to use an Excel file input (for example
 | 
			
		||||
`examples/CORONET_Global_Topology.xls <examples/CORONET_Global_Topology.xls>`_).
 | 
			
		||||
@@ -193,59 +203,59 @@ information to transmit.)
 | 
			
		||||
The EDFA equipment library is a list of supported amplifiers. New amplifiers
 | 
			
		||||
can be added and existing ones removed. Three different noise models are available:
 | 
			
		||||
 | 
			
		||||
1. `'type_def': 'variable_gain'` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: `nf_min`, `nf_max`, and `gain_flatmax`. It is not a simple interpolation but a 2-stage NF calculation.
 | 
			
		||||
2. `'type_def': 'fixed_gain'` is a fixed gain model.  `NF == Cte == nf0` if `gain_min < gain < gain_flatmax`
 | 
			
		||||
3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/std_medium_gain_advanced_config.json <examples/std_medium_gain_advanced_config.json>`_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled.
 | 
			
		||||
1. ``'type_def': 'variable_gain'`` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: ``nf_min``, ``nf_max``, and ``gain_flatmax``. It is not a simple interpolation but a 2-stage NF calculation.
 | 
			
		||||
2. ``'type_def': 'fixed_gain'`` is a fixed gain model.  `NF == Cte == nf0` if `gain_min < gain < gain_flatmax`
 | 
			
		||||
3. ``'type_def': None`` is an advanced model. A detailed JSON configuration file is required (by default `examples/std_medium_gain_advanced_config.json <examples/std_medium_gain_advanced_config.json>`_). It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled.
 | 
			
		||||
 | 
			
		||||
For all amplifier models:
 | 
			
		||||
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| field                |   type    | description                             |
 | 
			
		||||
+======================+===========+=========================================+
 | 
			
		||||
| `type_variety`       | (string)  | a unique name to ID the amplifier in the|
 | 
			
		||||
|                      |           | JSON/Excel template topology input file |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `out_voa_auto`       | (boolean) | auto_design feature to optimize the     |
 | 
			
		||||
|                      |           | amplifier output VOA. If true, output   |
 | 
			
		||||
|                      |           | VOA is present and will be used to push |
 | 
			
		||||
|                      |           | amplifier gain to its maximum, within   |
 | 
			
		||||
|                      |           | EOL power margins.                      |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `allowed_for_design` | (boolean) | If false, the amplifier will not be     |
 | 
			
		||||
|                      |           | picked by auto-design but it can still  |
 | 
			
		||||
|                      |           | be used as a manual input (from JSON or |
 | 
			
		||||
|                      |           | Excel template topology files.)         |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
+------------------------+-----------+-----------------------------------------+
 | 
			
		||||
| field                  |   type    | description                             |
 | 
			
		||||
+========================+===========+=========================================+
 | 
			
		||||
| ``type_variety``       | (string)  | a unique name to ID the amplifier in the|
 | 
			
		||||
|                        |           | JSON/Excel template topology input file |
 | 
			
		||||
+------------------------+-----------+-----------------------------------------+
 | 
			
		||||
| ``out_voa_auto``       | (boolean) | auto_design feature to optimize the     |
 | 
			
		||||
|                        |           | amplifier output VOA. If true, output   |
 | 
			
		||||
|                        |           | VOA is present and will be used to push |
 | 
			
		||||
|                        |           | amplifier gain to its maximum, within   |
 | 
			
		||||
|                        |           | EOL power margins.                      |
 | 
			
		||||
+------------------------+-----------+-----------------------------------------+
 | 
			
		||||
| ``allowed_for_design`` | (boolean) | If false, the amplifier will not be     |
 | 
			
		||||
|                        |           | picked by auto-design but it can still  |
 | 
			
		||||
|                        |           | be used as a manual input (from JSON or |
 | 
			
		||||
|                        |           | Excel template topology files.)         |
 | 
			
		||||
+------------------------+-----------+-----------------------------------------+
 | 
			
		||||
 | 
			
		||||
The fiber library currently describes SSMF 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    |
 | 
			
		||||
| ``type_variety``     | (string)  | a unique name to ID the fiber in the    |
 | 
			
		||||
|                      |           | JSON or Excel template topology input   |
 | 
			
		||||
|                      |           | file                                    |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `dispersion`         | (number)  | (s.m-1.m-1)                             |
 | 
			
		||||
| ``dispersion``       | (number)  | (s.m-1.m-1)                             |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `gamma`              | (number)  | 2pi.n2/(lambda*Aeff) (w-2.m-1)          |
 | 
			
		||||
| ``gamma``            | (number)  | 2pi.n2/(lambda*Aeff) (w-2.m-1)          |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
 | 
			
		||||
The transceiver equipment library is a list of supported transceivers. New
 | 
			
		||||
transceivers can be added and existing ones removed at will by the user. It is
 | 
			
		||||
used to determine the service list path feasibility when running the
 | 
			
		||||
path_request_run.py routine.
 | 
			
		||||
`path_request_run.py routine <examples/path_request_run.py>`_.
 | 
			
		||||
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| field                | type      | description                             |
 | 
			
		||||
+======================+===========+=========================================+
 | 
			
		||||
|  `type_variety`      | (string)  | a unique name to ID the transceiver in  |
 | 
			
		||||
| ``type_variety``     | (string)  | A unique name to ID the transceiver in  |
 | 
			
		||||
|                      |           | the JSON or Excel template topology     |
 | 
			
		||||
|                      |           | input file                              |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
|  `frequency`         | (number)  | Min/max as below.                       |
 | 
			
		||||
| ``frequency``        | (number)  | Min/max as below.                       |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
|  `mode`              | (number)  | a list of modes supported by the        |
 | 
			
		||||
| ``mode``             | (number)  | A list of modes supported by the        |
 | 
			
		||||
|                      |           | transponder. New modes can be added at  |
 | 
			
		||||
|                      |           | will by the user. The modes are specific|
 | 
			
		||||
|                      |           | to each transponder type_variety.       |
 | 
			
		||||
@@ -257,142 +267,142 @@ The modes are defined as follows:
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| field                | type      | description                             |
 | 
			
		||||
+======================+===========+=========================================+
 | 
			
		||||
| `format`             | (string)  | a unique name to ID the mode.           |
 | 
			
		||||
| ``format``           | (string)  | a unique name to ID the mode            |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `baud_rate`          | (number)  | in Hz                                   |
 | 
			
		||||
| ``baud_rate``        | (number)  | in Hz                                   |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `OSNR`               | (number)  | min required OSNR in 0.1nm (dB)         |
 | 
			
		||||
| ``OSNR``             | (number)  | min required OSNR in 0.1nm (dB)         |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `bit_rate`           | (number)  | in bit/s                                |
 | 
			
		||||
| ``bit_rate``         | (number)  | in bit/s                                |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `roll_off`           | (number)  | Not used.                               |
 | 
			
		||||
| ``roll_off``         | (number)  | Not used.                               |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `tx_osnr`            | (number)  | In dB. OSNR out from transponder.       |
 | 
			
		||||
| ``tx_osnr``          | (number)  | In dB. OSNR out from transponder.       |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
| `cost`               | (number)  | Arbitrary unit                          |
 | 
			
		||||
| ``cost``             | (number)  | Arbitrary unit                          |
 | 
			
		||||
+----------------------+-----------+-----------------------------------------+
 | 
			
		||||
 | 
			
		||||
Simulation parameters are defined as follows.
 | 
			
		||||
 | 
			
		||||
Auto-design automatically creates EDFA amplifier network elements when they are
 | 
			
		||||
missing, after a fiber, or between a ROADM and a fiber. This auto-design
 | 
			
		||||
functionality can be manually and locally deactivated by introducing a `Fused`
 | 
			
		||||
network element after a `Fiber` or a `Roadm` that doesn't need amplification.
 | 
			
		||||
functionality can be manually and locally deactivated by introducing a ``Fused``
 | 
			
		||||
network element after a ``Fiber`` or a ``Roadm`` that doesn't need amplification.
 | 
			
		||||
The amplifier is chosen in the EDFA list of the equipment library based on
 | 
			
		||||
gain, power, and NF criteria. Only the EDFA that are marked
 | 
			
		||||
`'allowed_for_design': true` are considered.
 | 
			
		||||
``'allowed_for_design': true`` are considered.
 | 
			
		||||
 | 
			
		||||
For amplifiers defined in the topology JSON input but whose gain = 0
 | 
			
		||||
(placeholder), auto-design will set its gain automatically: see `power_mode` in
 | 
			
		||||
the `Spans` library to find out how the gain is calculated.
 | 
			
		||||
For amplifiers defined in the topology JSON input but whose ``gain = 0``
 | 
			
		||||
(placeholder), auto-design will set its gain automatically: see ``power_mode`` in
 | 
			
		||||
the ``Spans`` library to find out how the gain is calculated.
 | 
			
		||||
 | 
			
		||||
Span configuration is performed as follows. It is not a list (which may change
 | 
			
		||||
in later releases) and the user can only modify the value of existing
 | 
			
		||||
parameters:
 | 
			
		||||
 | 
			
		||||
+------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| field                  | type      | description                                 |
 | 
			
		||||
+========================+===========+=============================================+
 | 
			
		||||
| `power_mode`           | (boolean) | If false, gain mode. Auto-design sets       |
 | 
			
		||||
|                        |           | amplifier gain = 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_length`           | (number)  | Split fiber lengths > max_length.           |
 | 
			
		||||
|                        |           | Interest to support high level              |
 | 
			
		||||
|                        |           | topologies that do not specify in line      |
 | 
			
		||||
|                        |           | amplification sites. For example the        |
 | 
			
		||||
|                        |           | CORONET_Global_Topology.xls defines         |
 | 
			
		||||
|                        |           | links > 1000km between 2 sites: it          |
 | 
			
		||||
|                        |           | couldn't be simulated if these links        |
 | 
			
		||||
|                        |           | were not splitted in shorter span           |
 | 
			
		||||
|                        |           | lengths.                                    |
 | 
			
		||||
+------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| `length_unit`          | "m"/"km"  | Unit for max_length.                        |
 | 
			
		||||
+------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| `max_loss`             | (number)  | Not used in the current code                |
 | 
			
		||||
|                        |           | implementation.                             |
 | 
			
		||||
+------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| `padding`              | (number)  | In dB. Min span loss before putting an      |
 | 
			
		||||
|                        |           | attenuator before fiber. Attenuator         |
 | 
			
		||||
|                        |           | value                                       |
 | 
			
		||||
|                        |           | Fiber.att_in = max(0, padding - span_loss). |
 | 
			
		||||
|                        |           | Padding can be set manually to reach a      |
 | 
			
		||||
|                        |           | higher padding value for a given fiber      |
 | 
			
		||||
|                        |           | by filling in the Fiber/params/att_in       |
 | 
			
		||||
|                        |           | field in the topology json input [1]        |
 | 
			
		||||
|                        |           | but if span_loss = length * loss_coef       |
 | 
			
		||||
|                        |           | + att_in + con_in + con_out < padding,      |
 | 
			
		||||
|                        |           | the specified att_in value will be          |
 | 
			
		||||
|                        |           | completed to have span_loss = padding.      |
 | 
			
		||||
|                        |           | Therefore it is not possible to set         |
 | 
			
		||||
|                        |           | span_loss < padding.                        |
 | 
			
		||||
+------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| `EOL`                  | (number)  | All fiber span loss ageing. The value       |
 | 
			
		||||
|                        |           | is added to the con_out (fiber output       |
 | 
			
		||||
|                        |           | connector). So the design and the path      |
 | 
			
		||||
|                        |           | feasibility are performed with              |
 | 
			
		||||
|                        |           | span_loss + EOL. EOL cannot be set          |
 | 
			
		||||
|                        |           | manually for a given fiber span             |
 | 
			
		||||
|                        |           | (workaround is to specify higher con_out    |
 | 
			
		||||
|                        |           | loss for this fiber).                       |
 | 
			
		||||
+------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| `con_in`, `con_out`    | (number)  | Default values if Fiber/params/con_in/out   |
 | 
			
		||||
|                        |           | is None in the topology input               |
 | 
			
		||||
|                        |           | description. This default value is          |
 | 
			
		||||
|                        |           | ignored if a Fiber/params/con_in/out        |
 | 
			
		||||
|                        |           | value is input in the topology for a        |
 | 
			
		||||
|                        |           | given Fiber.                                |
 | 
			
		||||
+------------------------+-----------+---------------------------------------------+
 | 
			
		||||
 | 
			
		||||
**[1]**
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| field                               | type      | description                                 |
 | 
			
		||||
+=====================================+===========+=============================================+
 | 
			
		||||
| ``power_mode``                      | (boolean) | If false, gain mode. Auto-design sets       |
 | 
			
		||||
|                                     |           | amplifier gain = preceding span loss,       |
 | 
			
		||||
|                                     |           | unless the amplifier exists and its         |
 | 
			
		||||
|                                     |           | gain > 0 in the topology input JSON.        |
 | 
			
		||||
|                                     |           | If true, power mode (recommended for        |
 | 
			
		||||
|                                     |           | auto-design and power sweep.)               |
 | 
			
		||||
|                                     |           | Auto-design sets amplifier power            |
 | 
			
		||||
|                                     |           | according to delta_power_range. If the      |
 | 
			
		||||
|                                     |           | amplifier exists with gain > 0 in the       |
 | 
			
		||||
|                                     |           | topology JSON input, then its gain is       |
 | 
			
		||||
|                                     |           | translated into a power target/channel.     |
 | 
			
		||||
|                                     |           | Moreover, when performing a power sweep     |
 | 
			
		||||
|                                     |           | (see ``power_range_db`` in the SI           |
 | 
			
		||||
|                                     |           | configuration library) the power sweep      |
 | 
			
		||||
|                                     |           | is performed w/r/t this power target,       |
 | 
			
		||||
|                                     |           | regardless of preceding amplifiers          |
 | 
			
		||||
|                                     |           | power saturation/limitations.               |
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``delta_power_range_db``            | (number)  | Auto-design only, power-mode                |
 | 
			
		||||
|                                     |           | only. Specifies the [min, max, step]        |
 | 
			
		||||
|                                     |           | power excursion/span. It is a relative      |
 | 
			
		||||
|                                     |           | power excursion w/r/t the                   |
 | 
			
		||||
|                                     |           | power_dbm + power_range_db                  |
 | 
			
		||||
|                                     |           | (power sweep if applicable) defined in      |
 | 
			
		||||
|                                     |           | the SI configuration library. This          |
 | 
			
		||||
|                                     |           | relative power excursion is = 1/3 of        |
 | 
			
		||||
|                                     |           | the span loss difference with the           |
 | 
			
		||||
|                                     |           | reference 20 dB span. The 1/3 slope is      |
 | 
			
		||||
|                                     |           | derived from the GN model equations.        |
 | 
			
		||||
|                                     |           | For example, a 23 dB span loss will be      |
 | 
			
		||||
|                                     |           | set to 1 dB more power than a 20 dB         |
 | 
			
		||||
|                                     |           | span loss. The 20 dB reference spans        |
 | 
			
		||||
|                                     |           | will *always* be set to                     |
 | 
			
		||||
|                                     |           | power = power_dbm + power_range_db.         |
 | 
			
		||||
|                                     |           | To configure the same power in all          |
 | 
			
		||||
|                                     |           | spans, use `[0, 0, 0]`. All spans will      |
 | 
			
		||||
|                                     |           | be set to                                   |
 | 
			
		||||
|                                     |           | power = power_dbm + power_range_db.         |
 | 
			
		||||
|                                     |           | To configure the same power in all spans    |
 | 
			
		||||
|                                     |           | and 3 dB more power just for the longest    |
 | 
			
		||||
|                                     |           | spans: `[0, 3, 3]`. The longest spans are   |
 | 
			
		||||
|                                     |           | set to                                      |
 | 
			
		||||
|                                     |           | power = power_dbm + power_range_db + 3.     |
 | 
			
		||||
|                                     |           | To configure a 4 dB power range across      |
 | 
			
		||||
|                                     |           | all spans in 0.5 dB steps: `[-2, 2, 0.5]`.  |
 | 
			
		||||
|                                     |           | A 17 dB span is set to                      |
 | 
			
		||||
|                                     |           | power = power_dbm + power_range_db - 1,     |
 | 
			
		||||
|                                     |           | a 20 dB span to                             |
 | 
			
		||||
|                                     |           | power = power_dbm + power_range_db and      |
 | 
			
		||||
|                                     |           | a 23 dB span to                             |
 | 
			
		||||
|                                     |           | power = power_dbm + power_range_db + 1      |
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``max_fiber_lineic_loss_for_raman`` | (number)  | Maximum linear fiber loss for Raman         |
 | 
			
		||||
|                                     |           | amplification use.                          |
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``max_length``                      | (number)  | Split fiber lengths > max_length.           |
 | 
			
		||||
|                                     |           | Interest to support high level              |
 | 
			
		||||
|                                     |           | topologies that do not specify in line      |
 | 
			
		||||
|                                     |           | amplification sites. For example the        |
 | 
			
		||||
|                                     |           | CORONET_Global_Topology.xls defines         |
 | 
			
		||||
|                                     |           | links > 1000km between 2 sites: it          |
 | 
			
		||||
|                                     |           | couldn't be simulated if these links        |
 | 
			
		||||
|                                     |           | were not split in shorter span lengths.     |
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``length_unit``                     | "m"/"km"  | Unit for ``max_length``.                    |
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``max_loss``                        | (number)  | Not used in the current code                |
 | 
			
		||||
|                                     |           | implementation.                             |
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``padding``                         | (number)  | In dB. Min span loss before putting an      |
 | 
			
		||||
|                                     |           | attenuator before fiber. Attenuator         |
 | 
			
		||||
|                                     |           | value                                       |
 | 
			
		||||
|                                     |           | Fiber.att_in = max(0, padding - span_loss). |
 | 
			
		||||
|                                     |           | Padding can be set manually to reach a      |
 | 
			
		||||
|                                     |           | higher padding value for a given fiber      |
 | 
			
		||||
|                                     |           | by filling in the Fiber/params/att_in       |
 | 
			
		||||
|                                     |           | field in the topology json input [1]        |
 | 
			
		||||
|                                     |           | but if span_loss = length * loss_coef       |
 | 
			
		||||
|                                     |           | + att_in + con_in + con_out < padding,      |
 | 
			
		||||
|                                     |           | the specified att_in value will be          |
 | 
			
		||||
|                                     |           | completed to have span_loss = padding.      |
 | 
			
		||||
|                                     |           | Therefore it is not possible to set         |
 | 
			
		||||
|                                     |           | span_loss < padding.                        |
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``EOL``                             | (number)  | All fiber span loss ageing. The value       |
 | 
			
		||||
|                                     |           | is added to the con_out (fiber output       |
 | 
			
		||||
|                                     |           | connector). So the design and the path      |
 | 
			
		||||
|                                     |           | feasibility are performed with              |
 | 
			
		||||
|                                     |           | span_loss + EOL. EOL cannot be set          |
 | 
			
		||||
|                                     |           | manually for a given fiber span             |
 | 
			
		||||
|                                     |           | (workaround is to specify higher            |
 | 
			
		||||
|                                     |           | ``con_out`` loss for this fiber).           |
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``con_in``,                         | (number)  | Default values if Fiber/params/con_in/out   |
 | 
			
		||||
| ``con_out``                         |           | is None in the topology input               |
 | 
			
		||||
|                                     |           | description. This default value is          |
 | 
			
		||||
|                                     |           | ignored if a Fiber/params/con_in/out        |
 | 
			
		||||
|                                     |           | value is input in the topology for a        |
 | 
			
		||||
|                                     |           | given Fiber.                                |
 | 
			
		||||
+-------------------------------------+-----------+---------------------------------------------+
 | 
			
		||||
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
@@ -415,33 +425,36 @@ parameters:
 | 
			
		||||
ROADMs can be configured as follows. The user can only modify the value of
 | 
			
		||||
existing parameters:
 | 
			
		||||
 | 
			
		||||
+-------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| field                   |   type    | description                                 |
 | 
			
		||||
+=========================+===========+=============================================+
 | 
			
		||||
|`gain_mode_default_loss` | (number)  | Default value if Roadm/params/loss is       |
 | 
			
		||||
|                         |           | None in the topology input description.     |
 | 
			
		||||
|                         |           | This default value is ignored if a          |
 | 
			
		||||
|                         |           | params/loss value is input in the           |
 | 
			
		||||
|                         |           | topology for a given ROADM.                 |
 | 
			
		||||
+-------------------------+-----------+---------------------------------------------+
 | 
			
		||||
|`power_mode_pref`        | (number)  | Power mode only. Auto-design sets the       |
 | 
			
		||||
|                         |           | power of ROADM ingress amplifiers to        |
 | 
			
		||||
|                         |           | power_dbm + power_range_db,                 |
 | 
			
		||||
|                         |           | regardless of existing gain settings        |
 | 
			
		||||
|                         |           | from the topology JSON input.               |
 | 
			
		||||
|                         |           | Auto-design sets the Roadm loss so that     |
 | 
			
		||||
|                         |           | its egress channel power = power_mode_pref, |
 | 
			
		||||
|                         |           | regardless of existing loss settings        |
 | 
			
		||||
|                         |           | from the topology JSON input. It means      |
 | 
			
		||||
|                         |           | that the output power from a ROADM (and      |
 | 
			
		||||
|                         |           | therefore its OSNR contribution) is Cte     |
 | 
			
		||||
|                         |           | and not depending from power_dbm and        |
 | 
			
		||||
|                         |           | power_range_db sweep settings. This         |
 | 
			
		||||
|                         |           | choice is meant to reflect some typical     |
 | 
			
		||||
|                         |           | control loop algorithms.                    |
 | 
			
		||||
+-------------------------+-----------+---------------------------------------------+
 | 
			
		||||
+--------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| field                    |   type    | description                                 |
 | 
			
		||||
+==========================+===========+=============================================+
 | 
			
		||||
| ``target_pch_out_db``    | (number)  | Auto-design sets the ROADM egress channel   |
 | 
			
		||||
|                          |           | power. This reflects typical control loop   |
 | 
			
		||||
|                          |           | algorithms that adjust ROADM losses to      |
 | 
			
		||||
|                          |           | equalize channels (eg coming from different |
 | 
			
		||||
|                          |           | ingress direction or add ports)             |
 | 
			
		||||
|                          |           | This is the default value                   |
 | 
			
		||||
|                          |           | Roadm/params/target_pch_out_db if no value  |
 | 
			
		||||
|                          |           | is given in the ``Roadm`` element in the    |
 | 
			
		||||
|                          |           | topology input description.                 |
 | 
			
		||||
|                          |           | This default value is ignored if a          |
 | 
			
		||||
|                          |           | params/target_pch_out_db value is input in  |
 | 
			
		||||
|                          |           | the topology for a given ROADM.             |
 | 
			
		||||
+--------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``add_drop_osnr``        | (number)  | OSNR contribution from the add/drop ports   |
 | 
			
		||||
+--------------------------+-----------+---------------------------------------------+
 | 
			
		||||
| ``restrictions``         | (dict of  | If non-empty, keys ``preamp_variety_list``  |
 | 
			
		||||
|                          |  strings) | and ``booster_variety_list`` represent      |
 | 
			
		||||
|                          |           | list of ``type_variety`` amplifiers which   |
 | 
			
		||||
|                          |           | are allowed for auto-design within ROADM's  |
 | 
			
		||||
|                          |           | line degrees.                               |
 | 
			
		||||
|                          |           |                                             |
 | 
			
		||||
|                          |           | If no booster should be placed on a degree, |
 | 
			
		||||
|                          |           | insert a ``Fused`` node on the degree       |
 | 
			
		||||
|                          |           | output.                                     |
 | 
			
		||||
+--------------------------+-----------+---------------------------------------------+
 | 
			
		||||
 | 
			
		||||
The `SpectralInformation` object can be configured as follows. The user can
 | 
			
		||||
The ``SpectralInformation`` object can be configured as follows. The user can
 | 
			
		||||
only modify the value of existing parameters. It defines a spectrum of N
 | 
			
		||||
identical carriers. While the code libraries allow for different carriers and
 | 
			
		||||
power levels, the current user parametrization only allows one carrier type and
 | 
			
		||||
@@ -450,21 +463,18 @@ one power/channel definition.
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| field                |   type    | description                               |
 | 
			
		||||
+======================+===========+===========================================+
 | 
			
		||||
| `f_min/max`          | (number)  | In Hz. Carrier min max excursion          |
 | 
			
		||||
| ``f_min``,           | (number)  | In Hz. Carrier min max excursion.         |
 | 
			
		||||
| ``f_max``            |           |                                           |
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| `baud_rate`          | (number)  | In Hz. Simulated baud rate.               |
 | 
			
		||||
| ``baud_rate``        | (number)  | In Hz. Simulated baud rate.               |
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| `spacing`            | (number)  | In Hz. Carrier spacing.                   |
 | 
			
		||||
| ``spacing``          | (number)  | In Hz. Carrier spacing.                   |
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| `roll_off`           | (number)  | Not used.                                 |
 | 
			
		||||
| ``roll_off``         | (number)  | Not used.                                 |
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| `OSNR`               | (number)  | Not used.                                 |
 | 
			
		||||
| ``tx_osnr``          | (number)  | In dB. OSNR out from transponder.         |
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| `bit_rate`           | (number)  | Not used.                                 |
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| `tx_osnr`            | (number)  | In dB. OSNR out from transponder.         |
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| `power_dbm`          | (number)  | Reference channel power. In gain mode     |
 | 
			
		||||
| ``power_dbm``        | (number)  | Reference channel power. In gain mode     |
 | 
			
		||||
|                      |           | (see spans/power_mode = false), all gain  |
 | 
			
		||||
|                      |           | settings are offset w/r/t this reference  |
 | 
			
		||||
|                      |           | power. In power mode, it is the           |
 | 
			
		||||
@@ -477,17 +487,30 @@ one power/channel definition.
 | 
			
		||||
|                      |           | power sweep is defined (see after) the    |
 | 
			
		||||
|                      |           | design is not repeated.                   |
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| `power_range_db`     | (number)  | Power sweep excursion around power_dbm.   |
 | 
			
		||||
| ``power_range_db``   | (number)  | Power sweep excursion around power_dbm.   |
 | 
			
		||||
|                      |           | It is not the min and max channel power   |
 | 
			
		||||
|                      |           | values! The reference power becomes:      |
 | 
			
		||||
|                      |           | power_range_db + power_dbm.               |
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
| ``sys_margins``      | (number)  | In dB. Added margin on min required       |
 | 
			
		||||
|                      |           | transceiver OSNR.                         |         
 | 
			
		||||
+----------------------+-----------+-------------------------------------------+
 | 
			
		||||
 | 
			
		||||
The `transmission_main_example.py <examples/transmission_main_example.py>`_
 | 
			
		||||
script propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing and 0
 | 
			
		||||
dBm/channel. These are not yet parametrized but can be modified directly in the
 | 
			
		||||
script (via the SpectralInformation structure) to accommodate any baud rate,
 | 
			
		||||
spacing, power or channel count demand.
 | 
			
		||||
The `transmission_main_example.py <examples/transmission_main_example.py>`_ script propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing and 0 dBm/channel. 
 | 
			
		||||
Launch power can be overridden by using the ``--power`` argument.
 | 
			
		||||
Spectrum information is not yet parametrized but can be modified directly in the ``eqpt_config.json`` (via the ``SpectralInformation`` -SI- structure) to accommodate any baud rate or spacing.
 | 
			
		||||
The number of channel is computed based on ``spacing`` and ``f_min``, ``f_max`` values.
 | 
			
		||||
 | 
			
		||||
An experimental support for Raman amplification is available:
 | 
			
		||||
 | 
			
		||||
.. code-block:: shell
 | 
			
		||||
 | 
			
		||||
     $ ./examples/transmission_main_example.py \
 | 
			
		||||
       examples/raman_edfa_example_network.json \
 | 
			
		||||
       --sim examples/sim_params.json --show-channels
 | 
			
		||||
 | 
			
		||||
Configuration of Raman pumps (their frequencies, power and pumping direction) is done via the `RamanFiber element in the network topology <examples/raman_edfa_example_network.json>`_.
 | 
			
		||||
General numeric parameters for simulaiton control are provided in the `examples/sim_params.json <examples/sim_params.json>`_.
 | 
			
		||||
 | 
			
		||||
Use `examples/path_requests_run.py <examples/path_requests_run.py>`_ to run multiple optimizations as follows:
 | 
			
		||||
 | 
			
		||||
@@ -496,7 +519,7 @@ Use `examples/path_requests_run.py <examples/path_requests_run.py>`_ to run mult
 | 
			
		||||
     $ python path_requests_run.py -h
 | 
			
		||||
     Usage: path_requests_run.py [-h] [-v] [-o OUTPUT] [network_filename] [service_filename] [eqpt_filename]
 | 
			
		||||
 | 
			
		||||
The `network_filename` and `service_filename` can be an XLS or JSON file. The `eqpt_filename` must be a JSON file.
 | 
			
		||||
The ``network_filename`` and ``service_filename`` can be an XLS or JSON file. The ``eqpt_filename`` must be a JSON file.
 | 
			
		||||
 | 
			
		||||
To see an example of it, run:
 | 
			
		||||
 | 
			
		||||
@@ -507,10 +530,10 @@ To see an example of it, run:
 | 
			
		||||
 | 
			
		||||
This program requires a list of connections to be estimated and the equipment
 | 
			
		||||
library. The program computes performances for the list of services (accepts
 | 
			
		||||
json or excel format) using the same spectrum propagation modules as
 | 
			
		||||
transmission_main_example.py. Explanation on the Excel template is provided in
 | 
			
		||||
JSON or Excel format) using the same spectrum propagation modules as
 | 
			
		||||
``transmission_main_example.py``. Explanation on the Excel template is provided in
 | 
			
		||||
the `Excel_userguide.rst <Excel_userguide.rst#service-sheet>`_. Template for
 | 
			
		||||
the json format can be found here: `service-template.json
 | 
			
		||||
the JSON format can be found here: `service-template.json
 | 
			
		||||
<service-template.json>`_.
 | 
			
		||||
 | 
			
		||||
Contributing
 | 
			
		||||
@@ -519,8 +542,8 @@ Contributing
 | 
			
		||||
``gnpy`` is looking for additional contributors, especially those with experience
 | 
			
		||||
planning and maintaining large-scale, real-world mesh optical networks.
 | 
			
		||||
 | 
			
		||||
To get involved, please contact James Powell
 | 
			
		||||
<james.powell@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>.
 | 
			
		||||
To get involved, please contact Jan Kundrát
 | 
			
		||||
<jan.kundrat@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>.
 | 
			
		||||
 | 
			
		||||
``gnpy`` contributions are currently limited to members of `TIP
 | 
			
		||||
<http://telecominfraproject.com>`_. Membership is free and open to all.
 | 
			
		||||
@@ -578,6 +601,11 @@ implementations.
 | 
			
		||||
  :alt: Build Status
 | 
			
		||||
  :scale: 100%
 | 
			
		||||
 | 
			
		||||
.. |doi| image:: https://zenodo.org/badge/96894149.svg
 | 
			
		||||
  :target: https://zenodo.org/badge/latestdoi/96894149
 | 
			
		||||
  :alt: DOI
 | 
			
		||||
  :scale: 100%
 | 
			
		||||
 | 
			
		||||
TIP OOPT/PSE & PSE WG Charter
 | 
			
		||||
-----------------------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -874,7 +874,7 @@ month={Sept},}
 | 
			
		||||
  number = {7},
 | 
			
		||||
  journal = {Optics Express},
 | 
			
		||||
  urlyear = {2017-11-14},
 | 
			
		||||
  year = {2012-03-26},
 | 
			
		||||
  date = {2012-03-26},
 | 
			
		||||
  year = {2012},
 | 
			
		||||
  pages = {7777},
 | 
			
		||||
  author = {Bononi, A. and Serena, P. and Rossi, N. and Grellier, E. and Vacondio, F.}
 | 
			
		||||
@@ -1114,7 +1114,7 @@ month={Sept},}
 | 
			
		||||
  number = {26},
 | 
			
		||||
  journal = {Optics Express},
 | 
			
		||||
  urlyear = {2017-11-16},
 | 
			
		||||
  year = {2013-12-30},
 | 
			
		||||
  date = {2013-12-30},
 | 
			
		||||
  year = {2013},
 | 
			
		||||
  pages = {32254},
 | 
			
		||||
  author = {Bononi, Alberto and Beucher, Ottmar and Serena, Paolo}
 | 
			
		||||
 
 | 
			
		||||
@@ -173,5 +173,4 @@ texinfo_documents = [
 | 
			
		||||
     'Miscellaneous'),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
autodoc_default_flags = ['members', 'undoc-members', 'private-members', 'show-inheritance']
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								docs/images/GNPy-banner.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/images/GNPy-banner.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 518 KiB  | 
@@ -51,6 +51,8 @@ Contributors in alphabetical order
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
| 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                   |
 | 
			
		||||
@@ -61,20 +63,28 @@ Contributors in alphabetical order
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
| James    | Powell     | Telecom Infra Project | james.powell@telecominfraproject.com |
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
| Jeanluc  | Auge       | Orange                | jeanluc.auge@orange.com              |
 | 
			
		||||
| Jan      | Kundrát    | Telecom Infra Project | jan.kundrat@telecominfraproject.com  |
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
| Jonas    | Martensson | RISE Research Sweden  | jonas.martensson@ri.se               |
 | 
			
		||||
| Jeanluc  | Augé       | Orange                | jeanluc.auge@orange.com              |
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
| Jonas    | Mårtensson | RISE Research Sweden  | jonas.martensson@ri.se               |
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
| Mattia   | Cantono    | Politecnico di Torino | mattia.cantono@polito.it             |
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
| Miguel   | Garrich    | University Catalunya  | miquel.garrich@upct.es               |
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
| Stefan   | Melin      | Telia Company         | Stefan.Melin@teliacompany.com        |
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
| 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                 |
 | 
			
		||||
+----------+------------+-----------------------+--------------------------------------+
 | 
			
		||||
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,39 @@ gnpy\.core package
 | 
			
		||||
Submodules
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.ansi_escapes module
 | 
			
		||||
-------------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.ansi_escapes
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.convert module
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.convert
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.elements module
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.elements
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.equipment module
 | 
			
		||||
----------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.equipment
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.exceptions module
 | 
			
		||||
-----------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.exceptions
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
@@ -16,30 +45,34 @@ gnpy\.core\.execute module
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.execute
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.info module
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.info
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.network module
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.network
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.node module
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.node
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.request module
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.request
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.service_sheet module
 | 
			
		||||
--------------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.service_sheet
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
@@ -48,23 +81,14 @@ gnpy\.core\.units module
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.units
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 | 
			
		||||
gnpy\.core\.utils module
 | 
			
		||||
------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core.utils
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Module contents
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy.core
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,3 @@ Module contents
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: gnpy
 | 
			
		||||
    :members:
 | 
			
		||||
    :undoc-members:
 | 
			
		||||
    :show-inheritance:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										124
									
								
								examples/2019-demo-equipment.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								examples/2019-demo-equipment.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,124 @@
 | 
			
		||||
{     "Edfa":[
 | 
			
		||||
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "fixed27",
 | 
			
		||||
            "type_def": "fixed_gain",
 | 
			
		||||
            "gain_flatmax": 27,
 | 
			
		||||
            "gain_min": 27,
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf0": 5.5,
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            },
 | 
			
		||||
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "fixed22",
 | 
			
		||||
            "type_def": "fixed_gain",
 | 
			
		||||
            "gain_flatmax": 22,
 | 
			
		||||
            "gain_min": 22,
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf0": 5.5,
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            }
 | 
			
		||||
      ],
 | 
			
		||||
      "Fiber":[{
 | 
			
		||||
            "type_variety": "SSMF",
 | 
			
		||||
            "dispersion": 1.67e-05,
 | 
			
		||||
            "gamma": 0.00127
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "NZDF",
 | 
			
		||||
            "dispersion": 0.5e-05,
 | 
			
		||||
            "gamma": 0.00146
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "LOF",
 | 
			
		||||
            "dispersion": 2.2e-05,
 | 
			
		||||
            "gamma": 0.000843
 | 
			
		||||
            }
 | 
			
		||||
      ],
 | 
			
		||||
      "Span":[{
 | 
			
		||||
            "power_mode": false,
 | 
			
		||||
            "delta_power_range_db": [-2,3,0.5],
 | 
			
		||||
            "max_fiber_lineic_loss_for_raman": 0.25,
 | 
			
		||||
            "target_extended_gain": 2.5,
 | 
			
		||||
            "max_length": 150,
 | 
			
		||||
            "length_units": "km",
 | 
			
		||||
            "max_loss": 28,
 | 
			
		||||
            "padding": 10,
 | 
			
		||||
            "EOL": 0,
 | 
			
		||||
            "con_in": 0,
 | 
			
		||||
            "con_out": 0
 | 
			
		||||
            }
 | 
			
		||||
      ],
 | 
			
		||||
      "Roadm":[{
 | 
			
		||||
            "target_pch_out_db": -25,
 | 
			
		||||
            "add_drop_osnr": 30.00,
 | 
			
		||||
            "restrictions": {
 | 
			
		||||
                            "preamp_variety_list":[],
 | 
			
		||||
                            "booster_variety_list":[]
 | 
			
		||||
                            }            
 | 
			
		||||
            }],
 | 
			
		||||
      "SI":[{
 | 
			
		||||
            "f_min": 191.6e12,
 | 
			
		||||
            "baud_rate": 32e9,
 | 
			
		||||
            "f_max":195.1e12,
 | 
			
		||||
            "spacing": 50e9,
 | 
			
		||||
            "power_dbm": 0,
 | 
			
		||||
            "power_range_db": [0,0,1],
 | 
			
		||||
            "roll_off": 0.15,
 | 
			
		||||
            "tx_osnr": 40,
 | 
			
		||||
            "sys_margins": 2
 | 
			
		||||
            }],
 | 
			
		||||
      "Transceiver":[
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "Cassini",
 | 
			
		||||
            "frequency":{
 | 
			
		||||
                        "min": 191.35e12,
 | 
			
		||||
                        "max": 196.1e12
 | 
			
		||||
                        },
 | 
			
		||||
            "mode":[
 | 
			
		||||
                       {
 | 
			
		||||
 | 
			
		||||
                       "format": "dp-qpsk",
 | 
			
		||||
                       "baud_rate": 32e9,
 | 
			
		||||
                       "OSNR": 11,
 | 
			
		||||
                       "bit_rate": 100e9,
 | 
			
		||||
                       "roll_off": 0.15,
 | 
			
		||||
                       "tx_osnr": 40,
 | 
			
		||||
                       "min_spacing": 37.5e9,
 | 
			
		||||
                       "cost":1
 | 
			
		||||
                       },
 | 
			
		||||
                       {
 | 
			
		||||
                       "format": "16-qam",
 | 
			
		||||
                       "baud_rate": 66e9,
 | 
			
		||||
                       "OSNR": 15,
 | 
			
		||||
                       "bit_rate": 200e9,
 | 
			
		||||
                       "roll_off": 0.15,
 | 
			
		||||
                       "tx_osnr": 40,
 | 
			
		||||
                       "min_spacing": 75e9,
 | 
			
		||||
                       "cost":1
 | 
			
		||||
                       }
 | 
			
		||||
                   ]
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "Voyager",
 | 
			
		||||
            "frequency":{
 | 
			
		||||
                        "min": 191.35e12,
 | 
			
		||||
                        "max": 196.1e12
 | 
			
		||||
                        },
 | 
			
		||||
            "mode":[
 | 
			
		||||
                       {
 | 
			
		||||
                       "format": "mode 1",
 | 
			
		||||
                       "baud_rate": 32e9,
 | 
			
		||||
                       "OSNR": 12,
 | 
			
		||||
                       "bit_rate": 100e9,
 | 
			
		||||
                       "roll_off": 0.15,
 | 
			
		||||
                       "tx_osnr": 40,
 | 
			
		||||
                       "min_spacing": 37.5e9,
 | 
			
		||||
                       "cost":1
 | 
			
		||||
                       }
 | 
			
		||||
                   ]
 | 
			
		||||
            }
 | 
			
		||||
      ]
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								examples/2019-demo-services.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								examples/2019-demo-services.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,67 @@
 | 
			
		||||
{
 | 
			
		||||
  "path-request": [
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "first",
 | 
			
		||||
      "source": "netconf:10.0.254.93:830",
 | 
			
		||||
      "destination": "netconf:10.0.254.94:830",
 | 
			
		||||
      "src-tp-id": "trx-Amsterdam",
 | 
			
		||||
      "dst-tp-id": "trx-Bremen",
 | 
			
		||||
      "bidirectional": true,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Cassini",
 | 
			
		||||
          "trx_mode": null,
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 100000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "second",
 | 
			
		||||
      "source": "netconf:10.0.254.93:830",
 | 
			
		||||
      "destination": "netconf:10.0.254.94:830",
 | 
			
		||||
      "src-tp-id": "trx-Amsterdam",
 | 
			
		||||
      "dst-tp-id": "trx-Bremen",
 | 
			
		||||
      "bidirectional": true,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Cassini",
 | 
			
		||||
          "trx_mode": null,
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 100000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "synchronization": [
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "some redundancy please",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "false",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "first",
 | 
			
		||||
          "second"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1263
									
								
								examples/2019-demo-topology.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1263
									
								
								examples/2019-demo-topology.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										179
									
								
								examples/2019-generate-tip-demo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								examples/2019-generate-tip-demo.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,179 @@
 | 
			
		||||
# How many nodes in the ring topology? Up to eight is supported, then I ran out of cities..
 | 
			
		||||
HOW_MANY = 3
 | 
			
		||||
 | 
			
		||||
# city names
 | 
			
		||||
ALL_CITIES = [
 | 
			
		||||
    'Amsterdam',
 | 
			
		||||
    'Bremen',
 | 
			
		||||
    'Cologne',
 | 
			
		||||
    'Dueseldorf',
 | 
			
		||||
    'Eindhoven',
 | 
			
		||||
    'Frankfurt',
 | 
			
		||||
    'Ghent',
 | 
			
		||||
    'Hague',
 | 
			
		||||
]
 | 
			
		||||
# end of configurable parameters
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
J = {
 | 
			
		||||
    "elements": [],
 | 
			
		||||
    "connections": [],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
def unidir_join(a, b):
 | 
			
		||||
    global J
 | 
			
		||||
    J["connections"].append(
 | 
			
		||||
        {"from_node": a, "to_node": b}
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def mk_edfa(name, gain, voa=0.0):
 | 
			
		||||
    global J
 | 
			
		||||
    J["elements"].append(
 | 
			
		||||
        {"uid": name, "type": "Edfa", "type_variety": f"fixed{gain}", "operational": {"gain_target": gain, "out_voa": voa}}
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def add_att(a, b, att):
 | 
			
		||||
    global J
 | 
			
		||||
    if att > 0:
 | 
			
		||||
        uid = f"att-({a})-({b})"
 | 
			
		||||
    else:
 | 
			
		||||
        uid = f"splice-({a})-({b})"
 | 
			
		||||
    J["elements"].append(
 | 
			
		||||
        {"uid": uid, "type": "Fused", "params": {"loss": att}},
 | 
			
		||||
    )
 | 
			
		||||
    unidir_join(a, uid)
 | 
			
		||||
    unidir_join(uid, b)
 | 
			
		||||
    return uid
 | 
			
		||||
 | 
			
		||||
def build_fiber(city1, city2):
 | 
			
		||||
    global J
 | 
			
		||||
    J["elements"].append(
 | 
			
		||||
        {
 | 
			
		||||
            "uid": f"fiber-{city1}-{city2}",
 | 
			
		||||
            "type": "Fiber",
 | 
			
		||||
            "type_variety": "SSMF",
 | 
			
		||||
            "params": {
 | 
			
		||||
                "length": 50,
 | 
			
		||||
                "length_units": "km",
 | 
			
		||||
                "loss_coef": 0.2,
 | 
			
		||||
                "con_in": 1.5,
 | 
			
		||||
                "con_out": 1.5,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
def unidir_patch(a, b):
 | 
			
		||||
    global J
 | 
			
		||||
    uid = f"patch-({a})-({b})"
 | 
			
		||||
    J["elements"].append(
 | 
			
		||||
        {
 | 
			
		||||
            "uid": uid,
 | 
			
		||||
            "type": "Fiber",
 | 
			
		||||
            "type_variety": "SSMF",
 | 
			
		||||
            "params": {
 | 
			
		||||
                "length": 0,
 | 
			
		||||
                "length_units": "km",
 | 
			
		||||
                "loss_coef": 0.2,
 | 
			
		||||
                "con_in": 0.5,
 | 
			
		||||
                "con_out": 0.5,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
    add_att(a, uid, 0.0)
 | 
			
		||||
    add_att(uid, b, 0.0)
 | 
			
		||||
 | 
			
		||||
for CITY in (ALL_CITIES[x] for x in range(0, HOW_MANY)):
 | 
			
		||||
    J["elements"].append(
 | 
			
		||||
        {"uid": f"trx-{CITY}", "type": "Transceiver"}
 | 
			
		||||
    )
 | 
			
		||||
    target_pwr = [
 | 
			
		||||
        {"to_node": f"trx-{CITY}", "target_pch_out_db": -25},
 | 
			
		||||
        {"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L1))", "target_pch_out_db": -12},
 | 
			
		||||
        {"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L2))", "target_pch_out_db": -12},
 | 
			
		||||
    ]
 | 
			
		||||
    J["elements"].append(
 | 
			
		||||
        {"uid": f"roadm-{CITY}-AD", "type": "Roadm", "params": {"target_pch_out_db": -2.0, "per_degree_target_pch_out_db": target_pwr}}
 | 
			
		||||
    )
 | 
			
		||||
    unidir_join(f"trx-{CITY}", f"roadm-{CITY}-AD")
 | 
			
		||||
    unidir_join(f"roadm-{CITY}-AD", f"trx-{CITY}")
 | 
			
		||||
 | 
			
		||||
    for n in (1,2):
 | 
			
		||||
        target_pwr = [
 | 
			
		||||
            {"to_node": f"roadm-{CITY}-L{n}-booster", "target_pch_out_db": -23},
 | 
			
		||||
            {"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-AD))", "target_pch_out_db": -12},
 | 
			
		||||
        ]
 | 
			
		||||
        for m in (1,2):
 | 
			
		||||
            if m == n:
 | 
			
		||||
                continue
 | 
			
		||||
            target_pwr.append(
 | 
			
		||||
              {"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-L{m}))", "target_pch_out_db": -12},
 | 
			
		||||
            )
 | 
			
		||||
        J["elements"].append(
 | 
			
		||||
            {"uid": f"roadm-{CITY}-L{n}", "type": "Roadm", "params": {"target_pch_out_db": -23.0, "per_degree_target_pch_out_db": target_pwr}}
 | 
			
		||||
        )
 | 
			
		||||
        mk_edfa(f"roadm-{CITY}-L{n}-booster", 22)
 | 
			
		||||
        mk_edfa(f"roadm-{CITY}-L{n}-preamp", 27)
 | 
			
		||||
        unidir_join(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{n}-booster")
 | 
			
		||||
        unidir_join(f"roadm-{CITY}-L{n}-preamp", f"roadm-{CITY}-L{n}")
 | 
			
		||||
 | 
			
		||||
        unidir_patch(f"roadm-{CITY}-AD", f"roadm-{CITY}-L{n}")
 | 
			
		||||
        unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-AD")
 | 
			
		||||
        for m in (1,2):
 | 
			
		||||
            if m == n:
 | 
			
		||||
                continue
 | 
			
		||||
            #add_att(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}", 22)
 | 
			
		||||
            unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}")
 | 
			
		||||
 | 
			
		||||
for city1, city2 in ((ALL_CITIES[i], ALL_CITIES[i + 1] if i < HOW_MANY - 1 else ALL_CITIES[0]) for i in range(0, HOW_MANY)):
 | 
			
		||||
    build_fiber(city1, city2)
 | 
			
		||||
    unidir_join(f"roadm-{city1}-L1-booster", f"fiber-{city1}-{city2}")
 | 
			
		||||
    unidir_join(f"fiber-{city1}-{city2}", f"roadm-{city2}-L2-preamp")
 | 
			
		||||
    build_fiber(city2, city1)
 | 
			
		||||
    unidir_join(f"roadm-{city2}-L2-booster", f"fiber-{city2}-{city1}")
 | 
			
		||||
    unidir_join(f"fiber-{city2}-{city1}", f"roadm-{city1}-L1-preamp")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
for _, E in enumerate(J["elements"]):
 | 
			
		||||
    uid = E["uid"]
 | 
			
		||||
    if uid.startswith("roadm-") and (uid.endswith("-L1-booster") or uid.endswith("-L2-booster")):
 | 
			
		||||
        E["operational"]["out_voa"] = 12.0
 | 
			
		||||
    #if uid.endswith("-AD-add"):
 | 
			
		||||
    #    E["operational"]["out_voa"] = 21
 | 
			
		||||
 | 
			
		||||
translate = {
 | 
			
		||||
    #"trx-Amsterdam": "10.0.254.93",
 | 
			
		||||
    #"trx-Bremen": "10.0.254.94",
 | 
			
		||||
    "trx-Amsterdam": "10.0.254.76",
 | 
			
		||||
    "trx-Bremen": "10.0.254.77",
 | 
			
		||||
 | 
			
		||||
    # Amsterdam A/D: coherent-v9u
 | 
			
		||||
    "roadm-Amsterdam-AD": "10.0.254.107",
 | 
			
		||||
    # Bremen A/D: -spi
 | 
			
		||||
    "roadm-Bremen-AD": "10.0.254.225",
 | 
			
		||||
 | 
			
		||||
    # Amsterdam -> Bremen ...QR79
 | 
			
		||||
    "roadm-Amsterdam-L1": "10.0.254.78",
 | 
			
		||||
    # Bremen -> Amsterdam ...QCP9
 | 
			
		||||
    "roadm-Bremen-L2": "10.0.254.102",
 | 
			
		||||
 | 
			
		||||
    # Bremen -> Cologne ...WKP
 | 
			
		||||
    "roadm-Bremen-L1": "10.0.254.100",
 | 
			
		||||
    # Cologne -> Bremen ...QLK6
 | 
			
		||||
    "roadm-Cologne-L2": "10.0.254.104",
 | 
			
		||||
 | 
			
		||||
    # Cologne -> Amsterdam ...TQQ
 | 
			
		||||
    "roadm-Cologne-L1": "10.0.254.99",
 | 
			
		||||
    # Amsterdam -> Cologne ...Q7JS
 | 
			
		||||
    "roadm-Amsterdam-L2": "10.0.254.79",
 | 
			
		||||
 | 
			
		||||
    # spare Line/Degree ...QC8B
 | 
			
		||||
    "spare-line-degree": "10.0.254.101",
 | 
			
		||||
    # spare Add/Drop: ...NNN
 | 
			
		||||
    "spare-add-drop": "10.0.254.228",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
s = json.dumps(J, indent=2)
 | 
			
		||||
for (old, new) in translate.items():
 | 
			
		||||
    s = s.replace(f'"{old}"', f'"netconf:{new}:830"')
 | 
			
		||||
print(s)
 | 
			
		||||
							
								
								
									
										160
									
								
								examples/Juniper-BoosterHG.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								examples/Juniper-BoosterHG.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
			
		||||
{
 | 
			
		||||
      "nf_fit_coeff": [
 | 
			
		||||
        0.0008,
 | 
			
		||||
        0.0272,
 | 
			
		||||
        -0.2249,
 | 
			
		||||
        6.4902
 | 
			
		||||
       ],
 | 
			
		||||
      "f_min": 191.35e12,
 | 
			
		||||
      "f_max": 196.1e12,
 | 
			
		||||
       "nf_ripple": [
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0,
 | 
			
		||||
        0.0
 | 
			
		||||
    ],
 | 
			
		||||
    "gain_ripple": [
 | 
			
		||||
        0.15017064489112,
 | 
			
		||||
        0.14157768006701,
 | 
			
		||||
        0.00223094639866,
 | 
			
		||||
        -0.06701528475711,
 | 
			
		||||
        -0.05982935510889,
 | 
			
		||||
        -0.01028161641541,
 | 
			
		||||
        0.02740682579566,
 | 
			
		||||
        0.02795958961474,
 | 
			
		||||
        0.00107516750419,
 | 
			
		||||
        -0.02199015912898,
 | 
			
		||||
        -0.00877407872698,
 | 
			
		||||
        0.0453465242881,
 | 
			
		||||
        0.1204721524288,
 | 
			
		||||
        0.18936662479061,
 | 
			
		||||
        0.23826109715241,
 | 
			
		||||
        0.26956762981574,
 | 
			
		||||
        0.27836159966498,
 | 
			
		||||
        0.26941687604691,
 | 
			
		||||
        0.23579878559464,
 | 
			
		||||
        0.18147717755444,
 | 
			
		||||
        0.1191656197655,
 | 
			
		||||
        0.05921587102177,
 | 
			
		||||
        0.01509526800668,
 | 
			
		||||
        -0.01053287269681,
 | 
			
		||||
        -0.02475397822447,
 | 
			
		||||
        -0.01847257118928,
 | 
			
		||||
        -0.00420121440538,
 | 
			
		||||
        0.01584903685091,
 | 
			
		||||
        0.0399193886097,
 | 
			
		||||
        0.04494451423784,
 | 
			
		||||
        0.04961788107202,
 | 
			
		||||
        0.03378873534338,
 | 
			
		||||
        0.01027114740367,
 | 
			
		||||
        -0.01319618927973,
 | 
			
		||||
        -0.04962835008375,
 | 
			
		||||
        -0.0765630234506,
 | 
			
		||||
        -0.10606051088777,
 | 
			
		||||
        -0.13550774706866,
 | 
			
		||||
        -0.15460322445561,
 | 
			
		||||
        -0.17113588777219,
 | 
			
		||||
        -0.18053287269681,
 | 
			
		||||
        -0.18324644053602,
 | 
			
		||||
        -0.19440221943049,
 | 
			
		||||
        -0.20897508375209,
 | 
			
		||||
        -0.23575900335007,
 | 
			
		||||
        -0.25188965661642,
 | 
			
		||||
        -0.22244242043552,
 | 
			
		||||
        -0.15656302345061
 | 
			
		||||
    ],
 | 
			
		||||
    "dgt": [
 | 
			
		||||
        2.4553191172498,
 | 
			
		||||
        2.44342862248888,
 | 
			
		||||
        2.41879254989742,
 | 
			
		||||
        2.38192717604575,
 | 
			
		||||
        2.33147727493671,
 | 
			
		||||
        2.26678136721453,
 | 
			
		||||
        2.19013043016015,
 | 
			
		||||
        2.10336369905543,
 | 
			
		||||
        2.01414465424155,
 | 
			
		||||
        1.92915262384742,
 | 
			
		||||
        1.85543800978691,
 | 
			
		||||
        1.79748596476494,
 | 
			
		||||
        1.75428006928365,
 | 
			
		||||
        1.72461030013125,
 | 
			
		||||
        1.70379790088896,
 | 
			
		||||
        1.68845480656382,
 | 
			
		||||
        1.6761448370895,
 | 
			
		||||
        1.66286684904577,
 | 
			
		||||
        1.64799163036252,
 | 
			
		||||
        1.63068023161292,
 | 
			
		||||
        1.61073904908309,
 | 
			
		||||
        1.58973304612691,
 | 
			
		||||
        1.56750088631614,
 | 
			
		||||
        1.54578500307573,
 | 
			
		||||
        1.5242627235492,
 | 
			
		||||
        1.50335352244996,
 | 
			
		||||
        1.48420288841848,
 | 
			
		||||
        1.46637521309853,
 | 
			
		||||
        1.44977369463316,
 | 
			
		||||
        1.43476940680732,
 | 
			
		||||
        1.42089447397912,
 | 
			
		||||
        1.40864903907609,
 | 
			
		||||
        1.3966294751726,
 | 
			
		||||
        1.38430337205545,
 | 
			
		||||
        1.3710092503689,
 | 
			
		||||
        1.35690844654118,
 | 
			
		||||
        1.3405812000038,
 | 
			
		||||
        1.32210817897091,
 | 
			
		||||
        1.30069883494415,
 | 
			
		||||
        1.27657903892303,
 | 
			
		||||
        1.24931318255134,
 | 
			
		||||
        1.21911100318577,
 | 
			
		||||
        1.18632744096844,
 | 
			
		||||
        1.15209185089701,
 | 
			
		||||
        1.11575888725852,
 | 
			
		||||
        1.07773189112355,
 | 
			
		||||
        1.03941448941778,
 | 
			
		||||
        1.0
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
@@ -11,64 +11,72 @@ If not present in the "Nodes" sheet, the "Type" column will be implicitly
 | 
			
		||||
determined based on the topology.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from sys import exit
 | 
			
		||||
try:
 | 
			
		||||
    from xlrd import open_workbook
 | 
			
		||||
except ModuleNotFoundError:
 | 
			
		||||
    exit('Required: `pip install xlrd`')
 | 
			
		||||
from argparse import ArgumentParser
 | 
			
		||||
from collections import namedtuple, defaultdict
 | 
			
		||||
 | 
			
		||||
PARSER = ArgumentParser()
 | 
			
		||||
PARSER.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls',
 | 
			
		||||
                    help='create the mandatory columns in Eqpt sheet')
 | 
			
		||||
ALL_ROWS = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
 | 
			
		||||
 | 
			
		||||
Shortlink = namedtuple('Link', 'src dest')
 | 
			
		||||
class Node:
 | 
			
		||||
    """ Node element contains uid, list of connected nodes and eqpt type
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, uid, to_node):
 | 
			
		||||
        self.uid = uid
 | 
			
		||||
        self.to_node = to_node
 | 
			
		||||
        self.eqpt = None
 | 
			
		||||
 | 
			
		||||
Shortnode = namedtuple('Node', 'nodename eqt')
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n'
 | 
			
		||||
 | 
			
		||||
parser = ArgumentParser()
 | 
			
		||||
parser.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls',
 | 
			
		||||
    help = 'create the mandatory columns in Eqpt sheet  ')
 | 
			
		||||
all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n'
 | 
			
		||||
 | 
			
		||||
def read_excel(input_filename):
 | 
			
		||||
    with open_workbook(input_filename) as wb:
 | 
			
		||||
    """ read excel Nodes and Links sheets and create a dict of nodes with
 | 
			
		||||
    their to_nodes and type of eqpt
 | 
			
		||||
    """
 | 
			
		||||
    with open_workbook(input_filename) as wobo:
 | 
			
		||||
        # reading Links sheet
 | 
			
		||||
        links_sheet = wb.sheet_by_name('Links')
 | 
			
		||||
        links = []
 | 
			
		||||
        nodeoccuranceinlinks = []
 | 
			
		||||
        links_by_src = defaultdict(list)
 | 
			
		||||
        links_by_dest = defaultdict(list)
 | 
			
		||||
        for row in all_rows(links_sheet, start=5):
 | 
			
		||||
            links.append(Shortlink(row[0].value,row[1].value))
 | 
			
		||||
            links_by_src[row[0].value].append(Shortnode(row[1].value,''))
 | 
			
		||||
            links_by_dest[row[1].value].append(Shortnode(row[0].value,''))
 | 
			
		||||
            #print(f'source {links[len(links)-1].src} dest {links[len(links)-1].dest}')
 | 
			
		||||
            nodeoccuranceinlinks.append(row[0].value)
 | 
			
		||||
            nodeoccuranceinlinks.append(row[1].value)
 | 
			
		||||
        links_sheet = wobo.sheet_by_name('Links')
 | 
			
		||||
        nodes = {}
 | 
			
		||||
        for row in ALL_ROWS(links_sheet, start=5):
 | 
			
		||||
            try:
 | 
			
		||||
                nodes[row[0].value].to_node.append(row[1].value)
 | 
			
		||||
            except KeyError:
 | 
			
		||||
                nodes[row[0].value] = Node(row[0].value, [row[1].value])
 | 
			
		||||
            try:
 | 
			
		||||
                nodes[row[1].value].to_node.append(row[0].value)
 | 
			
		||||
            except KeyError:
 | 
			
		||||
                nodes[row[1].value] = Node(row[1].value, [row[0].value])
 | 
			
		||||
 | 
			
		||||
        # reading Nodes sheet
 | 
			
		||||
        nodes_sheet = wb.sheet_by_name('Nodes')
 | 
			
		||||
        nodes = []
 | 
			
		||||
        node_degree = []
 | 
			
		||||
        for row in all_rows(nodes_sheet, start=5) :
 | 
			
		||||
        nodes_sheet = wobo.sheet_by_name('Nodes')
 | 
			
		||||
        for row in ALL_ROWS(nodes_sheet, start=5):
 | 
			
		||||
            node = row[0].value
 | 
			
		||||
            eqpt = row[6].value
 | 
			
		||||
            try:
 | 
			
		||||
                if eqpt == 'ILA' and len(nodes[node].to_node) != 2:
 | 
			
		||||
                    print(f'Inconsistancy ILA node with degree > 2: {node} ')
 | 
			
		||||
                    exit()
 | 
			
		||||
                if eqpt == '' and len(nodes[node].to_node) == 2:
 | 
			
		||||
                    nodes[node].eqpt = 'ILA'
 | 
			
		||||
                elif eqpt == '' and len(nodes[node].to_node) != 2:
 | 
			
		||||
                    nodes[node].eqpt = 'ROADM'
 | 
			
		||||
                else:
 | 
			
		||||
                    nodes[node].eqpt = eqpt
 | 
			
		||||
            except KeyError:
 | 
			
		||||
                print(f'inconsistancy between nodes and links sheet: {node} is not listed in links')
 | 
			
		||||
                exit()
 | 
			
		||||
        return nodes
 | 
			
		||||
 | 
			
		||||
            temp_eqt = row[6].value
 | 
			
		||||
            # verify node degree to confirm eqt type
 | 
			
		||||
            node_degree.append(nodeoccuranceinlinks.count(row[0].value))
 | 
			
		||||
            if temp_eqt.lower() == 'ila' and nodeoccuranceinlinks.count(row[0].value) !=2 :
 | 
			
		||||
                print(f'Inconsistancy: node {nodes[len(nodes)-1]} has degree \
 | 
			
		||||
                    {node_degree[len(nodes)-1]} and can not be an ILA ... replaced by ROADM')
 | 
			
		||||
                temp_eqt = 'ROADM'
 | 
			
		||||
            if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) == 2 :
 | 
			
		||||
                temp_eqt = 'ILA'
 | 
			
		||||
            if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) != 2 :
 | 
			
		||||
                temp_eqt = 'ROADM'
 | 
			
		||||
            # print(f'node {nodes[len(nodes)-1]} eqt {temp_eqt}')
 | 
			
		||||
            nodes.append(Shortnode(row[0].value,temp_eqt))
 | 
			
		||||
            # print(len(nodes)-1)
 | 
			
		||||
            print(f'reading: node {nodes[len(nodes)-1].nodename} eqpt {temp_eqt}')
 | 
			
		||||
        return links,nodes, links_by_src , links_by_dest
 | 
			
		||||
 | 
			
		||||
def create_eqt_template(links,nodes, links_by_src , links_by_dest, input_filename):
 | 
			
		||||
def create_eqt_template(nodes, input_filename):
 | 
			
		||||
    """ writes list of node A node Z corresponding to Nodes and Links sheets in order
 | 
			
		||||
    to help user populating Eqpt
 | 
			
		||||
    """
 | 
			
		||||
    output_filename = f'{input_filename[:-4]}_eqpt_sheet.txt'
 | 
			
		||||
    with open(output_filename, 'w', encoding='utf-8') as my_file:
 | 
			
		||||
        # print header similar to excel
 | 
			
		||||
@@ -77,27 +85,17 @@ def create_eqt_template(links,nodes, links_by_src , links_by_dest, input_filenam
 | 
			
		||||
           \nNode A \tNode Z \tamp type \tatt_in \tamp gain \ttilt \tatt_out\
 | 
			
		||||
           amp type   \tatt_in \tamp gain   \ttilt   \tatt_out\n')
 | 
			
		||||
 | 
			
		||||
        tab = []
 | 
			
		||||
        temp = []
 | 
			
		||||
        i = 0
 | 
			
		||||
        for lk in links:
 | 
			
		||||
            if [e for n,e in nodes if n==lk.src][0] != 'FUSED' :
 | 
			
		||||
                temp = [lk.src , lk.dest]
 | 
			
		||||
                tab.append(temp)
 | 
			
		||||
                my_file.write(f'{temp[0]}\t{temp[1]}\n')
 | 
			
		||||
        for n in nodes :
 | 
			
		||||
            if n.eqt.lower() == 'roadm' :
 | 
			
		||||
                for src in  links_by_dest[n.nodename] :
 | 
			
		||||
                    temp = [n.nodename , src.nodename]
 | 
			
		||||
                    tab.append(temp)
 | 
			
		||||
                    # print(temp)
 | 
			
		||||
                    my_file.write(f'{temp[0]}\t{temp[1]}\n')
 | 
			
		||||
            i = i + 1
 | 
			
		||||
 | 
			
		||||
        for node in nodes.values():
 | 
			
		||||
            if node.eqpt == 'ILA':
 | 
			
		||||
                my_file.write(f'{node.uid}\t{node.to_node[0]}\n')
 | 
			
		||||
            if node.eqpt == 'ROADM':
 | 
			
		||||
                for to_node in node.to_node:
 | 
			
		||||
                    my_file.write(f'{node.uid}\t{to_node}\n')
 | 
			
		||||
 | 
			
		||||
        print(f'File {output_filename} successfully created with Node A - Node Z ' +
 | 
			
		||||
        ' entries for Eqpt sheet in excel file.')
 | 
			
		||||
              ' entries for Eqpt sheet in excel file.')
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
    input_filename = args.workbook
 | 
			
		||||
    links,nodes,links_by_src, links_by_dest = read_excel(input_filename)
 | 
			
		||||
    create_eqt_template(links,nodes, links_by_src , links_by_dest , input_filename)
 | 
			
		||||
    ARGS = PARSER.parse_args()
 | 
			
		||||
    create_eqt_template(read_excel(ARGS.workbook), ARGS.workbook)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1033
									
								
								examples/demo.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1033
									
								
								examples/demo.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,5 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "nf_ripple": "NFR0_96.txt", 
 | 
			
		||||
    "gain_ripple": "DFG0_96.txt",
 | 
			
		||||
    "dgt": "DGT_96.txt"    
 | 
			
		||||
    "nf_ripple": "NFR_96.txt", 
 | 
			
		||||
    "gain_ripple": "DFG_96.txt",
 | 
			
		||||
    "dgt": "DGT_96.txt",
 | 
			
		||||
    "nf_fit_coeff": "pNFfit3.txt"
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01
 | 
			
		||||
  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02
 | 
			
		||||
  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01
 | 
			
		||||
  -2.0500000000000000e+01  -2.0489473680000000e+01  -2.0478947370000000e+01  -2.0468421050000000e+01  -2.0457894740000000e+01  -2.0447368420000000e+01  -2.0436842110000001e+01  -2.0426315790000000e+01  -2.0415789470000000e+01  -2.0405263160000001e+01  -2.0394736840000000e+01  -2.0384210530000001e+01  -2.0373684210000000e+01  -2.0363157890000000e+01  -2.0352631580000001e+01  -2.0342105260000000e+01  -2.0331578950000001e+01  -2.0321052630000001e+01  -2.0310526320000001e+01  -2.0300000000000001e+01  -2.0289473680000000e+01  -2.0278947370000001e+01  -2.0268421050000001e+01  -2.0257894740000001e+01  -2.0247368420000001e+01  -2.0236842110000001e+01  -2.0226315790000001e+01  -2.0215789470000001e+01  -2.0205263160000001e+01  -2.0194736840000001e+01  -2.0184210530000001e+01  -2.0173684210000001e+01  -2.0163157890000001e+01  -2.0152631580000001e+01  -2.0142105260000001e+01  -2.0131578950000002e+01  -2.0121052630000001e+01  -2.0110526320000002e+01  -2.0100000000000001e+01  -2.0089473680000001e+01  -2.0078947370000002e+01  -2.0068421050000001e+01  -2.0057894739999998e+01  -2.0047368420000002e+01  -2.0036842109999998e+01  -2.0026315790000002e+01  -2.0015789470000001e+01  -2.0005263159999998e+01  -1.9994736840000002e+01  -1.9984210529999999e+01  -1.9973684209999998e+01  -1.9963157890000002e+01  -1.9952631579999998e+01  -1.9942105260000002e+01  -1.9931578949999999e+01  -1.9921052629999998e+01  -1.9910526319999999e+01  -1.9899999999999999e+01  -1.9889473679999998e+01  -1.9878947369999999e+01  -1.9868421049999998e+01  -1.9857894739999999e+01  -1.9847368419999999e+01  -1.9836842109999999e+01  -1.9826315789999999e+01  -1.9815789469999999e+01  -1.9805263159999999e+01  -1.9794736839999999e+01  -1.9784210529999999e+01  -1.9773684209999999e+01  -1.9763157889999999e+01  -1.9752631579999999e+01  -1.9742105259999999e+01  -1.9731578949999999e+01  -1.9721052629999999e+01  -1.9710526320000000e+01  -1.9699999999999999e+01  -1.9689473679999999e+01  -1.9678947369999999e+01  -1.9668421049999999e+01  -1.9657894740000000e+01  -1.9647368419999999e+01  -1.9636842110000000e+01  -1.9626315790000000e+01  -1.9615789469999999e+01  -1.9605263160000000e+01  -1.9594736839999999e+01  -1.9584210530000000e+01  -1.9573684210000000e+01  -1.9563157889999999e+01  -1.9552631580000000e+01  -1.9542105260000000e+01  -1.9531578950000000e+01  -1.9521052630000000e+01  -1.9510526320000000e+01  -1.9500000000000000e+01
 | 
			
		||||
  -2.0500000000000000e+01  -2.0489473680000000e+01  -2.0478947370000000e+01  -2.0468421050000000e+01  -2.0457894740000000e+01  -2.0447368420000000e+01  -2.0436842110000001e+01  -2.0426315790000000e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.9573684210000000e+01  -1.9563157889999999e+01  -1.9552631580000000e+01  -1.9542105260000000e+01  -1.9531578950000000e+01  -1.9521052630000000e+01  -1.9510526320000000e+01  -1.9500000000000000e+01
 | 
			
		||||
  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.4460000000000001e+01
 | 
			
		||||
  -1.4460000000000001e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01
 | 
			
		||||
  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.4460000000000001e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02
 | 
			
		||||
@@ -1,8 +0,0 @@
 | 
			
		||||
   7.0000000000000000e+01	   1.1700000000000000e+02	   1.0800000000000000e+02	   1.0800000000000000e+02	   3.2000000000000000e+01	   7.0000000000000000e+01	   1.0800000000000000e+02	   9.7000000000000000e+01	   1.1600000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	
 | 
			
		||||
   7.9000000000000000e+01	   1.1000000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	   7.1000000000000000e+01	   1.1400000000000000e+02	   1.1100000000000000e+02	   1.1700000000000000e+02	   1.1200000000000000e+02	   3.2000000000000000e+01	   6.6000000000000000e+01	   1.0800000000000000e+02	   1.1700000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	
 | 
			
		||||
   7.9000000000000000e+01	   1.1000000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	   7.1000000000000000e+01	   1.1400000000000000e+02	   1.1100000000000000e+02	   1.1700000000000000e+02	   1.1200000000000000e+02	   3.2000000000000000e+01	   8.2000000000000000e+01	   1.0100000000000000e+02	   1.0000000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	
 | 
			
		||||
   7.0000000000000000e+01	   1.1700000000000000e+02	   1.0800000000000000e+02	   1.0800000000000000e+02	   3.2000000000000000e+01	   1.1900000000000000e+02	   3.2000000000000000e+01	   8.3000000000000000e+01	   8.2000000000000000e+01	   8.3000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	
 | 
			
		||||
   6.6000000000000000e+01	   1.1100000000000000e+02	   1.1600000000000000e+02	   1.0400000000000000e+02	   3.2000000000000000e+01	   6.9000000000000000e+01	   1.1000000000000000e+02	   1.0000000000000000e+02	   1.1500000000000000e+02	   3.2000000000000000e+01	   1.1900000000000000e+02	   3.2000000000000000e+01	   8.3000000000000000e+01	   8.2000000000000000e+01	   8.3000000000000000e+01	
 | 
			
		||||
   1.0400000000000000e+02	   1.0100000000000000e+02	   9.7000000000000000e+01	   1.1800000000000000e+02	   1.2100000000000000e+02	   3.2000000000000000e+01	   9.8000000000000000e+01	   1.0800000000000000e+02	   1.1700000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	
 | 
			
		||||
   1.0400000000000000e+02	   1.0100000000000000e+02	   9.7000000000000000e+01	   1.1800000000000000e+02	   1.2100000000000000e+02	   3.2000000000000000e+01	   1.1400000000000000e+02	   1.0100000000000000e+02	   1.0000000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	
 | 
			
		||||
   1.1900000000000000e+02	   1.1100000000000000e+02	   1.1400000000000000e+02	   1.1500000000000000e+02	   1.1600000000000000e+02	   3.2000000000000000e+01	   9.9000000000000000e+01	   9.7000000000000000e+01	   1.1500000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	
 | 
			
		||||
@@ -1,301 +0,0 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
"""
 | 
			
		||||
Created on Mon Nov 27 12:32:04 2017
 | 
			
		||||
 | 
			
		||||
@author: briantaylor
 | 
			
		||||
"""
 | 
			
		||||
import numpy as np
 | 
			
		||||
from numpy import polyfit, polyval, mean
 | 
			
		||||
from utilities import lin2db, db2lin, itufs, freq2wavelength
 | 
			
		||||
import matplotlib.pyplot as plt
 | 
			
		||||
from scipy.constants import h
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def noise_profile(nf, gain, ffs, df):
 | 
			
		||||
    """ noise_profile(nf, gain, ffs, df) computes amplifier ase
 | 
			
		||||
 | 
			
		||||
    :param nf: Noise figure in dB
 | 
			
		||||
    :param gain: Actual gain calculated for the EDFA in dB units
 | 
			
		||||
    :param ffs: A numpy array of frequencies
 | 
			
		||||
    :param df: the reference bw in THz
 | 
			
		||||
    :type nf: numpy.ndarray
 | 
			
		||||
    :type gain: numpy.ndarray
 | 
			
		||||
    :type ffs: numpy.ndarray
 | 
			
		||||
    :type df: float
 | 
			
		||||
    :return: the asepower in dBm
 | 
			
		||||
    :rtype: numpy.ndarray
 | 
			
		||||
 | 
			
		||||
    ASE POWER USING PER CHANNEL GAIN PROFILE
 | 
			
		||||
    INPUTS:
 | 
			
		||||
    NF_dB - Noise figure in dB, vector of length number of channels or
 | 
			
		||||
            spectral slices
 | 
			
		||||
    G_dB  - Actual gain calculated for the EDFA, vector of length number of
 | 
			
		||||
            channels or spectral slices
 | 
			
		||||
    ffs     - Center frequency grid of the channels or spectral slices in THz,
 | 
			
		||||
            vector of length number of channels or spectral slices
 | 
			
		||||
    dF    - width of each channel or spectral slice in THz,
 | 
			
		||||
            vector of length number of channels or spectral slices
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        ase_dBm - ase in dBm per channel or spectral slice
 | 
			
		||||
    NOTE: the output is the total ASE in the channel or spectral slice. For
 | 
			
		||||
    50GHz channels the ASE BW is effectively 0.4nm. To get to noise power in
 | 
			
		||||
    0.1nm, subtract 6dB.
 | 
			
		||||
 | 
			
		||||
    ONSR is usually quoted as channel power divided by
 | 
			
		||||
    the ASE power in 0.1nm RBW, regardless of the width of the actual
 | 
			
		||||
    channel.  This is a historical convention from the days when optical
 | 
			
		||||
    signals were much smaller (155Mbps, 2.5Gbps, ... 10Gbps) than the
 | 
			
		||||
    resolution of the OSAs that were used to measure spectral power which
 | 
			
		||||
    were set to 0.1nm resolution for convenience.  Moving forward into
 | 
			
		||||
    flexible grid and high baud rate signals, it may be convenient to begin
 | 
			
		||||
    quoting power spectral density in the same BW for both signal and ASE,
 | 
			
		||||
    e.g. 12.5GHz."""
 | 
			
		||||
 | 
			
		||||
    h_mWThz = 1e-3 * h * (1e14)**2
 | 
			
		||||
    nf_lin = db2lin(nf)
 | 
			
		||||
    g_lin = db2lin(gain)
 | 
			
		||||
    ase = h_mWThz * df * ffs * (nf_lin * g_lin - 1)
 | 
			
		||||
    asedb = lin2db(ase)
 | 
			
		||||
 | 
			
		||||
    return asedb
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def gain_profile(dfg, dgt, Pin, gp, gtp):
 | 
			
		||||
    """
 | 
			
		||||
    :param dfg: design flat gain
 | 
			
		||||
    :param dgt: design gain tilt
 | 
			
		||||
    :param Pin: channing input power profile
 | 
			
		||||
    :param gp: Average gain setpoint in dB units
 | 
			
		||||
    :param gtp: gain tilt setting
 | 
			
		||||
    :type dfg: numpy.ndarray
 | 
			
		||||
    :type dgt: numpy.ndarray
 | 
			
		||||
    :type Pin: numpy.ndarray
 | 
			
		||||
    :type gp: float
 | 
			
		||||
    :type gtp: float
 | 
			
		||||
    :return: gain profile in dBm
 | 
			
		||||
    :rtype: numpy.ndarray
 | 
			
		||||
 | 
			
		||||
    AMPLIFICATION USING INPUT PROFILE
 | 
			
		||||
    INPUTS:
 | 
			
		||||
        DFG - vector of length number of channels or spectral slices
 | 
			
		||||
        DGT - vector of length number of channels or spectral slices
 | 
			
		||||
        Pin - input powers vector of length number of channels or
 | 
			
		||||
        spectral slices
 | 
			
		||||
        Gp  - provisioned gain length 1
 | 
			
		||||
        GTp - provisioned tilt length 1
 | 
			
		||||
 | 
			
		||||
    OUTPUT:
 | 
			
		||||
        amp gain per channel or spectral slice
 | 
			
		||||
    NOTE: there is no checking done for violations of the total output power
 | 
			
		||||
        capability of the amp.
 | 
			
		||||
        Ported from Matlab version written by David Boerges at Ciena.
 | 
			
		||||
    Based on:
 | 
			
		||||
        R. di Muro, "The Er3+ fiber gain coefficient derived from a dynamic
 | 
			
		||||
        gain
 | 
			
		||||
        tilt technique", Journal of Lightwave Technology, Vol. 18, Iss. 3,
 | 
			
		||||
        Pp. 343-347, 2000.
 | 
			
		||||
    """
 | 
			
		||||
    err_tolerance = 1.0e-11
 | 
			
		||||
    simple_opt = True
 | 
			
		||||
 | 
			
		||||
    # TODO make all values linear unit and convert to dB units as needed within
 | 
			
		||||
    # this function.
 | 
			
		||||
    nchan = list(range(len(Pin)))
 | 
			
		||||
 | 
			
		||||
    # TODO find a way to use these or lose them.  Primarily we should have a
 | 
			
		||||
    # way to determine if exceeding the gain or output power of the amp
 | 
			
		||||
    tot_in_power_db = lin2db(np.sum(db2lin(Pin)))
 | 
			
		||||
    avg_gain_db = lin2db(mean(db2lin(dfg)))
 | 
			
		||||
 | 
			
		||||
    # Linear fit to get the
 | 
			
		||||
    p = polyfit(nchan, dgt, 1)
 | 
			
		||||
    dgt_slope = p[0]
 | 
			
		||||
 | 
			
		||||
    # Calculate the target slope-  Currently assumes equal spaced channels
 | 
			
		||||
    # TODO make it so that supports arbitrary channel spacing.
 | 
			
		||||
    targ_slope = gtp / (len(nchan) - 1)
 | 
			
		||||
 | 
			
		||||
    # 1st estimate of DGT scaling
 | 
			
		||||
    dgts1 = targ_slope / dgt_slope
 | 
			
		||||
 | 
			
		||||
    # when simple_opt is true code makes 2 attempts to compute gain and
 | 
			
		||||
    # the internal voa value.  This is currently here to provide direct
 | 
			
		||||
    # comparison with original Matlab code.  Will be removed.
 | 
			
		||||
    # TODO replace with loop
 | 
			
		||||
 | 
			
		||||
    if simple_opt:
 | 
			
		||||
 | 
			
		||||
        # 1st estimate of Er gain & voa loss
 | 
			
		||||
        g1st = dfg + dgt * dgts1
 | 
			
		||||
        voa = lin2db(mean(db2lin(g1st))) - gp
 | 
			
		||||
 | 
			
		||||
        # 2nd estimate of Amp ch gain using the channel input profile
 | 
			
		||||
        g2nd = g1st - voa
 | 
			
		||||
        pout_db = lin2db(np.sum(db2lin(Pin + g2nd)))
 | 
			
		||||
        dgts2 = gp - (pout_db - tot_in_power_db)
 | 
			
		||||
 | 
			
		||||
        # Center estimate of amp ch gain
 | 
			
		||||
        xcent = dgts2
 | 
			
		||||
        gcent = g1st - voa + dgt * xcent
 | 
			
		||||
        pout_db = lin2db(np.sum(db2lin(Pin + gcent)))
 | 
			
		||||
        gavg_cent = pout_db - tot_in_power_db
 | 
			
		||||
 | 
			
		||||
        # Lower estimate of Amp ch gain
 | 
			
		||||
        deltax = np.max(g1st) - np.min(g1st)
 | 
			
		||||
        xlow = dgts2 - deltax
 | 
			
		||||
        glow = g1st - voa + xlow * dgt
 | 
			
		||||
        pout_db = lin2db(np.sum(db2lin(Pin + glow)))
 | 
			
		||||
        gavg_low = pout_db - tot_in_power_db
 | 
			
		||||
 | 
			
		||||
        # Upper gain estimate
 | 
			
		||||
        xhigh = dgts2 + deltax
 | 
			
		||||
        ghigh = g1st - voa + xhigh * dgt
 | 
			
		||||
        pout_db = lin2db(np.sum(db2lin(Pin + ghigh)))
 | 
			
		||||
        gavg_high = pout_db - tot_in_power_db
 | 
			
		||||
 | 
			
		||||
        # compute slope
 | 
			
		||||
        slope1 = (gavg_low - gavg_cent) / (xlow - xcent)
 | 
			
		||||
        slope2 = (gavg_cent - gavg_high) / (xcent - xhigh)
 | 
			
		||||
 | 
			
		||||
        if np.abs(gp - gavg_cent) <= err_tolerance:
 | 
			
		||||
            dgts3 = xcent
 | 
			
		||||
        elif gp < gavg_cent:
 | 
			
		||||
            dgts3 = xcent - (gavg_cent - gp) / slope1
 | 
			
		||||
        else:
 | 
			
		||||
            dgts3 = xcent + (-gavg_cent + gp) / slope2
 | 
			
		||||
 | 
			
		||||
        gprofile = g1st - voa + dgt * dgts3
 | 
			
		||||
    else:
 | 
			
		||||
        gprofile = None
 | 
			
		||||
 | 
			
		||||
    return gprofile
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
 | 
			
		||||
    plt.close('all')
 | 
			
		||||
    fc = itufs(0.05)
 | 
			
		||||
    lc = freq2wavelength(fc) / 1000
 | 
			
		||||
    nchan = list(range(len(lc)))
 | 
			
		||||
    df = np.array([0.05] * (nchan[-1] + 1))
 | 
			
		||||
    # TODO remove path dependence
 | 
			
		||||
    path = ''
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    DFG_96:  Design flat gain at each wavelength in the 96 channel 50GHz ITU
 | 
			
		||||
    grid in dB.  This can be experimentally determined by measuring the gain
 | 
			
		||||
    at each wavelength using a full, flat channel (or ASE) load at the input.
 | 
			
		||||
    The amplifier should be set to its maximum flat gain (tilt = 0dB).  This
 | 
			
		||||
    measurement captures the ripple of the amplifier.  If the amplifier was
 | 
			
		||||
    designed to be mimimum ripple at some other tilt value, then the ripple
 | 
			
		||||
    reflected in this measurement will not be that minimum.  However, when
 | 
			
		||||
    the DGT gets applied through the provisioning of tilt, the model should
 | 
			
		||||
    accurately reproduce the expected ripple at that tilt value.  One could
 | 
			
		||||
    also do the measurement at some expected tilt value and back-calculate
 | 
			
		||||
    this vector using the DGT method.  Alternatively, one could re-write the
 | 
			
		||||
    algorithm to accept a nominal tilt and a tiled version of this vector.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    dfg_96 = np.loadtxt(path + 'DFG_96.txt')
 | 
			
		||||
 | 
			
		||||
    """maximum gain for flat operation - the amp in the data file was designed
 | 
			
		||||
    for 25dB gain and has an internal VOA for setting the external gain
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    avg_dfg = dfg_96.mean()
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    DGT_96:  This is the so-called Dynamic Gain Tilt of the EDFA in dB/dB. It
 | 
			
		||||
    is the change in gain at each wavelength corresponding to a 1dB change at
 | 
			
		||||
    the longest wavelength supported.  The value can be obtained
 | 
			
		||||
    experimentally or through analysis of the cross sections or Giles
 | 
			
		||||
    parameters of the Er fibre.  This is experimentally measured by changing
 | 
			
		||||
    the gain of the amplifier above the maximum flat gain while not changing
 | 
			
		||||
    the internal VOA (i.e. the mid-stage VOA is set to minimum and does not
 | 
			
		||||
    change during the measurement). Note that the measurement can change the
 | 
			
		||||
    gain by an arbitrary amount and divide by the gain change (in dB) which
 | 
			
		||||
    is measured at the reference wavelength (the red end of the band).
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    dgt_96 = np.loadtxt(path + 'DGT_96.txt')
 | 
			
		||||
 | 
			
		||||
    """
 | 
			
		||||
    pNFfit3:  Cubic polynomial fit coefficients to noise figure in dB
 | 
			
		||||
    averaged across wavelength as a function of gain change from design flat:
 | 
			
		||||
 | 
			
		||||
        NFavg = pNFfit3(1)*dG^3 + pNFfit3(2)*dG^2 pNFfit3(3)*dG + pNFfit3(4)
 | 
			
		||||
    where
 | 
			
		||||
        dG = GainTarget - average(DFG_96)
 | 
			
		||||
    note that dG will normally be a negative value.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    nf_fitco = np.loadtxt(path + 'pNFfit3.txt')
 | 
			
		||||
 | 
			
		||||
    """NFR_96:  Noise figure ripple in dB away from the average noise figure
 | 
			
		||||
    across the band.  This captures the wavelength dependence of the NF.  To
 | 
			
		||||
    calculate the NF across channels, one uses the cubic fit coefficients
 | 
			
		||||
    with the external gain target to get the average nosie figure, NFavg and
 | 
			
		||||
    then adds this to NFR_96:
 | 
			
		||||
    NF_96 = NFR_96 + NFavg
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    nf_ripple = np.loadtxt(path + 'NFR_96.txt')
 | 
			
		||||
 | 
			
		||||
    # This is an example to set the provisionable gain and gain-tilt values
 | 
			
		||||
    # Tilt is in units of dB/THz
 | 
			
		||||
    gain_target = 20.0
 | 
			
		||||
    tilt_target = -0.7
 | 
			
		||||
 | 
			
		||||
    # calculate the NF for the EDFA at this gain setting
 | 
			
		||||
    dg = gain_target - avg_dfg
 | 
			
		||||
    nf_avg = polyval(nf_fitco, dg)
 | 
			
		||||
    nf_96 = nf_ripple + nf_avg
 | 
			
		||||
 | 
			
		||||
    # get the input power profiles to show
 | 
			
		||||
    pch2d = np.loadtxt(path + 'Pchan2D.txt')
 | 
			
		||||
 | 
			
		||||
    # Load legend and assemble legend text
 | 
			
		||||
    pch2d_legend_data = np.loadtxt(path + 'Pchan2DLegend.txt')
 | 
			
		||||
    pch2d_legend = []
 | 
			
		||||
    for ea in pch2d_legend_data:
 | 
			
		||||
        s = ''.join([chr(xx) for xx in ea.astype(dtype=int)]).strip()
 | 
			
		||||
        pch2d_legend.append(s)
 | 
			
		||||
 | 
			
		||||
    # assemble plot
 | 
			
		||||
    axis_font = {'fontname': 'Arial', 'size': '16', 'fontweight': 'bold'}
 | 
			
		||||
    title_font = {'fontname': 'Arial', 'size': '17', 'fontweight': 'bold'}
 | 
			
		||||
    tic_font = {'fontname': 'Arial', 'size': '12'}
 | 
			
		||||
 | 
			
		||||
    plt.rcParams["font.family"] = "Arial"
 | 
			
		||||
    plt.figure()
 | 
			
		||||
    plt.plot(nchan, pch2d.T, '.-', lw=2)
 | 
			
		||||
    plt.xlabel('Channel Number', **axis_font)
 | 
			
		||||
    plt.ylabel('Channel Power [dBm]', **axis_font)
 | 
			
		||||
    plt.title('Input Power Profiles for Different Channel Loading',
 | 
			
		||||
              **title_font)
 | 
			
		||||
    plt.legend(pch2d_legend, loc=5)
 | 
			
		||||
    plt.grid()
 | 
			
		||||
    plt.ylim((-100, -10))
 | 
			
		||||
    plt.xlim((0, 110))
 | 
			
		||||
    plt.xticks(np.arange(0, 100, 10), **tic_font)
 | 
			
		||||
    plt.yticks(np.arange(-110, -10, 10), **tic_font)
 | 
			
		||||
 | 
			
		||||
    plt.figure()
 | 
			
		||||
    ea = pch2d[1, :]
 | 
			
		||||
    for ea in pch2d:
 | 
			
		||||
        chgain = gain_profile(dfg_96, dgt_96, ea, gain_target, tilt_target)
 | 
			
		||||
        pase = noise_profile(nf_96, chgain, fc, df)
 | 
			
		||||
        pout = lin2db(db2lin(ea + chgain) + db2lin(pase))
 | 
			
		||||
        plt.plot(nchan, pout, '.-', lw=2)
 | 
			
		||||
    plt.title('Output Power with ASE for Different Channel Loading',
 | 
			
		||||
              **title_font)
 | 
			
		||||
    plt.xlabel('Channel Number', **axis_font)
 | 
			
		||||
    plt.ylabel('Channel Power [dBm]', **axis_font)
 | 
			
		||||
    plt.grid()
 | 
			
		||||
    plt.ylim((-50, 10))
 | 
			
		||||
    plt.xlim((0, 100))
 | 
			
		||||
    plt.xticks(np.arange(0, 100, 10), **tic_font)
 | 
			
		||||
    plt.yticks(np.arange(-50, 10, 10), **tic_font)
 | 
			
		||||
    plt.legend(pch2d_legend, loc=5)
 | 
			
		||||
    plt.show()
 | 
			
		||||
							
								
								
									
										300
									
								
								examples/edfa_model/amplifier_models_description.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								examples/edfa_model/amplifier_models_description.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,300 @@
 | 
			
		||||
*********************************************
 | 
			
		||||
Amplifier models and configuration
 | 
			
		||||
*********************************************
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
1. Equipment configuration description
 | 
			
		||||
#######################################
 | 
			
		||||
 | 
			
		||||
Equipment description defines equipment types and parameters.
 | 
			
		||||
It takes place in the default **eqpt_config.json** file. 
 | 
			
		||||
By default **transmission_main_example.py** uses **eqpt_config.json** file and that
 | 
			
		||||
can be changed with **-e** or **--equipment** command line parameter.
 | 
			
		||||
 | 
			
		||||
2. Amplifier parameters and subtypes
 | 
			
		||||
#######################################
 | 
			
		||||
 | 
			
		||||
Several amplifiers can be used by GNpy, so they are defined as an array of equipment parameters in **eqpt_config.json** file.
 | 
			
		||||
 | 
			
		||||
- *"type_variety"*:
 | 
			
		||||
    Each amplifier is identified by its unique *"type_variety"*, which is used in the topology files input to reference a specific amplifier. It is a user free defined id.
 | 
			
		||||
    
 | 
			
		||||
    For each amplifier *type_variety*, specific parameters are describing its attributes and performance:
 | 
			
		||||
 | 
			
		||||
- *"type_def"*:
 | 
			
		||||
    Sets the amplifier model that the simulation will use to calculate the ase noise contribution. 5 models are defined with reserved words:
 | 
			
		||||
 | 
			
		||||
    - *"advanced_model"*
 | 
			
		||||
    - *"variable_gain"*
 | 
			
		||||
    - *"fixed_gain"*
 | 
			
		||||
    - *"dual_stage"*
 | 
			
		||||
    - *"openroadm"*
 | 
			
		||||
        *see next section for a full description of these models*
 | 
			
		||||
 | 
			
		||||
- *"advanced_config_from_json"*:
 | 
			
		||||
    **This parameter is only applicable to the _"advanced_model"_ model**
 | 
			
		||||
    
 | 
			
		||||
    json file name describing:
 | 
			
		||||
 | 
			
		||||
    - nf_fit_coeff
 | 
			
		||||
    - f_min/max
 | 
			
		||||
    - gain_ripple
 | 
			
		||||
    - nf_ripple 
 | 
			
		||||
    - dgt
 | 
			
		||||
    
 | 
			
		||||
    *see next section for a full description*
 | 
			
		||||
 | 
			
		||||
- *"gain_flatmax"*: 
 | 
			
		||||
    amplifier maximum gain in dB before its extended gain range: flat or nominal tilt output. 
 | 
			
		||||
    
 | 
			
		||||
    If gain > gain_flatmax, the amplifier will tilt, based on its dgt function
 | 
			
		||||
 | 
			
		||||
    If gain > gain_flatmax + target_extended_gain, the amplifier output power is reduced to  not exceed the extended gain range.
 | 
			
		||||
 | 
			
		||||
- *"gain_min"*: 
 | 
			
		||||
    amplifier minimum gain in dB.
 | 
			
		||||
 | 
			
		||||
    If gain < gain_min, the amplifier input is automatically padded, which results in
 | 
			
		||||
 | 
			
		||||
    NF += gain_min - gain 
 | 
			
		||||
 | 
			
		||||
- *"p_max"*: 
 | 
			
		||||
    amplifier max output power, full load
 | 
			
		||||
 | 
			
		||||
    Total signal output power will not be allowed beyond this value
 | 
			
		||||
 | 
			
		||||
- *"nf_min/max"*:
 | 
			
		||||
    **These parameters are only applicable to the _"variable_gain"_ model**
 | 
			
		||||
 | 
			
		||||
    min & max NF values in dB
 | 
			
		||||
 | 
			
		||||
    NF_min is the amplifier NF @ gain_max  
 | 
			
		||||
 | 
			
		||||
    NF_max is the amplifier NF @ gain_min  
 | 
			
		||||
 | 
			
		||||
- *"nf_coef"*: 
 | 
			
		||||
    **This parameter is only applicable to the *"openroadm"* model**
 | 
			
		||||
 | 
			
		||||
    [a, b, c, d] 3rd order polynomial coefficients list to define the incremental OSNR vs Pin
 | 
			
		||||
    
 | 
			
		||||
    Incremental OSNR is the amplifier OSNR contribution
 | 
			
		||||
    
 | 
			
		||||
    Pin is the amplifier channel input power defined in a 50GHz bandwidth
 | 
			
		||||
    
 | 
			
		||||
    Incremental OSNR = a*Pin³ + b*Pin² + c*Pin + d
 | 
			
		||||
 | 
			
		||||
- *"preamp_variety"*: 
 | 
			
		||||
    **This parameter is only applicable to the _"dual_stage"_ model**
 | 
			
		||||
 | 
			
		||||
    1st stage type_variety
 | 
			
		||||
 | 
			
		||||
- *"booster_variety"*: 
 | 
			
		||||
    **This parameter is only applicable to the *"dual_stage"* model**
 | 
			
		||||
 | 
			
		||||
    2nd stage type_variety
 | 
			
		||||
 | 
			
		||||
- *"out_voa_auto"*: true/false
 | 
			
		||||
    **power_mode only**
 | 
			
		||||
 | 
			
		||||
    **This parameter is only applicable to the *"advanced_model"* and *"variable_gain"* models**
 | 
			
		||||
 | 
			
		||||
    If "out_voa_auto": true, auto_design will chose the output_VOA value that maximizes the amplifier gain within its power capability and therefore minimizes its NF.
 | 
			
		||||
 | 
			
		||||
- *"allowed_for_design"*: true/false
 | 
			
		||||
    **auto_design only**
 | 
			
		||||
 | 
			
		||||
    Tells auto_design if this amplifier can be picked for the design (deactivates unwanted amplifiers)
 | 
			
		||||
 | 
			
		||||
    It does not prevent the use of an amplifier if it is placed in the topology input.
 | 
			
		||||
 | 
			
		||||
    .. code-block:: json
 | 
			
		||||
 | 
			
		||||
        {"Edfa": [{
 | 
			
		||||
                    "type_variety": "std_medium_gain",
 | 
			
		||||
                    "type_def": "variable_gain",
 | 
			
		||||
                    "gain_flatmax": 26,
 | 
			
		||||
                    "gain_min": 15,
 | 
			
		||||
                    "p_max": 23,
 | 
			
		||||
                    "nf_min": 6,
 | 
			
		||||
                    "nf_max": 10,
 | 
			
		||||
                    "out_voa_auto": false,
 | 
			
		||||
                    "allowed_for_design": true
 | 
			
		||||
                    },
 | 
			
		||||
                    {
 | 
			
		||||
                    "type_variety": "std_low_gain",
 | 
			
		||||
                    "type_def": "variable_gain",
 | 
			
		||||
                    "gain_flatmax": 16,
 | 
			
		||||
                    "gain_min": 8,
 | 
			
		||||
                    "p_max": 23,
 | 
			
		||||
                    "nf_min": 6.5,
 | 
			
		||||
                    "nf_max": 11,
 | 
			
		||||
                    "out_voa_auto": false,
 | 
			
		||||
                    "allowed_for_design": true
 | 
			
		||||
                    }
 | 
			
		||||
            ]}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
3. Amplifier models
 | 
			
		||||
#######################################
 | 
			
		||||
 | 
			
		||||
In an opensource and multi-vendor environnement, it is needed to support different use cases and context. Therefore several models are supported for amplifiers.
 | 
			
		||||
 | 
			
		||||
5 types of EDFA definition are possible and referenced by the *"type_def"* parameter with the following reserved words:
 | 
			
		||||
 | 
			
		||||
-  *"advanced_model"* 
 | 
			
		||||
    This model is refered as a whitebox model because of the detailed level of knowledge that is required. The amplifier NF model and ripple definition are described by a json file referenced with *"advanced_config_from_json"*: json filename. This json file contains:
 | 
			
		||||
 | 
			
		||||
    - nf_fit_coeff: [a,b,c,d]
 | 
			
		||||
         
 | 
			
		||||
        3rd order polynomial NF = f(-dg) coeficients list
 | 
			
		||||
 | 
			
		||||
        dg = gain - gain_max
 | 
			
		||||
 | 
			
		||||
    - f_min/max: amplifier frequency range in Hz
 | 
			
		||||
    - gain_ripple : [...]
 | 
			
		||||
 | 
			
		||||
        amplifier gain ripple excursion comb list in dB across the frequency range.
 | 
			
		||||
    - nf_ripple : [...]
 | 
			
		||||
        
 | 
			
		||||
        amplifier nf ripple excursion comb list in dB across the frequency range. 
 | 
			
		||||
    - dgt : [...]
 | 
			
		||||
        amplifier dynamic gain tilt comb list across the frequency range.
 | 
			
		||||
            
 | 
			
		||||
        *See next section for the generation of this json file*
 | 
			
		||||
 | 
			
		||||
    .. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
        "Edfa":[{
 | 
			
		||||
                "type_variety": "high_detail_model_example",
 | 
			
		||||
                "type_def": "advanced_model",
 | 
			
		||||
                "gain_flatmax": 25,
 | 
			
		||||
                "gain_min": 15,
 | 
			
		||||
                "p_max": 21,
 | 
			
		||||
                "advanced_config_from_json": "std_medium_gain_advanced_config.json",
 | 
			
		||||
                "out_voa_auto": false,
 | 
			
		||||
                "allowed_for_design": false
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
- *"variable_gain"* 
 | 
			
		||||
    This model is refered as an operator model because a lower level of knowledge is required. A full polynomial description of the NF cross the gain range is not required. Instead, NF_min and NF_max values are required and used by the code to model a dual stage amplifier with an internal mid stage VOA. NF_min and NF_max values are typically available from equipment suppliers data-sheet.
 | 
			
		||||
 | 
			
		||||
    There is a default JSON file ”default_edfa_config.json”* to enforce 0 tilt and ripple values because GNpy core algorithm is a multi-carrier propogation.
 | 
			
		||||
    - gain_ripple =[0,...,0]
 | 
			
		||||
    - nf_ripple = [0,...,0]
 | 
			
		||||
    - dgt = [...] generic dgt comb
 | 
			
		||||
 | 
			
		||||
    .. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
        "Edfa":[{
 | 
			
		||||
                "type_variety": "std_medium_gain",
 | 
			
		||||
                "type_def": "variable_gain",
 | 
			
		||||
                "gain_flatmax": 26,
 | 
			
		||||
                "gain_min": 15,
 | 
			
		||||
                "p_max": 23,
 | 
			
		||||
                "nf_min": 6,
 | 
			
		||||
                "nf_max": 10,
 | 
			
		||||
                "out_voa_auto": false,
 | 
			
		||||
                "allowed_for_design": true
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
-  *"fixed_gain"* 
 | 
			
		||||
    This model is also an operator model with a single NF value that emulates basic single coil amplifiers without internal VOA.
 | 
			
		||||
 | 
			
		||||
    if gain_min < gain < gain_max, NF == nf0
 | 
			
		||||
    
 | 
			
		||||
    if gain < gain_min, the amplifier input is automatically padded, which results in 
 | 
			
		||||
 | 
			
		||||
    NF += gain_min - gain
 | 
			
		||||
 | 
			
		||||
    .. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
        "Edfa":[{
 | 
			
		||||
                "type_variety": "std_fixed_gain",
 | 
			
		||||
                "type_def": "fixed_gain",
 | 
			
		||||
                "gain_flatmax": 21,
 | 
			
		||||
                "gain_min": 20,
 | 
			
		||||
                "p_max": 21,
 | 
			
		||||
                "nf0": 5.5,
 | 
			
		||||
                "allowed_for_design": false
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
- *"openroadm"* 
 | 
			
		||||
    This model is a black box model replicating OpenRoadm MSA spec for ILA.
 | 
			
		||||
 | 
			
		||||
    .. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
        "Edfa":[{
 | 
			
		||||
                "type_variety": "low_noise",
 | 
			
		||||
                "type_def": "openroadm",
 | 
			
		||||
                "gain_flatmax": 27,
 | 
			
		||||
                "gain_min": 12,
 | 
			
		||||
                "p_max": 22,
 | 
			
		||||
                "nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62],
 | 
			
		||||
                "allowed_for_design": false
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
 | 
			
		||||
- *"dual_stage"* 
 | 
			
		||||
    This model allows the cascade (pre-defined combination) of any 2 amplifiers already described in the eqpt_config.json library.
 | 
			
		||||
    
 | 
			
		||||
    - preamp_variety defines the 1st stge type variety
 | 
			
		||||
    
 | 
			
		||||
    - booster variety defines the 2nd stage type variety
 | 
			
		||||
    
 | 
			
		||||
    Both preamp and booster variety must exist in the eqpt libray
 | 
			
		||||
    The resulting NF is the sum of the 2 amplifiers 
 | 
			
		||||
    The preamp is operated to its maximum gain
 | 
			
		||||
    
 | 
			
		||||
    - gain_min indicates to auto_design when this dual_stage should be used
 | 
			
		||||
    
 | 
			
		||||
    But unlike other models the 1st stage input will not be padded: it is always operated to its maximu gain and min NF. Therefore if gain adaptation and padding is needed it will be performed by the 2nd stage.
 | 
			
		||||
 | 
			
		||||
    .. code-block:: json
 | 
			
		||||
 | 
			
		||||
                {
 | 
			
		||||
                "type_variety": "medium+low_gain",
 | 
			
		||||
                "type_def": "dual_stage",
 | 
			
		||||
                "gain_min": 25,
 | 
			
		||||
                "preamp_variety": "std_medium_gain",
 | 
			
		||||
                "booster_variety": "std_low_gain",
 | 
			
		||||
                "allowed_for_design": true
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
4. advanced_config_from_json 
 | 
			
		||||
#######################################
 | 
			
		||||
 | 
			
		||||
The build_oa_json.py library in gnpy/examples/edfa_model can be used to build the json file required for the amplifier advanced_model type_def:
 | 
			
		||||
 | 
			
		||||
Update an existing json file with all the 96ch txt files for a given amplifier type
 | 
			
		||||
amplifier type 'OA_type1' is hard coded but can be modified and other types added
 | 
			
		||||
returns an updated amplifier json file: output_json_file_name = 'edfa_config.json'
 | 
			
		||||
amplifier file names
 | 
			
		||||
 | 
			
		||||
Convert a set of amplifier files + input json definiton file into a valid edfa_json_file:
 | 
			
		||||
 | 
			
		||||
nf_fit_coeff: NF 3rd order polynomial coefficients txt file
 | 
			
		||||
 | 
			
		||||
nf = f(dg) with dg = gain_operational - gain_max
 | 
			
		||||
 | 
			
		||||
nf_ripple: NF ripple excursion txt file
 | 
			
		||||
 | 
			
		||||
gain_ripple: gain ripple txt file
 | 
			
		||||
 | 
			
		||||
dgt: dynamic gain txt file
 | 
			
		||||
 | 
			
		||||
input json file in argument (defult = 'OA.json')
 | 
			
		||||
 | 
			
		||||
the json input file should have the following fields:
 | 
			
		||||
 | 
			
		||||
    .. code-block:: json
 | 
			
		||||
 | 
			
		||||
        {
 | 
			
		||||
            "nf_fit_coeff": "nf_filename.txt",
 | 
			
		||||
            "nf_ripple": "nf_ripple_filename.txt", 
 | 
			
		||||
            "gain_ripple": "DFG_filename.txt",
 | 
			
		||||
            "dgt": "DGT_filename.txt"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -4,7 +4,6 @@
 | 
			
		||||
Created on Tue Jan 30 12:32:00 2018
 | 
			
		||||
 | 
			
		||||
@author: jeanluc-auge
 | 
			
		||||
@comments about amplifier input files from Brian Taylor & Dave Boertjes
 | 
			
		||||
 | 
			
		||||
update an existing json file with all the 96ch txt files for a given amplifier type
 | 
			
		||||
amplifier type 'OA_type1' is hard coded but can be modified and other types added
 | 
			
		||||
@@ -18,46 +17,29 @@ from gnpy.core.utils import lin2db, db2lin
 | 
			
		||||
 | 
			
		||||
"""amplifier file names
 | 
			
		||||
convert a set of amplifier files + input json definiton file into a valid edfa_json_file:
 | 
			
		||||
nf_fit_coeff: NF polynomial coefficients txt file (optional)
 | 
			
		||||
nf_fit_coeff: NF 3rd order polynomial coefficients txt file
 | 
			
		||||
    nf = f(dg)
 | 
			
		||||
    with dg = gain_operational - gain_max
 | 
			
		||||
nf_ripple: NF ripple excursion txt file
 | 
			
		||||
dfg: gain txt file
 | 
			
		||||
gain_ripple: gain ripple txt file
 | 
			
		||||
dgt: dynamic gain txt file
 | 
			
		||||
input json file in argument (defult = 'OA.json')
 | 
			
		||||
 | 
			
		||||
the json input file should have the following fields:
 | 
			
		||||
{
 | 
			
		||||
    "gain_flatmax": 25,
 | 
			
		||||
    "gain_min": 15,
 | 
			
		||||
    "p_max": 21,
 | 
			
		||||
    "nf_fit_coeff": "pNFfit3.txt",
 | 
			
		||||
    "nf_ripple": "NFR_96.txt", 
 | 
			
		||||
    "dfg": "DFG_96.txt",
 | 
			
		||||
    "dgt": "DGT_96.txt",
 | 
			
		||||
    "nf_model": 
 | 
			
		||||
        {
 | 
			
		||||
        "enabled": true,
 | 
			
		||||
        "nf_min": 5.8,
 | 
			
		||||
        "nf_max": 10
 | 
			
		||||
        }
 | 
			
		||||
    "nf_fit_coeff": "nf_filename.txt",
 | 
			
		||||
    "nf_ripple": "nf_ripple_filename.txt", 
 | 
			
		||||
    "gain_ripple": "DFG_filename.txt",
 | 
			
		||||
    "dgt": "DGT_filename.txt",
 | 
			
		||||
}
 | 
			
		||||
gain_flat = max flat gain (dB)
 | 
			
		||||
gain_min = min gain (dB) : will consider an input VOA if below (TBD vs throwing an exception)
 | 
			
		||||
p_max = max power (dBm)
 | 
			
		||||
nf_fit = boolean (True, False) : 
 | 
			
		||||
        if False nf_fit_coeff are ignored and nf_model fields are used
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
input_json_file_name = "OA.json" #default path
 | 
			
		||||
output_json_file_name = "default_edfa_config.json"
 | 
			
		||||
param_field  ="params"
 | 
			
		||||
gain_min_field = "gain_min"
 | 
			
		||||
gain_max_field = "gain_flatmax"
 | 
			
		||||
gain_ripple_field = "dfg"
 | 
			
		||||
gain_ripple_field = "gain_ripple"
 | 
			
		||||
nf_ripple_field = "nf_ripple"
 | 
			
		||||
nf_fit_coeff = "nf_fit_coeff"
 | 
			
		||||
nf_model_field = "nf_model"
 | 
			
		||||
nf_model_enabled_field = "enabled"
 | 
			
		||||
nf_min_field  ="nf_min"
 | 
			
		||||
nf_max_field = "nf_max"
 | 
			
		||||
 | 
			
		||||
def read_file(field, file_name):
 | 
			
		||||
    """read and format the 96 channels txt files describing the amplifier NF and ripple
 | 
			
		||||
@@ -76,6 +58,7 @@ def read_file(field, file_name):
 | 
			
		||||
        #consider ripple excursion only to avoid redundant information
 | 
			
		||||
        #because the max flat_gain is already given by the 'gain_flat' field in json
 | 
			
		||||
        #remove the mean component
 | 
			
		||||
        print(file_name, ', mean value =', data.mean(), ' is substracted')
 | 
			
		||||
        data = data - data.mean()
 | 
			
		||||
    data = data.tolist()
 | 
			
		||||
    return data
 | 
			
		||||
 
 | 
			
		||||
@@ -1,313 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
    "params": {
 | 
			
		||||
        "gain_flatmax": 25,
 | 
			
		||||
        "gain_min": 15,
 | 
			
		||||
        "p_max": 21,
 | 
			
		||||
        "nf_fit_coeff": [
 | 
			
		||||
            0.000168241,
 | 
			
		||||
            0.0469961,
 | 
			
		||||
            0.0359549,
 | 
			
		||||
            5.82851
 | 
			
		||||
        ],
 | 
			
		||||
        "nf_ripple": [
 | 
			
		||||
            -0.3110761646066259,
 | 
			
		||||
            -0.3110761646066259,
 | 
			
		||||
            -0.31110274831665313,
 | 
			
		||||
            -0.31419329378173544,
 | 
			
		||||
            -0.3172854168606314,
 | 
			
		||||
            -0.32037911876162584,
 | 
			
		||||
            -0.3233255190215882,
 | 
			
		||||
            -0.31624321721895354,
 | 
			
		||||
            -0.30915729645781326,
 | 
			
		||||
            -0.30206775396360075,
 | 
			
		||||
            -0.2949045115165272,
 | 
			
		||||
            -0.26632156113294336,
 | 
			
		||||
            -0.23772399031437283,
 | 
			
		||||
            -0.20911178784023846,
 | 
			
		||||
            -0.18048410390821285,
 | 
			
		||||
            -0.14379944379052215,
 | 
			
		||||
            -0.10709599992470213,
 | 
			
		||||
            -0.07037375788020579,
 | 
			
		||||
            -0.03372858157230583,
 | 
			
		||||
            -0.015660302006048,
 | 
			
		||||
            0.0024172385953583004,
 | 
			
		||||
            0.020504047353947653,
 | 
			
		||||
            0.03860013139908377,
 | 
			
		||||
            0.05670549786742816,
 | 
			
		||||
            0.07482015390297145,
 | 
			
		||||
            0.0838762040768461,
 | 
			
		||||
            0.09284481475528361,
 | 
			
		||||
            0.1018180306253394,
 | 
			
		||||
            0.11079585523492333,
 | 
			
		||||
            0.1020395478432815,
 | 
			
		||||
            0.09310160456603413,
 | 
			
		||||
            0.08415906712621996,
 | 
			
		||||
            0.07521193198077789,
 | 
			
		||||
            0.0676340601339394,
 | 
			
		||||
            0.06005437964543287,
 | 
			
		||||
            0.052470799141237305,
 | 
			
		||||
            0.044883315610536455,
 | 
			
		||||
            0.037679759069084225,
 | 
			
		||||
            0.03047647598902483,
 | 
			
		||||
            0.02326948274513522,
 | 
			
		||||
            0.01605877647020772,
 | 
			
		||||
            0.021248462316134083,
 | 
			
		||||
            0.02657315875107553,
 | 
			
		||||
            0.03190060058247842,
 | 
			
		||||
            0.03723078993416436,
 | 
			
		||||
            0.04256372893215024,
 | 
			
		||||
            0.047899419704645264,
 | 
			
		||||
            0.03915515813685565,
 | 
			
		||||
            0.030289222542492025,
 | 
			
		||||
            0.021418708618354456,
 | 
			
		||||
            0.012573926129294415,
 | 
			
		||||
            0.006240488799898697,
 | 
			
		||||
            -9.622162373026585e-05,
 | 
			
		||||
            -0.006436207679519103,
 | 
			
		||||
            -0.012779471908040341,
 | 
			
		||||
            -0.02038153550619876,
 | 
			
		||||
            -0.027999803010447587,
 | 
			
		||||
            -0.035622012697103154,
 | 
			
		||||
            -0.043236398934156144,
 | 
			
		||||
            -0.04493583574805963,
 | 
			
		||||
            -0.04663615264317309,
 | 
			
		||||
            -0.048337350303318156,
 | 
			
		||||
            -0.050039429413028365,
 | 
			
		||||
            -0.051742390657545205,
 | 
			
		||||
            -0.05342028484370278,
 | 
			
		||||
            -0.05254242298580185,
 | 
			
		||||
            -0.05166410580536087,
 | 
			
		||||
            -0.05078533294804249,
 | 
			
		||||
            -0.04990610405914272,
 | 
			
		||||
            -0.05409792133358102,
 | 
			
		||||
            -0.05832916277634124,
 | 
			
		||||
            -0.06256260169582961,
 | 
			
		||||
            -0.06660356886269536,
 | 
			
		||||
            -0.04779792991567815,
 | 
			
		||||
            -0.028982516728038848,
 | 
			
		||||
            -0.010157321677553965,
 | 
			
		||||
            0.00861320615127981,
 | 
			
		||||
            0.01913736978785662,
 | 
			
		||||
            0.029667009055877668,
 | 
			
		||||
            0.04020212822983975,
 | 
			
		||||
            0.050742731588695494,
 | 
			
		||||
            0.061288823415841555,
 | 
			
		||||
            0.07184040799914815,
 | 
			
		||||
            0.1043252636301016,
 | 
			
		||||
            0.13687829834471027,
 | 
			
		||||
            0.1694483010211072,
 | 
			
		||||
            0.202035284929368,
 | 
			
		||||
            0.23624619427167134,
 | 
			
		||||
            0.27048596623174515,
 | 
			
		||||
            0.30474360397422756,
 | 
			
		||||
            0.3390191214858807,
 | 
			
		||||
            0.36358851509924695,
 | 
			
		||||
            0.38814205928193013,
 | 
			
		||||
            0.41270842850729195,
 | 
			
		||||
            0.4372876328262819,
 | 
			
		||||
            0.4372876328262819
 | 
			
		||||
        ],
 | 
			
		||||
        "dgt": [
 | 
			
		||||
            2.714526681131686,
 | 
			
		||||
            2.705443819238505,
 | 
			
		||||
            2.6947834587664494,
 | 
			
		||||
            2.6841217449620203,
 | 
			
		||||
            2.6681935771243177,
 | 
			
		||||
            2.6521732021128046,
 | 
			
		||||
            2.630396440815385,
 | 
			
		||||
            2.602860350286428,
 | 
			
		||||
            2.5696460593920065,
 | 
			
		||||
            2.5364027376452056,
 | 
			
		||||
            2.499446286796604,
 | 
			
		||||
            2.4587748041127506,
 | 
			
		||||
            2.414398437185221,
 | 
			
		||||
            2.3699990328716107,
 | 
			
		||||
            2.322373696229342,
 | 
			
		||||
            2.271520771371253,
 | 
			
		||||
            2.2174389328192197,
 | 
			
		||||
            2.16337565384239,
 | 
			
		||||
            2.1183028432496016,
 | 
			
		||||
            2.082225099873648,
 | 
			
		||||
            2.055100772005235,
 | 
			
		||||
            2.0279625371819305,
 | 
			
		||||
            2.0008103857988204,
 | 
			
		||||
            1.9736443063300082,
 | 
			
		||||
            1.9482128147680253,
 | 
			
		||||
            1.9245345552113182,
 | 
			
		||||
            1.9026104247588487,
 | 
			
		||||
            1.8806927939516411,
 | 
			
		||||
            1.862235672444246,
 | 
			
		||||
            1.847275503201129,
 | 
			
		||||
            1.835814081380705,
 | 
			
		||||
            1.824381436842932,
 | 
			
		||||
            1.8139629377087627,
 | 
			
		||||
            1.8045606557581335,
 | 
			
		||||
            1.7961751115773796,
 | 
			
		||||
            1.7877868031023945,
 | 
			
		||||
            1.7793941781790852,
 | 
			
		||||
            1.7709972329654864,
 | 
			
		||||
            1.7625959636196327,
 | 
			
		||||
            1.7541903672600494,
 | 
			
		||||
            1.7459181197626403,
 | 
			
		||||
            1.737780757913635,
 | 
			
		||||
            1.7297783508684146,
 | 
			
		||||
            1.7217732861435076,
 | 
			
		||||
            1.7137640932265894,
 | 
			
		||||
            1.7057507692361864,
 | 
			
		||||
            1.6918150918099673,
 | 
			
		||||
            1.6719047669939942,
 | 
			
		||||
            1.6460167077689267,
 | 
			
		||||
            1.6201194134191075,
 | 
			
		||||
            1.5986915141218316,
 | 
			
		||||
            1.5817353179379183,
 | 
			
		||||
            1.569199764184379,
 | 
			
		||||
            1.5566577309558969,
 | 
			
		||||
            1.545374152761467,
 | 
			
		||||
            1.5353620432989845,
 | 
			
		||||
            1.5266220576235803,
 | 
			
		||||
            1.5178910621476225,
 | 
			
		||||
            1.5097346239790443,
 | 
			
		||||
            1.502153039909686,
 | 
			
		||||
            1.495145456062699,
 | 
			
		||||
            1.488134243479226,
 | 
			
		||||
            1.48111939735681,
 | 
			
		||||
            1.474100442252211,
 | 
			
		||||
            1.4670307626366115,
 | 
			
		||||
            1.4599103316162523,
 | 
			
		||||
            1.45273959485914,
 | 
			
		||||
            1.445565137158368,
 | 
			
		||||
            1.4340878115214444,
 | 
			
		||||
            1.418273806730323,
 | 
			
		||||
            1.3981208704326855,
 | 
			
		||||
            1.3779439775587023,
 | 
			
		||||
            1.3598972673004606,
 | 
			
		||||
            1.3439818461440451,
 | 
			
		||||
            1.3301807335621048,
 | 
			
		||||
            1.316383926863083,
 | 
			
		||||
            1.3040618749785347,
 | 
			
		||||
            1.2932153453410835,
 | 
			
		||||
            1.2838336236692311,
 | 
			
		||||
            1.2744470198196236,
 | 
			
		||||
            1.2650555289898042,
 | 
			
		||||
            1.2556591482982988,
 | 
			
		||||
            1.2428104897182262,
 | 
			
		||||
            1.2264996957264114,
 | 
			
		||||
            1.2067249615595257,
 | 
			
		||||
            1.1869318618366975,
 | 
			
		||||
            1.1672278304018044,
 | 
			
		||||
            1.1476135933863398,
 | 
			
		||||
            1.1280891949729075,
 | 
			
		||||
            1.108555289615659,
 | 
			
		||||
            1.0895983485572227,
 | 
			
		||||
            1.0712204022764056,
 | 
			
		||||
            1.0534217504465226,
 | 
			
		||||
            1.0356155337864215,
 | 
			
		||||
            1.017807767853702,
 | 
			
		||||
            1.0
 | 
			
		||||
        ],
 | 
			
		||||
        "nf_model": {
 | 
			
		||||
            "enabled": true,
 | 
			
		||||
            "nf1": 5.727887800964238,
 | 
			
		||||
            "nf2": 7.727887800964238,
 | 
			
		||||
            "delta_p": 5.238350271545567
 | 
			
		||||
        },
 | 
			
		||||
        "gain_ripple": [
 | 
			
		||||
            0.1359703369791596,
 | 
			
		||||
            0.11822862697916037,
 | 
			
		||||
            0.09542181697916163,
 | 
			
		||||
            0.06245819697916133,
 | 
			
		||||
            0.02602813697916062,
 | 
			
		||||
            -0.0036199830208403228,
 | 
			
		||||
            -0.018326963020840026,
 | 
			
		||||
            -0.0246928330208398,
 | 
			
		||||
            -0.016792253020838643,
 | 
			
		||||
            -0.0028138630208403015,
 | 
			
		||||
            0.017572956979162058,
 | 
			
		||||
            0.038328296979159404,
 | 
			
		||||
            0.054956336979159914,
 | 
			
		||||
            0.0670723869791594,
 | 
			
		||||
            0.07091459697916136,
 | 
			
		||||
            0.07094413697916124,
 | 
			
		||||
            0.07114372697916238,
 | 
			
		||||
            0.07533675697916209,
 | 
			
		||||
            0.08731066697916035,
 | 
			
		||||
            0.10313984697916112,
 | 
			
		||||
            0.12276252697916235,
 | 
			
		||||
            0.14239527697916188,
 | 
			
		||||
            0.15945681697916214,
 | 
			
		||||
            0.1739275269791598,
 | 
			
		||||
            0.1767381569791624,
 | 
			
		||||
            0.17037189697916233,
 | 
			
		||||
            0.15216302697916007,
 | 
			
		||||
            0.13114358697916018,
 | 
			
		||||
            0.10802383697916085,
 | 
			
		||||
            0.08548825697916129,
 | 
			
		||||
            0.06916723697916183,
 | 
			
		||||
            0.05848224697916038,
 | 
			
		||||
            0.05447361697916264,
 | 
			
		||||
            0.05154489697916276,
 | 
			
		||||
            0.04946107697915991,
 | 
			
		||||
            0.04717897697916129,
 | 
			
		||||
            0.04551704697916037,
 | 
			
		||||
            0.04467697697916151,
 | 
			
		||||
            0.04072968697916224,
 | 
			
		||||
            0.03285456697916089,
 | 
			
		||||
            0.023488786979161347,
 | 
			
		||||
            0.01659282697915998,
 | 
			
		||||
            0.013321846979160057,
 | 
			
		||||
            0.011234826979162449,
 | 
			
		||||
            0.01030063697916006,
 | 
			
		||||
            0.00936596697916059,
 | 
			
		||||
            0.00874012697916271,
 | 
			
		||||
            0.00842583697916055,
 | 
			
		||||
            0.006965146979162284,
 | 
			
		||||
            0.0040435869791615175,
 | 
			
		||||
            0.0007104669791608842,
 | 
			
		||||
            -0.0015763130208377163,
 | 
			
		||||
            -0.006936193020838033,
 | 
			
		||||
            -0.016475303020840215,
 | 
			
		||||
            -0.028748483020837767,
 | 
			
		||||
            -0.039618433020837784,
 | 
			
		||||
            -0.051112303020840244,
 | 
			
		||||
            -0.06468462302083822,
 | 
			
		||||
            -0.07868024302083754,
 | 
			
		||||
            -0.09101254302083817,
 | 
			
		||||
            -0.10103437302083762,
 | 
			
		||||
            -0.11041488302083735,
 | 
			
		||||
            -0.11916081302083725,
 | 
			
		||||
            -0.12789859302083784,
 | 
			
		||||
            -0.1353792530208402,
 | 
			
		||||
            -0.14160178302083892,
 | 
			
		||||
            -0.1455411330208385,
 | 
			
		||||
            -0.1484450830208388,
 | 
			
		||||
            -0.14823350302084037,
 | 
			
		||||
            -0.14591937302083835,
 | 
			
		||||
            -0.1409032730208395,
 | 
			
		||||
            -0.13525493302083902,
 | 
			
		||||
            -0.1279646530208396,
 | 
			
		||||
            -0.11963431302083904,
 | 
			
		||||
            -0.11089282302084058,
 | 
			
		||||
            -0.1027863830208382,
 | 
			
		||||
            -0.09717347302083823,
 | 
			
		||||
            -0.09343261302083761,
 | 
			
		||||
            -0.0913487130208388,
 | 
			
		||||
            -0.08906007302083907,
 | 
			
		||||
            -0.0865687230208394,
 | 
			
		||||
            -0.08407607302083875,
 | 
			
		||||
            -0.07844600302084004,
 | 
			
		||||
            -0.06968090302083851,
 | 
			
		||||
            -0.05947139302083926,
 | 
			
		||||
            -0.05095282302083959,
 | 
			
		||||
            -0.042428283020839785,
 | 
			
		||||
            -0.03218106302083967,
 | 
			
		||||
            -0.01819858302084043,
 | 
			
		||||
            -0.0021726530208390216,
 | 
			
		||||
            0.01393231697916164,
 | 
			
		||||
            0.028098946979159933,
 | 
			
		||||
            0.040326236979161934,
 | 
			
		||||
            0.05257029697916238,
 | 
			
		||||
            0.06479749697916048,
 | 
			
		||||
            0.07704745697916238
 | 
			
		||||
        ]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +1,22 @@
 | 
			
		||||
{     "Edfa":[{
 | 
			
		||||
            "type_variety": "high_detail_model_example",
 | 
			
		||||
            "type_def": "advanced_model",
 | 
			
		||||
            "gain_flatmax": 25,
 | 
			
		||||
            "gain_min": 15,
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "advanced_config_from_json": "std_medium_gain_advanced_config.json",
 | 
			
		||||
            "out_voa_auto": false,
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            },
 | 
			
		||||
            },                  {
 | 
			
		||||
            "type_variety": "Juniper_BoosterHG",
 | 
			
		||||
            "type_def": "advanced_model",
 | 
			
		||||
            "gain_flatmax": 25,
 | 
			
		||||
            "gain_min": 10,
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "advanced_config_from_json": "Juniper-BoosterHG.json",
 | 
			
		||||
            "out_voa_auto": false,
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            }, 
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "operator_model_example",
 | 
			
		||||
            "type_def": "variable_gain",
 | 
			
		||||
@@ -37,6 +47,17 @@
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "std_high_gain",
 | 
			
		||||
            "type_def": "variable_gain",
 | 
			
		||||
            "gain_flatmax": 35,
 | 
			
		||||
            "gain_min": 25,
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf_min": 5.5,
 | 
			
		||||
            "nf_max": 7,
 | 
			
		||||
            "out_voa_auto": false,
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            },            
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "std_medium_gain",
 | 
			
		||||
            "type_def": "variable_gain",
 | 
			
		||||
            "gain_flatmax": 26,
 | 
			
		||||
@@ -59,6 +80,17 @@
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "high_power",
 | 
			
		||||
            "type_def": "variable_gain",
 | 
			
		||||
            "gain_flatmax": 16,
 | 
			
		||||
            "gain_min": 8,
 | 
			
		||||
            "p_max": 25,
 | 
			
		||||
            "nf_min": 9,
 | 
			
		||||
            "nf_max": 15,
 | 
			
		||||
            "out_voa_auto": false,
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            },            
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "std_fixed_gain",
 | 
			
		||||
            "type_def": "fixed_gain",
 | 
			
		||||
            "gain_flatmax": 21,
 | 
			
		||||
@@ -66,7 +98,50 @@
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf0": 5.5,
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            }       
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "4pumps_raman",
 | 
			
		||||
            "type_def": "fixed_gain",
 | 
			
		||||
            "gain_flatmax": 12,
 | 
			
		||||
            "gain_min": 12,
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf0": -1,
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            },   
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "hybrid_4pumps_lowgain",
 | 
			
		||||
            "type_def": "dual_stage",
 | 
			
		||||
            "raman": true,
 | 
			
		||||
            "gain_min": 25,
 | 
			
		||||
            "preamp_variety": "4pumps_raman",
 | 
			
		||||
            "booster_variety": "std_low_gain",
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "hybrid_4pumps_mediumgain",
 | 
			
		||||
            "type_def": "dual_stage",
 | 
			
		||||
            "raman": true,
 | 
			
		||||
            "gain_min": 25,
 | 
			
		||||
            "preamp_variety": "4pumps_raman",
 | 
			
		||||
            "booster_variety": "std_medium_gain",
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            },            
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "medium+low_gain",
 | 
			
		||||
            "type_def": "dual_stage",
 | 
			
		||||
            "gain_min": 25,
 | 
			
		||||
            "preamp_variety": "std_medium_gain",
 | 
			
		||||
            "booster_variety": "std_low_gain",
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "medium+high_power",
 | 
			
		||||
            "type_def": "dual_stage",
 | 
			
		||||
            "gain_min": 25,
 | 
			
		||||
            "preamp_variety": "std_medium_gain",
 | 
			
		||||
            "booster_variety": "high_power",
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            }                                  
 | 
			
		||||
      ],
 | 
			
		||||
      "Fiber":[{
 | 
			
		||||
            "type_variety": "SSMF",
 | 
			
		||||
@@ -84,9 +159,41 @@
 | 
			
		||||
            "gamma": 0.000843
 | 
			
		||||
            }
 | 
			
		||||
      ],
 | 
			
		||||
      "Spans":[{
 | 
			
		||||
      "RamanFiber":[{
 | 
			
		||||
            "type_variety": "SSMF",
 | 
			
		||||
            "dispersion": 1.67e-05,
 | 
			
		||||
            "gamma": 0.00127,
 | 
			
		||||
            "raman_efficiency": {
 | 
			
		||||
              "cr":[
 | 
			
		||||
                  0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119,
 | 
			
		||||
                  0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469,
 | 
			
		||||
                  0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826,
 | 
			
		||||
                  0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05,
 | 
			
		||||
                  7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05,
 | 
			
		||||
                  2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05,
 | 
			
		||||
                  2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05,
 | 
			
		||||
                  1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05,
 | 
			
		||||
                  1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07,
 | 
			
		||||
                  2E-07, 1E-07
 | 
			
		||||
              ],
 | 
			
		||||
              "frequency_offset":[
 | 
			
		||||
                0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12,
 | 
			
		||||
                7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12,
 | 
			
		||||
                13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12,
 | 
			
		||||
                17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12,
 | 
			
		||||
                22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12,
 | 
			
		||||
                28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12,
 | 
			
		||||
                35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12,
 | 
			
		||||
                41.5e12, 42e12
 | 
			
		||||
              ]
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
      ],
 | 
			
		||||
      "Span":[{
 | 
			
		||||
            "power_mode":true,
 | 
			
		||||
            "delta_power_range_db": [0,0,0.5],
 | 
			
		||||
            "delta_power_range_db": [-2,3,0.5],
 | 
			
		||||
            "max_fiber_lineic_loss_for_raman": 0.25,
 | 
			
		||||
            "target_extended_gain": 2.5,
 | 
			
		||||
            "max_length": 150,
 | 
			
		||||
            "length_units": "km",
 | 
			
		||||
            "max_loss": 28,
 | 
			
		||||
@@ -96,10 +203,13 @@
 | 
			
		||||
            "con_out": 0
 | 
			
		||||
            }
 | 
			
		||||
      ],
 | 
			
		||||
      "Roadms":[{
 | 
			
		||||
            "gain_mode_default_loss": 20,
 | 
			
		||||
            "power_mode_pout_target": -20,
 | 
			
		||||
            "add_drop_osnr": 38
 | 
			
		||||
      "Roadm":[{
 | 
			
		||||
            "target_pch_out_db": -20,
 | 
			
		||||
            "add_drop_osnr": 38,
 | 
			
		||||
            "restrictions": {
 | 
			
		||||
                            "preamp_variety_list":[],
 | 
			
		||||
                            "booster_variety_list":[]
 | 
			
		||||
                            }            
 | 
			
		||||
            }],
 | 
			
		||||
      "SI":[{
 | 
			
		||||
            "f_min": 191.3e12,
 | 
			
		||||
@@ -107,10 +217,10 @@
 | 
			
		||||
            "f_max":195.1e12,
 | 
			
		||||
            "spacing": 50e9,
 | 
			
		||||
            "power_dbm": 0,
 | 
			
		||||
            "power_range_db": [0,0,0.5],
 | 
			
		||||
            "power_range_db": [0,0,1],
 | 
			
		||||
            "roll_off": 0.15,
 | 
			
		||||
            "tx_osnr": 40,
 | 
			
		||||
            "sys_margins": 0
 | 
			
		||||
            "sys_margins": 2
 | 
			
		||||
            }],
 | 
			
		||||
      "Transceiver":[
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -623,31 +623,469 @@
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lannion_CAS to Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Corlay to Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Corlay",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Loudeac to Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Loudeac",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lannion_CAS to Stbrieuc",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Stbrieuc to Rennes_STA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Stbrieuc",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 1.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lannion_CAS to Morlaix",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lorient_KMA to Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Vannes_KBE to Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Vannes_KBE",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 4.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Rennes_STA to Stbrieuc",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Rennes_STA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Brest_KLA to Morlaix",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Brest_KLA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 4.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Lannion_CAS to Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Corlay to Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Corlay",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Loudeac to Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Loudeac",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Lorient_KMA to Vannes_KBE",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Lannion_CAS to Stbrieuc",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Stbrieuc to Rennes_STA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Stbrieuc",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 1.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Lannion_CAS to Morlaix",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Lorient_KMA to Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Vannes_KBE to Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Vannes_KBE",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 4.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Rennes_STA to Stbrieuc",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Rennes_STA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west edfa in Brest_KLA to Morlaix",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Brest_KLA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 4.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": null,
 | 
			
		||||
        "delta_p": 1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lorient_KMA to Vannes_KBE",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "loss": 0
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "connections": [
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "east edfa in Lannion_CAS to Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lannion_CAS to Corlay",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Corlay)-F061"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Corlay → Lannion_CAS)-F061",
 | 
			
		||||
      "to_node": "west edfa in Lannion_CAS to Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west edfa in Lannion_CAS to Corlay",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "east edfa in Lannion_CAS to Stbrieuc"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lannion_CAS to Stbrieuc",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Stbrieuc → Lannion_CAS)-F056",
 | 
			
		||||
      "to_node": "west edfa in Lannion_CAS to Stbrieuc"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west edfa in Lannion_CAS to Stbrieuc",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "east edfa in Lannion_CAS to Morlaix"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lannion_CAS to Morlaix",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Morlaix)-F059"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Morlaix → Lannion_CAS)-F059",
 | 
			
		||||
      "to_node": "west edfa in Lannion_CAS to Morlaix"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west edfa in Lannion_CAS to Morlaix",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -684,18 +1122,34 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "east edfa in Lorient_KMA to Loudeac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lorient_KMA to Loudeac",
 | 
			
		||||
      "to_node": "fiber (Lorient_KMA → Loudeac)-F054"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Loudeac → Lorient_KMA)-F054",
 | 
			
		||||
      "to_node": "west edfa in Lorient_KMA to Loudeac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west edfa in Lorient_KMA to Loudeac",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "east edfa in Lorient_KMA to Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lorient_KMA to Vannes_KBE",
 | 
			
		||||
      "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055",
 | 
			
		||||
      "to_node": "west edfa in Lorient_KMA to Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west edfa in Lorient_KMA to Vannes_KBE",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -708,10 +1162,18 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Vannes_KBE",
 | 
			
		||||
      "to_node": "east edfa in Vannes_KBE to Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Vannes_KBE to Lorient_KMA",
 | 
			
		||||
      "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055",
 | 
			
		||||
      "to_node": "west edfa in Vannes_KBE to Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west edfa in Vannes_KBE to Lorient_KMA",
 | 
			
		||||
      "to_node": "roadm Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -724,18 +1186,34 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056",
 | 
			
		||||
      "to_node": "east edfa in Stbrieuc to Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Stbrieuc to Rennes_STA",
 | 
			
		||||
      "to_node": "fiber (Stbrieuc → Rennes_STA)-F057"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Rennes_STA → Stbrieuc)-F057",
 | 
			
		||||
      "to_node": "west edfa in Stbrieuc to Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west edfa in Stbrieuc to Rennes_STA",
 | 
			
		||||
      "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Rennes_STA",
 | 
			
		||||
      "to_node": "east edfa in Rennes_STA to Stbrieuc"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Rennes_STA to Stbrieuc",
 | 
			
		||||
      "to_node": "fiber (Rennes_STA → Stbrieuc)-F057"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Stbrieuc → Rennes_STA)-F057",
 | 
			
		||||
      "to_node": "west edfa in Rennes_STA to Stbrieuc"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west edfa in Rennes_STA to Stbrieuc",
 | 
			
		||||
      "to_node": "roadm Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -764,10 +1242,18 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Brest_KLA",
 | 
			
		||||
      "to_node": "east edfa in Brest_KLA to Morlaix"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Brest_KLA to Morlaix",
 | 
			
		||||
      "to_node": "fiber (Brest_KLA → Morlaix)-F060"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Morlaix → Brest_KLA)-F060",
 | 
			
		||||
      "to_node": "west edfa in Brest_KLA to Morlaix"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west edfa in Brest_KLA to Morlaix",
 | 
			
		||||
      "to_node": "roadm Brest_KLA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -2,10 +2,11 @@
 | 
			
		||||
  "path-request": [
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "0",
 | 
			
		||||
      "source": "Lorient_KMA",
 | 
			
		||||
      "destination": "Vannes_KBE",
 | 
			
		||||
      "source": "trx Lorient_KMA",
 | 
			
		||||
      "destination": "trx Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -13,8 +14,8 @@
 | 
			
		||||
          "trx_mode": null,
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
@@ -22,17 +23,15 @@
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 100000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "1",
 | 
			
		||||
      "source": "Brest_KLA",
 | 
			
		||||
      "destination": "Vannes_KBE",
 | 
			
		||||
      "source": "trx Brest_KLA",
 | 
			
		||||
      "destination": "trx Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Brest_KLA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -40,8 +39,8 @@
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
@@ -50,66 +49,42 @@
 | 
			
		||||
          "path_bandwidth": 200000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm Brest_KLA",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 1,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm Lannion_CAS",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 2,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm Lorient_KMA",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 3,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm Vannes_KBE",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
@@ -117,10 +92,11 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "3",
 | 
			
		||||
      "source": "Lannion_CAS",
 | 
			
		||||
      "destination": "Rennes_STA",
 | 
			
		||||
      "source": "trx Lannion_CAS",
 | 
			
		||||
      "destination": "trx Rennes_STA",
 | 
			
		||||
      "src-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "dst-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -128,8 +104,8 @@
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
@@ -137,17 +113,15 @@
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 60000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "4",
 | 
			
		||||
      "source": "Rennes_STA",
 | 
			
		||||
      "destination": "Lannion_CAS",
 | 
			
		||||
      "source": "trx Rennes_STA",
 | 
			
		||||
      "destination": "trx Lannion_CAS",
 | 
			
		||||
      "src-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "dst-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -155,8 +129,8 @@
 | 
			
		||||
          "trx_mode": null,
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 75000000000.0,
 | 
			
		||||
@@ -164,17 +138,15 @@
 | 
			
		||||
          "output-power": 0.0019952623149688794,
 | 
			
		||||
          "path_bandwidth": 150000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "5",
 | 
			
		||||
      "source": "Rennes_STA",
 | 
			
		||||
      "destination": "Lannion_CAS",
 | 
			
		||||
      "source": "trx Rennes_STA",
 | 
			
		||||
      "destination": "trx Lannion_CAS",
 | 
			
		||||
      "src-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "dst-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -182,8 +154,8 @@
 | 
			
		||||
          "trx_mode": "mode 2",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 75000000000.0,
 | 
			
		||||
@@ -191,17 +163,15 @@
 | 
			
		||||
          "output-power": 0.0019952623149688794,
 | 
			
		||||
          "path_bandwidth": 20000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "6",
 | 
			
		||||
      "source": "Lannion_CAS",
 | 
			
		||||
      "destination": "Lorient_KMA",
 | 
			
		||||
      "source": "trx Lannion_CAS",
 | 
			
		||||
      "destination": "trx Lorient_KMA",
 | 
			
		||||
      "src-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "dst-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -209,8 +179,8 @@
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
@@ -218,17 +188,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "7",
 | 
			
		||||
      "source": "Lannion_CAS",
 | 
			
		||||
      "destination": "Lorient_KMA",
 | 
			
		||||
      "source": "trx Lannion_CAS",
 | 
			
		||||
      "destination": "trx Lorient_KMA",
 | 
			
		||||
      "src-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "dst-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -236,8 +204,8 @@
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
@@ -245,17 +213,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 400000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "7b",
 | 
			
		||||
      "source": "Lannion_CAS",
 | 
			
		||||
      "destination": "Lorient_KMA",
 | 
			
		||||
      "source": "trx Lannion_CAS",
 | 
			
		||||
      "destination": "trx Lorient_KMA",
 | 
			
		||||
      "src-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "dst-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -263,8 +229,8 @@
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 75000000000.0,
 | 
			
		||||
@@ -272,9 +238,6 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 400000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
@@ -282,9 +245,8 @@
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "3",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "relaxable": "false",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "3",
 | 
			
		||||
          "1"
 | 
			
		||||
@@ -294,9 +256,8 @@
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "4",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "relaxable": "false",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "4",
 | 
			
		||||
          "5"
 | 
			
		||||
 
 | 
			
		||||
@@ -18,77 +18,107 @@ from pathlib import Path
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO
 | 
			
		||||
from json import dumps, loads
 | 
			
		||||
from networkx import (draw_networkx_nodes, draw_networkx_edges,
 | 
			
		||||
                      draw_networkx_labels)
 | 
			
		||||
from numpy import mean
 | 
			
		||||
from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element
 | 
			
		||||
from gnpy.core.utils import load_json
 | 
			
		||||
from gnpy.core.network import load_network, build_network, set_roadm_loss, save_network
 | 
			
		||||
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch, automatic_spacing
 | 
			
		||||
from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused, Fiber
 | 
			
		||||
from gnpy.core.network import load_network, build_network, save_network, network_from_json
 | 
			
		||||
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch
 | 
			
		||||
from gnpy.core.elements import Transceiver, Roadm
 | 
			
		||||
from gnpy.core.utils import db2lin, lin2db
 | 
			
		||||
from gnpy.core.request import (Path_request, Result_element, compute_constrained_path,
 | 
			
		||||
                              propagate, jsontocsv, Disjunction, compute_path_dsjctn, requests_aggregation,
 | 
			
		||||
                              propagate_and_optimize_mode)
 | 
			
		||||
from gnpy.core.request import (Path_request, Result_element,
 | 
			
		||||
                               propagate, jsontocsv, Disjunction, compute_path_dsjctn,
 | 
			
		||||
                               requests_aggregation, propagate_and_optimize_mode,
 | 
			
		||||
                               BLOCKING_NOPATH, BLOCKING_NOMODE,
 | 
			
		||||
                               find_reversed_path)
 | 
			
		||||
from gnpy.core.exceptions import (ConfigurationError, EquipmentConfigError, NetworkTopologyError,
 | 
			
		||||
                                  ServiceError, DisjunctionError)
 | 
			
		||||
import gnpy.core.ansi_escapes as ansi_escapes
 | 
			
		||||
from gnpy.core.spectrum_assignment import (build_oms_list, pth_assign_spectrum)
 | 
			
		||||
from copy import copy, deepcopy
 | 
			
		||||
from textwrap import dedent
 | 
			
		||||
from math import ceil
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from flask import Flask, jsonify, make_response, request
 | 
			
		||||
from flask_restful import Api, Resource, reqparse, fields
 | 
			
		||||
 | 
			
		||||
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
 | 
			
		||||
 | 
			
		||||
logger = getLogger(__name__)
 | 
			
		||||
LOGGER = getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
parser = ArgumentParser(description = 'A function that computes performances for a list of services provided in a json file or an excel sheet.')
 | 
			
		||||
parser.add_argument('network_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls')
 | 
			
		||||
parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls')
 | 
			
		||||
parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json')
 | 
			
		||||
parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence')
 | 
			
		||||
parser.add_argument('-o', '--output', type = Path)
 | 
			
		||||
PARSER = ArgumentParser(description='A function that computes performances for a list of ' +
 | 
			
		||||
                        'services provided in a json file or an excel sheet.')
 | 
			
		||||
PARSER.add_argument('network_filename', nargs='?', type=Path,\
 | 
			
		||||
                    default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\
 | 
			
		||||
                    help='input topology file in xls or json')
 | 
			
		||||
PARSER.add_argument('service_filename', nargs='?', type=Path,\
 | 
			
		||||
                    default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\
 | 
			
		||||
                    help='input service file in xls or json')
 | 
			
		||||
PARSER.add_argument('eqpt_filename', nargs='?', type=Path,\
 | 
			
		||||
                    default=Path(__file__).parent / 'eqpt_config.json',\
 | 
			
		||||
                    help='input equipment library in json. Default is eqpt_config.json')
 | 
			
		||||
PARSER.add_argument('-bi', '--bidir', action='store_true',\
 | 
			
		||||
                    help='considers that all demands are bidir')
 | 
			
		||||
PARSER.add_argument('-v', '--verbose', action='count', default=0,\
 | 
			
		||||
                    help='increases verbosity for each occurence')
 | 
			
		||||
PARSER.add_argument('-o', '--output', type=Path)
 | 
			
		||||
PARSER.add_argument('-r', '--rest', action='count', default=0, help='use the REST API')
 | 
			
		||||
 | 
			
		||||
NETWORK_FILENAME = 'topoDemov1.json' #'disagregatedTopoDemov1.json' #
 | 
			
		||||
 | 
			
		||||
def requests_from_json(json_data,equipment):
 | 
			
		||||
APP = Flask(__name__, static_url_path="")
 | 
			
		||||
API = Api(APP)
 | 
			
		||||
 | 
			
		||||
def requests_from_json(json_data, equipment):
 | 
			
		||||
    """ converts the json data into a list of requests elements
 | 
			
		||||
    """
 | 
			
		||||
    requests_list = []
 | 
			
		||||
 | 
			
		||||
    for req in json_data['path-request']:
 | 
			
		||||
        # init all params from request
 | 
			
		||||
        params = {}
 | 
			
		||||
        params['request_id'] = req['request-id']
 | 
			
		||||
        params['source'] = req['src-tp-id']
 | 
			
		||||
        params['destination'] = req['dst-tp-id']
 | 
			
		||||
        params['source'] = req['source']
 | 
			
		||||
        params['bidir'] = req['bidirectional']
 | 
			
		||||
        params['destination'] = req['destination']
 | 
			
		||||
        params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type']
 | 
			
		||||
        params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode']
 | 
			
		||||
        params['format'] = params['trx_mode']
 | 
			
		||||
        nd_list = req['optimizations']['explicit-route-include-objects']
 | 
			
		||||
        params['nodes_list'] = [n['unnumbered-hop']['node-id'] for n in nd_list]
 | 
			
		||||
        params['loose_list'] = [n['unnumbered-hop']['hop-type'] for n in nd_list]
 | 
			
		||||
        params['spacing'] = req['path-constraints']['te-bandwidth']['spacing']
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            nd_list = req['explicit-route-objects']['route-object-include-exclude']
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            nd_list = []
 | 
			
		||||
        params['nodes_list'] = [n['num-unnum-hop']['node-id'] for n in nd_list]
 | 
			
		||||
        params['loose_list'] = [n['num-unnum-hop']['hop-type'] for n in nd_list]
 | 
			
		||||
        # recover trx physical param (baudrate, ...) from type and mode
 | 
			
		||||
        # in trx_mode_params optical power is read from equipment['SI']['default'] and
 | 
			
		||||
        # nb_channel is computed based on min max frequency and spacing
 | 
			
		||||
        trx_params = trx_mode_params(equipment,params['trx_type'],params['trx_mode'],True)
 | 
			
		||||
        trx_params = trx_mode_params(equipment, params['trx_type'], params['trx_mode'], True)
 | 
			
		||||
        params.update(trx_params)
 | 
			
		||||
        # print(trx_params['min_spacing'])
 | 
			
		||||
        # optical power might be set differently in the request. if it is indicated then the 
 | 
			
		||||
        # optical power might be set differently in the request. if it is indicated then the
 | 
			
		||||
        # params['power'] is updated
 | 
			
		||||
        if req['path-constraints']['te-bandwidth']['output-power']:
 | 
			
		||||
            params['power'] = req['path-constraints']['te-bandwidth']['output-power']
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            if req['path-constraints']['te-bandwidth']['output-power']:
 | 
			
		||||
                params['power'] = req['path-constraints']['te-bandwidth']['output-power']
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            pass
 | 
			
		||||
        # same process for nb-channel
 | 
			
		||||
        f_min = params['f_min']
 | 
			
		||||
        f_max_from_si = params['f_max']
 | 
			
		||||
        if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None :
 | 
			
		||||
            nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] 
 | 
			
		||||
            params['nb_channel'] = nch         
 | 
			
		||||
            spacing = params['spacing']
 | 
			
		||||
            params['f_max'] = f_min + nch*spacing
 | 
			
		||||
        else :
 | 
			
		||||
            params['nb_channel'] = automatic_nch(f_min,f_max_from_si,params['spacing'])
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None:
 | 
			
		||||
                nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel']
 | 
			
		||||
                params['nb_channel'] = nch
 | 
			
		||||
                spacing = params['spacing']
 | 
			
		||||
                params['f_max'] = f_min + nch*spacing
 | 
			
		||||
            else:
 | 
			
		||||
                params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
 | 
			
		||||
        consistency_check(params, f_max_from_si)
 | 
			
		||||
 | 
			
		||||
        try :
 | 
			
		||||
        try:
 | 
			
		||||
            params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth']
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            pass
 | 
			
		||||
@@ -96,304 +126,444 @@ def requests_from_json(json_data,equipment):
 | 
			
		||||
    return requests_list
 | 
			
		||||
 | 
			
		||||
def consistency_check(params, f_max_from_si):
 | 
			
		||||
    """ checks that the requested parameters are consistant (spacing vs nb channel,
 | 
			
		||||
        vs transponder mode...)
 | 
			
		||||
    """
 | 
			
		||||
    f_min = params['f_min']
 | 
			
		||||
    f_max = params['f_max']
 | 
			
		||||
    max_recommanded_nb_channels = automatic_nch(f_min,f_max,
 | 
			
		||||
                params['spacing'])
 | 
			
		||||
    max_recommanded_nb_channels = automatic_nch(f_min, f_max, params['spacing'])
 | 
			
		||||
    if params['baud_rate'] is not None:
 | 
			
		||||
        #implicitely means that a mode is defined with min_spacing
 | 
			
		||||
        if params['min_spacing']>params['spacing'] : 
 | 
			
		||||
            msg = f'Request {params["request_id"]} has spacing below transponder {params["trx_type"]}'+\
 | 
			
		||||
                f' {params["trx_mode"]} min spacing value {params["min_spacing"]*1e-9}GHz.\n'+\
 | 
			
		||||
                'Computation stopped'
 | 
			
		||||
        if params['min_spacing'] > params['spacing']:
 | 
			
		||||
            msg = f'Request {params["request_id"]} has spacing below transponder ' +\
 | 
			
		||||
                  f'{params["trx_type"]} {params["trx_mode"]} min spacing value ' +\
 | 
			
		||||
                  f'{params["min_spacing"]*1e-9}GHz.\nComputation stopped'
 | 
			
		||||
            print(msg)
 | 
			
		||||
            logger.critical(msg)
 | 
			
		||||
            exit()
 | 
			
		||||
        if f_max>f_max_from_si:
 | 
			
		||||
            LOGGER.critical(msg)
 | 
			
		||||
            raise ServiceError(msg)
 | 
			
		||||
        if f_max > f_max_from_si:
 | 
			
		||||
            msg = dedent(f'''
 | 
			
		||||
            Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz 
 | 
			
		||||
            is not consistent with frequency range {f_min*1e-12} THz, {f_max*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz.
 | 
			
		||||
            max recommanded nb of channels is {max_recommanded_nb_channels}
 | 
			
		||||
            Computation stopped.''')
 | 
			
		||||
            logger.critical(msg)
 | 
			
		||||
            exit()    
 | 
			
		||||
            LOGGER.critical(msg)
 | 
			
		||||
            raise ServiceError(msg)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def disjunctions_from_json(json_data):
 | 
			
		||||
    """ reads the disjunction requests from the json dict and create the list
 | 
			
		||||
        of requested disjunctions for this set of requests
 | 
			
		||||
    """
 | 
			
		||||
    disjunctions_list = []
 | 
			
		||||
    try:
 | 
			
		||||
        temp_test = json_data['synchronization']
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        temp_test = []
 | 
			
		||||
    if temp_test:
 | 
			
		||||
        for snc in json_data['synchronization']:
 | 
			
		||||
            params = {}
 | 
			
		||||
            params['disjunction_id'] = snc['synchronization-id']
 | 
			
		||||
            params['relaxable'] = snc['svec']['relaxable']
 | 
			
		||||
            params['link_diverse'] = 'link' in snc['svec']['disjointness']
 | 
			
		||||
            params['node_diverse'] = 'node' in snc['svec']['disjointness']
 | 
			
		||||
            params['disjunctions_req'] = snc['svec']['request-id-number']
 | 
			
		||||
            disjunctions_list.append(Disjunction(**params))
 | 
			
		||||
 | 
			
		||||
    for snc in json_data['synchronization']:
 | 
			
		||||
        params = {}
 | 
			
		||||
        params['disjunction_id'] = snc['synchronization-id']
 | 
			
		||||
        params['relaxable'] = snc['svec']['relaxable']
 | 
			
		||||
        params['link_diverse'] = snc['svec']['link-diverse']
 | 
			
		||||
        params['node_diverse'] = snc['svec']['node-diverse']
 | 
			
		||||
        params['disjunctions_req'] = snc['svec']['request-id-number']
 | 
			
		||||
        disjunctions_list.append(Disjunction(**params))
 | 
			
		||||
    return disjunctions_list
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def load_requests(filename,eqpt_filename):
 | 
			
		||||
def load_requests(filename, eqpt_filename, bidir):
 | 
			
		||||
    """ loads the requests from a json or an excel file into a data string
 | 
			
		||||
    """
 | 
			
		||||
    if filename.suffix.lower() == '.xls':
 | 
			
		||||
        logger.info('Automatically converting requests from XLS to JSON')
 | 
			
		||||
        json_data = convert_service_sheet(filename,eqpt_filename)
 | 
			
		||||
        LOGGER.info('Automatically converting requests from XLS to JSON')
 | 
			
		||||
        try:
 | 
			
		||||
            json_data = convert_service_sheet(filename, eqpt_filename, bidir=bidir)
 | 
			
		||||
        except ServiceError as this_e:
 | 
			
		||||
            print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
 | 
			
		||||
            exit(1)
 | 
			
		||||
    else:
 | 
			
		||||
        with open(filename, encoding='utf-8') as f:
 | 
			
		||||
            json_data = loads(f.read())
 | 
			
		||||
        with open(filename, encoding='utf-8') as my_f:
 | 
			
		||||
            json_data = loads(my_f.read())
 | 
			
		||||
    return json_data
 | 
			
		||||
 | 
			
		||||
def compute_path(network, equipment, pathreqlist):
 | 
			
		||||
 | 
			
		||||
    # This function is obsolete and not relevant with respect to network building: suggest either to correct
 | 
			
		||||
    # or to suppress it
 | 
			
		||||
    
 | 
			
		||||
    path_res_list = []
 | 
			
		||||
 | 
			
		||||
    for pathreq in pathreqlist:
 | 
			
		||||
        #need to rebuid the network for each path because the total power
 | 
			
		||||
        #can be different and the choice of amplifiers in autodesign is power dependant
 | 
			
		||||
        #but the design is the same if the total power is the same
 | 
			
		||||
        #TODO parametrize the total spectrum power so the same design can be shared
 | 
			
		||||
        p_db = lin2db(pathreq.power*1e3)
 | 
			
		||||
        p_total_db = p_db + lin2db(pathreq.nb_channel)
 | 
			
		||||
        build_network(network, equipment, p_db, p_total_db)
 | 
			
		||||
        pathreq.nodes_list.append(pathreq.destination)
 | 
			
		||||
        #we assume that the destination is a strict constraint
 | 
			
		||||
        pathreq.loose_list.append('strict')
 | 
			
		||||
        print(f'Computing path from {pathreq.source} to {pathreq.destination}')
 | 
			
		||||
        print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output
 | 
			
		||||
        total_path = compute_constrained_path(network, pathreq)
 | 
			
		||||
        print(f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}\n')
 | 
			
		||||
 | 
			
		||||
        if total_path :
 | 
			
		||||
            total_path = propagate(total_path,pathreq,equipment, show=False)
 | 
			
		||||
        else:
 | 
			
		||||
            total_path = []
 | 
			
		||||
        # we record the last tranceiver object in order to have th whole
 | 
			
		||||
        # information about spectrum. Important Note: since transceivers
 | 
			
		||||
        # attached to roadms are actually logical elements to simulate
 | 
			
		||||
        # performance, several demands having the same destination may use
 | 
			
		||||
        # the same transponder for the performance simaulation. This is why
 | 
			
		||||
        # we use deepcopy: to ensure each propagation is recorded and not
 | 
			
		||||
        # overwritten
 | 
			
		||||
 | 
			
		||||
        path_res_list.append(deepcopy(total_path))
 | 
			
		||||
    return path_res_list
 | 
			
		||||
 | 
			
		||||
def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist):
 | 
			
		||||
    
 | 
			
		||||
    # use a list but a dictionnary might be helpful to find path bathsed on request_id
 | 
			
		||||
    # TODO change all these req, dsjct, res lists into dict !
 | 
			
		||||
    """ use a list but a dictionnary might be helpful to find path based on request_id
 | 
			
		||||
        TODO change all these req, dsjct, res lists into dict !
 | 
			
		||||
    """
 | 
			
		||||
    path_res_list = []
 | 
			
		||||
    reversed_path_res_list = []
 | 
			
		||||
    propagated_reversed_path_res_list = []
 | 
			
		||||
 | 
			
		||||
    for i,pathreq in enumerate(pathreqlist):
 | 
			
		||||
    for i, pathreq in enumerate(pathreqlist):
 | 
			
		||||
 | 
			
		||||
        # use the power specified in requests but might be different from the one specified for design
 | 
			
		||||
        # the power is an optional parameter for requests definition
 | 
			
		||||
        # if optional, use the one defines in eqt_config.json
 | 
			
		||||
        # use the power specified in requests but might be different from the one
 | 
			
		||||
        # specified for design the power is an optional parameter for requests
 | 
			
		||||
        # definition if optional, use the one defines in eqt_config.json
 | 
			
		||||
        p_db = lin2db(pathreq.power*1e3)
 | 
			
		||||
        p_total_db = p_db + lin2db(pathreq.nb_channel)
 | 
			
		||||
        print(f'request {pathreq.request_id}')
 | 
			
		||||
        print(f'Computing path from {pathreq.source} to {pathreq.destination}')
 | 
			
		||||
        print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output
 | 
			
		||||
        # adding first node to be clearer on the output
 | 
			
		||||
        print(f'with path constraint: {[pathreq.source] + pathreq.nodes_list}')
 | 
			
		||||
 | 
			
		||||
        total_path = pathlist[i]
 | 
			
		||||
        print(f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}\n')
 | 
			
		||||
        # pathlist[i] contains the whole path information for request i
 | 
			
		||||
        # last element is a transciver and where the result of the propagation is
 | 
			
		||||
        # recorded.
 | 
			
		||||
        # Important Note: since transceivers attached to roadms are actually logical
 | 
			
		||||
        # elements to simulate performance, several demands having the same destination
 | 
			
		||||
        # may use the same transponder for the performance simulation. This is why
 | 
			
		||||
        # we use deepcopy: to ensure that each propagation is recorded and not overwritten
 | 
			
		||||
        total_path = deepcopy(pathlist[i])
 | 
			
		||||
        print(f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}')
 | 
			
		||||
        # for debug
 | 
			
		||||
        # print(f'{pathreq.baud_rate}   {pathreq.power}   {pathreq.spacing}   {pathreq.nb_channel}')
 | 
			
		||||
        if total_path :
 | 
			
		||||
        if total_path:
 | 
			
		||||
            if pathreq.baud_rate is not None:
 | 
			
		||||
                total_path = propagate(total_path,pathreq,equipment, show=False)
 | 
			
		||||
                temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))),2)
 | 
			
		||||
                if temp_snr01nm < pathreq.OSNR :
 | 
			
		||||
                    msg = f'\tWarning! Request {pathreq.request_id} computed path from {pathreq.source} to {pathreq.destination} does not pass with {pathreq.tsp_mode}\n' +\
 | 
			
		||||
                    f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}\n'
 | 
			
		||||
                # means that at this point the mode was entered/forced by user and thus a
 | 
			
		||||
                # baud_rate was defined
 | 
			
		||||
                total_path = propagate(total_path, pathreq, equipment)
 | 
			
		||||
                temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2)
 | 
			
		||||
                if temp_snr01nm < pathreq.OSNR:
 | 
			
		||||
                    msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
 | 
			
		||||
                          f' {pathreq.source} to {pathreq.destination} does not pass with' +\
 | 
			
		||||
                          f' {pathreq.tsp_mode}\n\tcomputedSNR in 0.1nm = {temp_snr01nm} ' +\
 | 
			
		||||
                          f'- required osnr {pathreq.OSNR}'
 | 
			
		||||
                    print(msg)
 | 
			
		||||
                    logger.warning(msg)
 | 
			
		||||
                    total_path = []
 | 
			
		||||
                    LOGGER.warning(msg)
 | 
			
		||||
                    pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
 | 
			
		||||
            else:
 | 
			
		||||
                total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment)
 | 
			
		||||
                # if no baudrate satisfies spacing, no mode is returned and an empty path is returned
 | 
			
		||||
                total_path, mode = propagate_and_optimize_mode(total_path, pathreq, equipment)
 | 
			
		||||
                # if no baudrate satisfies spacing, no mode is returned and the last explored mode
 | 
			
		||||
                # a warning is shown in the propagate_and_optimize_mode
 | 
			
		||||
                if mode is not None :
 | 
			
		||||
                    # propagate_and_optimize_mode function returns the mode with the highest bitrate
 | 
			
		||||
                    # that passes. if no mode passes, then it returns an empty path
 | 
			
		||||
                # propagate_and_optimize_mode function returns the mode with the highest bitrate
 | 
			
		||||
                # that passes. if no mode passes, then a attribute blocking_reason is added on
 | 
			
		||||
                # pathreq that contains the reason for blocking: 'NO_PATH', 'NO_FEASIBLE_MODE', ...
 | 
			
		||||
                try:
 | 
			
		||||
                    if pathreq.blocking_reason in BLOCKING_NOPATH:
 | 
			
		||||
                        total_path = []
 | 
			
		||||
                    elif pathreq.blocking_reason in BLOCKING_NOMODE:
 | 
			
		||||
                        pathreq.baud_rate = mode['baud_rate']
 | 
			
		||||
                        pathreq.tsp_mode = mode['format']
 | 
			
		||||
                        pathreq.format = mode['format']
 | 
			
		||||
                        pathreq.OSNR = mode['OSNR']
 | 
			
		||||
                        pathreq.tx_osnr = mode['tx_osnr']
 | 
			
		||||
                        pathreq.bit_rate = mode['bit_rate']
 | 
			
		||||
                    # other blocking reason should not appear at this point
 | 
			
		||||
                except AttributeError:
 | 
			
		||||
                    pathreq.baud_rate = mode['baud_rate']
 | 
			
		||||
                    pathreq.tsp_mode = mode['format']
 | 
			
		||||
                    pathreq.format = mode['format']
 | 
			
		||||
                    pathreq.OSNR = mode['OSNR']
 | 
			
		||||
                    pathreq.tx_osnr = mode['tx_osnr']
 | 
			
		||||
                    pathreq.bit_rate = mode['bit_rate']
 | 
			
		||||
                else :
 | 
			
		||||
                    total_path = []
 | 
			
		||||
        # we record the last tranceiver object in order to have th whole 
 | 
			
		||||
        # information about spectrum. Important Note: since transceivers 
 | 
			
		||||
        # attached to roadms are actually logical elements to simulate
 | 
			
		||||
        # performance, several demands having the same destination may use 
 | 
			
		||||
        # the same transponder for the performance simaulation. This is why 
 | 
			
		||||
        # we use deepcopy: to ensure each propagation is recorded and not 
 | 
			
		||||
        # overwritten 
 | 
			
		||||
        
 | 
			
		||||
        path_res_list.append(deepcopy(total_path))
 | 
			
		||||
    return path_res_list
 | 
			
		||||
 | 
			
		||||
            # reversed path is needed for correct spectrum assignment
 | 
			
		||||
            reversed_path = find_reversed_path(pathlist[i])
 | 
			
		||||
            if pathreq.bidir:
 | 
			
		||||
                # only propagate if bidir is true, but needs the reversed path anyway for
 | 
			
		||||
                # correct spectrum assignment
 | 
			
		||||
                rev_p = deepcopy(reversed_path)
 | 
			
		||||
 | 
			
		||||
                print(f'\n\tPropagating Z to A direction {pathreq.destination} to {pathreq.source}')
 | 
			
		||||
                print(f'\tPath (roadsm) {[r.uid for r in rev_p if isinstance(r,Roadm)]}\n')
 | 
			
		||||
                propagated_reversed_path = propagate(rev_p, pathreq, equipment)
 | 
			
		||||
                temp_snr01nm = round(mean(propagated_reversed_path[-1].snr +\
 | 
			
		||||
                                          lin2db(pathreq.baud_rate/(12.5e9))), 2)
 | 
			
		||||
                if temp_snr01nm < pathreq.OSNR:
 | 
			
		||||
                    msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
 | 
			
		||||
                          f' {pathreq.source} to {pathreq.destination} does not pass with' +\
 | 
			
		||||
                          f' {pathreq.tsp_mode}\n' +\
 | 
			
		||||
                          f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}'
 | 
			
		||||
                    print(msg)
 | 
			
		||||
                    LOGGER.warning(msg)
 | 
			
		||||
                    # TODO selection of mode should also be on reversed direction !!
 | 
			
		||||
                    pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
 | 
			
		||||
            else:
 | 
			
		||||
                propagated_reversed_path = []
 | 
			
		||||
        else:
 | 
			
		||||
            msg = 'Total path is empty. No propagation'
 | 
			
		||||
            print(msg)
 | 
			
		||||
            LOGGER.info(msg)
 | 
			
		||||
            reversed_path = []
 | 
			
		||||
            propagated_reversed_path = []
 | 
			
		||||
 | 
			
		||||
        path_res_list.append(total_path)
 | 
			
		||||
        reversed_path_res_list.append(reversed_path)
 | 
			
		||||
        propagated_reversed_path_res_list.append(propagated_reversed_path)
 | 
			
		||||
        # print to have a nice output
 | 
			
		||||
        print('')
 | 
			
		||||
    return path_res_list, reversed_path_res_list, propagated_reversed_path_res_list
 | 
			
		||||
 | 
			
		||||
def correct_route_list(network, pathreqlist):
 | 
			
		||||
    # prepares the format of route list of nodes to be consistant
 | 
			
		||||
    # remove wrong names, remove endpoints
 | 
			
		||||
    # also correct source and destination
 | 
			
		||||
    anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver) and not isinstance(n, Fiber)]
 | 
			
		||||
    # TODO there is a problem of identification of fibers in case of parallel fibers bitween two adjacent roadms
 | 
			
		||||
    # so fiber constraint is not supported
 | 
			
		||||
    """ prepares the format of route list of nodes to be consistant
 | 
			
		||||
        remove wrong names, remove endpoints
 | 
			
		||||
        also correct source and destination
 | 
			
		||||
    """
 | 
			
		||||
    anytype = [n.uid for n in network.nodes()]
 | 
			
		||||
    # TODO there is a problem of identification of fibers in case of parallel fibers
 | 
			
		||||
    # between two adjacent roadms so fiber constraint is not supported
 | 
			
		||||
    transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)]
 | 
			
		||||
    for pathreq in pathreqlist:
 | 
			
		||||
        for i,n_id in enumerate(pathreq.nodes_list):
 | 
			
		||||
        for i, n_id in enumerate(pathreq.nodes_list):
 | 
			
		||||
            # replace possibly wrong name with a formated roadm name
 | 
			
		||||
            # print(n_id)
 | 
			
		||||
            if n_id not in anytype :
 | 
			
		||||
            if n_id not in anytype:
 | 
			
		||||
                # find nodes name that include constraint among all possible names except
 | 
			
		||||
                # transponders (not yet supported as constraints).
 | 
			
		||||
                nodes_suggestion = [uid for uid in anytype \
 | 
			
		||||
                    if n_id.lower() in uid.lower()]
 | 
			
		||||
                if pathreq.loose_list[i] == 'loose':
 | 
			
		||||
                    if len(nodes_suggestion)>0 :
 | 
			
		||||
                    if n_id.lower() in uid.lower() and uid not in transponders]
 | 
			
		||||
                if pathreq.loose_list[i] == 'LOOSE':
 | 
			
		||||
                    if len(nodes_suggestion) > 0:
 | 
			
		||||
                        new_n = nodes_suggestion[0]
 | 
			
		||||
                        print(f'invalid route node specified:\
 | 
			
		||||
                        \n\'{n_id}\', replaced with \'{new_n}\'')
 | 
			
		||||
                        pathreq.nodes_list[i] = new_n
 | 
			
		||||
                    else:
 | 
			
		||||
                        print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!'+'\x1b[0m')
 | 
			
		||||
                        print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\',' +\
 | 
			
		||||
                              f' could not use it as constraint, skipped!'+'\x1b[0m')
 | 
			
		||||
                        pathreq.nodes_list.remove(n_id)
 | 
			
		||||
                        pathreq.loose_list.pop(i)
 | 
			
		||||
                else:
 | 
			
		||||
                    msg = f'\x1b[1;33;40m'+f'could not find node : {n_id} in network topology. Strict constraint can not be applied.'+'\x1b[0m'
 | 
			
		||||
                    logger.critical(msg)
 | 
			
		||||
                    msg = f'\x1b[1;33;40m'+f'could not find node: {n_id} in network topology.' +\
 | 
			
		||||
                          f' Strict constraint can not be applied.' + '\x1b[0m'
 | 
			
		||||
                    LOGGER.critical(msg)
 | 
			
		||||
                    raise ValueError(msg)
 | 
			
		||||
        if pathreq.source not in transponders:
 | 
			
		||||
            msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.'+'\x1b[0m'
 | 
			
		||||
            logger.critical(msg)
 | 
			
		||||
            msg = f'\x1b[1;31;40m' + f'Request: {pathreq.request_id}: could not find' +\
 | 
			
		||||
                  f' transponder source: {pathreq.source}.'+'\x1b[0m'
 | 
			
		||||
            LOGGER.critical(msg)
 | 
			
		||||
            print(f'{msg}\nComputation stopped.')
 | 
			
		||||
            exit()
 | 
			
		||||
            
 | 
			
		||||
        if pathreq.destination not in transponders:
 | 
			
		||||
            msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find transponder destination : {pathreq.destination}.'+'\x1b[0m'
 | 
			
		||||
            logger.critical(msg)
 | 
			
		||||
            print(f'{msg}\nComputation stopped.')
 | 
			
		||||
            exit()
 | 
			
		||||
            raise ServiceError(msg)
 | 
			
		||||
 | 
			
		||||
        # TODO remove endpoints from this list in case they were added by the user in the xls or json files
 | 
			
		||||
        if pathreq.destination not in transponders:
 | 
			
		||||
            msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find' +\
 | 
			
		||||
                  f' transponder destination: {pathreq.destination}.'+'\x1b[0m'
 | 
			
		||||
            LOGGER.critical(msg)
 | 
			
		||||
            print(f'{msg}\nComputation stopped.')
 | 
			
		||||
            raise ServiceError(msg)
 | 
			
		||||
 | 
			
		||||
        # TODO remove endpoints from this list in case they were added by the user
 | 
			
		||||
        # in the xls or json files
 | 
			
		||||
    return pathreqlist
 | 
			
		||||
 | 
			
		||||
def correct_disjn(disjn):
 | 
			
		||||
    """ clean disjunctions to remove possible repetition
 | 
			
		||||
    """
 | 
			
		||||
    local_disjn = disjn.copy()
 | 
			
		||||
    for el in local_disjn:
 | 
			
		||||
        for d in local_disjn:
 | 
			
		||||
            if set(el.disjunctions_req) == set(d.disjunctions_req) and\
 | 
			
		||||
             el.disjunction_id != d.disjunction_id:
 | 
			
		||||
                local_disjn.remove(d)
 | 
			
		||||
    for elem in local_disjn:
 | 
			
		||||
        for dis_elem in local_disjn:
 | 
			
		||||
            if set(elem.disjunctions_req) == set(dis_elem.disjunctions_req) and\
 | 
			
		||||
             elem.disjunction_id != dis_elem.disjunction_id:
 | 
			
		||||
                local_disjn.remove(dis_elem)
 | 
			
		||||
    return local_disjn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def path_result_json(pathresult):
 | 
			
		||||
    """ create the response dictionnary
 | 
			
		||||
    """
 | 
			
		||||
    data = {
 | 
			
		||||
        'path': [n.json for n in pathresult]
 | 
			
		||||
        'response': [n.json for n in pathresult]
 | 
			
		||||
    }
 | 
			
		||||
    return data
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    start = time.time()
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
    basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, DEBUG))
 | 
			
		||||
    logger.info(f'Computing path requests {args.service_filename} into JSON format')
 | 
			
		||||
    print('\x1b[1;34;40m'+f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m')
 | 
			
		||||
    # for debug
 | 
			
		||||
    # print( args.eqpt_filename)
 | 
			
		||||
    data = load_requests(args.service_filename,args.eqpt_filename)
 | 
			
		||||
    equipment = load_equipment(args.eqpt_filename)
 | 
			
		||||
    network = load_network(args.network_filename,equipment)
 | 
			
		||||
 | 
			
		||||
def compute_requests(network, data, equipment):
 | 
			
		||||
    """ Main program calling functions
 | 
			
		||||
    """
 | 
			
		||||
    # Build the network once using the default power defined in SI in eqpt config
 | 
			
		||||
    # TODO power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
 | 
			
		||||
    # spacing, f_min and f_max 
 | 
			
		||||
    # 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)
 | 
			
		||||
    save_network(args.network_filename, network)
 | 
			
		||||
    save_network(ARGS.network_filename, network)
 | 
			
		||||
 | 
			
		||||
    rqs = requests_from_json(data, equipment)
 | 
			
		||||
    oms_list = build_oms_list(network, equipment)
 | 
			
		||||
 | 
			
		||||
    # check that request ids are unique. Non unique ids, may 
 | 
			
		||||
    # mess the computation : better to stop the computation
 | 
			
		||||
    try:
 | 
			
		||||
        rqs = requests_from_json(data, equipment)
 | 
			
		||||
    except ServiceError as this_e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
 | 
			
		||||
        raise this_e
 | 
			
		||||
    # check that request ids are unique. Non unique ids, may
 | 
			
		||||
    # mess the computation: better to stop the computation
 | 
			
		||||
    all_ids = [r.request_id for r in rqs]
 | 
			
		||||
    if len(all_ids) != len(set(all_ids)):
 | 
			
		||||
        for a in list(set(all_ids)):
 | 
			
		||||
            all_ids.remove(a)
 | 
			
		||||
        for item in list(set(all_ids)):
 | 
			
		||||
            all_ids.remove(item)
 | 
			
		||||
        msg = f'Requests id {all_ids} are not unique'
 | 
			
		||||
        logger.critical(msg)
 | 
			
		||||
        exit()
 | 
			
		||||
    rqs = correct_route_list(network, rqs)
 | 
			
		||||
 | 
			
		||||
        LOGGER.critical(msg)
 | 
			
		||||
        raise ServiceError(msg)
 | 
			
		||||
    try:
 | 
			
		||||
        rqs = correct_route_list(network, rqs)
 | 
			
		||||
    except ServiceError as this_e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
 | 
			
		||||
        raise this_e
 | 
			
		||||
        #exit(1)
 | 
			
		||||
    # pths = compute_path(network, equipment, rqs)
 | 
			
		||||
    dsjn = disjunctions_from_json(data)
 | 
			
		||||
 | 
			
		||||
    print('\x1b[1;34;40m'+f'List of disjunctions'+ '\x1b[0m')
 | 
			
		||||
    print('\x1b[1;34;40m' + f'List of disjunctions' + '\x1b[0m')
 | 
			
		||||
    print(dsjn)
 | 
			
		||||
    # need to warn or correct in case of wrong disjunction form
 | 
			
		||||
    # disjunction must not be repeated with same or different ids
 | 
			
		||||
    dsjn = correct_disjn(dsjn)
 | 
			
		||||
        
 | 
			
		||||
    # Aggregate demands with same exact constraints
 | 
			
		||||
    print('\x1b[1;34;40m'+f'Aggregating similar requests'+ '\x1b[0m')
 | 
			
		||||
 | 
			
		||||
    rqs,dsjn = requests_aggregation(rqs,dsjn)
 | 
			
		||||
    # Aggregate demands with same exact constraints
 | 
			
		||||
    print('\x1b[1;34;40m' + f'Aggregating similar requests' + '\x1b[0m')
 | 
			
		||||
 | 
			
		||||
    rqs, dsjn = requests_aggregation(rqs, dsjn)
 | 
			
		||||
    # TODO export novel set of aggregated demands in a json file
 | 
			
		||||
 | 
			
		||||
    print('\x1b[1;34;40m'+'The following services have been requested:'+ '\x1b[0m')
 | 
			
		||||
    print('\x1b[1;34;40m' + 'The following services have been requested:' + '\x1b[0m')
 | 
			
		||||
    print(rqs)
 | 
			
		||||
    
 | 
			
		||||
    print('\x1b[1;34;40m'+f'Computing all paths with constraints'+ '\x1b[0m')
 | 
			
		||||
    pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
 | 
			
		||||
 | 
			
		||||
    print('\x1b[1;34;40m'+f'Propagating on selected path'+ '\x1b[0m')
 | 
			
		||||
    propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths)
 | 
			
		||||
    print('\x1b[1;34;40m' + f'Computing all paths with constraints' + '\x1b[0m')
 | 
			
		||||
    try:
 | 
			
		||||
        pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
 | 
			
		||||
    except DisjunctionError as this_e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Disjunction error:{ansi_escapes.reset} {this_e}')
 | 
			
		||||
        raise this_e
 | 
			
		||||
 | 
			
		||||
    print('\x1b[1;34;40m' + f'Propagating on selected path' + '\x1b[0m')
 | 
			
		||||
    propagatedpths, reversed_pths, reversed_propagatedpths = \
 | 
			
		||||
        compute_path_with_disjunction(network, equipment, rqs, pths)
 | 
			
		||||
    # Note that deepcopy used in compute_path_with_disjunction returns
 | 
			
		||||
    # a list of nodes which are not belonging to network (they are copies of the node objects).
 | 
			
		||||
    # so there can not be propagation on these nodes.
 | 
			
		||||
 | 
			
		||||
    pth_assign_spectrum(pths, rqs, oms_list, reversed_pths)
 | 
			
		||||
 | 
			
		||||
    end = time.time()
 | 
			
		||||
    print(f'computation time {end-start}')
 | 
			
		||||
    print('\x1b[1;34;40m'+f'Result summary'+ '\x1b[0m')
 | 
			
		||||
    
 | 
			
		||||
    header = ['req id', '  demand','  snr@bandwidth','  snr@0.1nm','  Receiver minOSNR', '  mode', '  Gbit/s' , '  nb of tsp pairs']
 | 
			
		||||
    header = ['req id', '  demand', '  snr@bandwidth A-Z (Z-A)', '  snr@0.1nm A-Z (Z-A)',\
 | 
			
		||||
              '  Receiver minOSNR', '  mode', '  Gbit/s', '  nb of tsp pairs',\
 | 
			
		||||
              'N,M or blocking reason']
 | 
			
		||||
    data = []
 | 
			
		||||
    data.append(header)
 | 
			
		||||
    for i, p in enumerate(propagatedpths):
 | 
			
		||||
        if p:
 | 
			
		||||
            line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\
 | 
			
		||||
                f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\
 | 
			
		||||
                f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}' , f'{round(rqs[i].path_bandwidth * 1e-9,2)}' , f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }']
 | 
			
		||||
        else:
 | 
			
		||||
            line = [f'{rqs[i].request_id}',f' {rqs[i].source} to {rqs[i].destination} : not feasible ']
 | 
			
		||||
    for i, this_p in enumerate(propagatedpths):
 | 
			
		||||
        rev_pth = reversed_propagatedpths[i]
 | 
			
		||||
        if rev_pth and this_p:
 | 
			
		||||
            psnrb = f'{round(mean(this_p[-1].snr),2)} ({round(mean(rev_pth[-1].snr),2)})'
 | 
			
		||||
            psnr = f'{round(mean(this_p[-1].snr_01nm), 2)}' +\
 | 
			
		||||
                   f' ({round(mean(rev_pth[-1].snr_01nm),2)})'
 | 
			
		||||
        elif this_p:
 | 
			
		||||
            psnrb = f'{round(mean(this_p[-1].snr),2)}'
 | 
			
		||||
            psnr = f'{round(mean(this_p[-1].snr_01nm),2)}'
 | 
			
		||||
 | 
			
		||||
        try :
 | 
			
		||||
            if rqs[i].blocking_reason in  BLOCKING_NOPATH:
 | 
			
		||||
                line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} :',\
 | 
			
		||||
                        f'-', f'-', f'-', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9,2)}',\
 | 
			
		||||
                        f'-', f'{rqs[i].blocking_reason}']
 | 
			
		||||
            else:
 | 
			
		||||
                line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', psnrb,\
 | 
			
		||||
                        psnr, f'-', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9, 2)}',\
 | 
			
		||||
                        f'-', f'{rqs[i].blocking_reason}']
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', psnrb,\
 | 
			
		||||
                    psnr, f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9,2)}',\
 | 
			
		||||
                    f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }', f'({rqs[i].N},{rqs[i].M})']
 | 
			
		||||
        data.append(line)
 | 
			
		||||
 | 
			
		||||
    col_width = max(len(word) for row in data for word in row[2:])   # padding
 | 
			
		||||
    firstcol_width = max(len(row[0]) for row in data )   # padding
 | 
			
		||||
    secondcol_width = max(len(row[1]) for row in data )   # padding
 | 
			
		||||
    firstcol_width = max(len(row[0]) for row in data)   # padding
 | 
			
		||||
    secondcol_width = max(len(row[1]) for row in data)   # padding
 | 
			
		||||
    for row in data:
 | 
			
		||||
        firstcol = ''.join(row[0].ljust(firstcol_width)) 
 | 
			
		||||
        firstcol = ''.join(row[0].ljust(firstcol_width))
 | 
			
		||||
        secondcol = ''.join(row[1].ljust(secondcol_width))
 | 
			
		||||
        remainingcols = ''.join(word.center(col_width,' ') for word in row[2:])
 | 
			
		||||
        remainingcols = ''.join(word.center(col_width, ' ') for word in row[2:])
 | 
			
		||||
        print(f'{firstcol} {secondcol} {remainingcols}')
 | 
			
		||||
    print('\x1b[1;33;40m'+f'Result summary shows mean SNR and OSNR (average over all channels)' +\
 | 
			
		||||
          '\x1b[0m')
 | 
			
		||||
 | 
			
		||||
    return propagatedpths, reversed_propagatedpths, rqs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    if args.output :
 | 
			
		||||
def launch_cli(network, data, equipment):
 | 
			
		||||
    """ Compute requests using network, data and equipment with client line interface
 | 
			
		||||
    """
 | 
			
		||||
    propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment)
 | 
			
		||||
    #Generate the output
 | 
			
		||||
    if ARGS.output :
 | 
			
		||||
        result = []
 | 
			
		||||
        # assumes that list of rqs and list of propgatedpths have same order
 | 
			
		||||
        for i,p in enumerate(propagatedpths):
 | 
			
		||||
            result.append(Result_element(rqs[i],p))
 | 
			
		||||
        for i, pth in enumerate(propagatedpths):
 | 
			
		||||
            result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i]))
 | 
			
		||||
        temp = path_result_json(result)
 | 
			
		||||
        fnamecsv = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.csv'
 | 
			
		||||
        fnamejson = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.json'
 | 
			
		||||
        with open(fnamejson, 'w', encoding='utf-8') as f:
 | 
			
		||||
            f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False))
 | 
			
		||||
            with open(fnamecsv,"w", encoding='utf-8') as fcsv :
 | 
			
		||||
                jsontocsv(temp,equipment,fcsv)
 | 
			
		||||
                print('\x1b[1;34;40m'+f'saving in {args.output} and {fnamecsv}'+ '\x1b[0m')
 | 
			
		||||
        fnamecsv = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.csv'
 | 
			
		||||
        fnamejson = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.json'
 | 
			
		||||
        with open(fnamejson, 'w', encoding='utf-8') as fjson:
 | 
			
		||||
            fjson.write(dumps(path_result_json(result), indent=2, ensure_ascii=False))
 | 
			
		||||
            with open(fnamecsv, "w", encoding='utf-8') as fcsv:
 | 
			
		||||
                jsontocsv(temp, equipment, fcsv)
 | 
			
		||||
                print('\x1b[1;34;40m'+f'saving in {ARGS.output} and {fnamecsv}'+ '\x1b[0m')
 | 
			
		||||
 | 
			
		||||
class GnpyAPI(Resource):
 | 
			
		||||
    """ Compute requests using network, data and equipment with rest api
 | 
			
		||||
    """
 | 
			
		||||
    def get(self):
 | 
			
		||||
        return {"ping": True}, 200
 | 
			
		||||
 | 
			
		||||
    def post(self):
 | 
			
		||||
        data = request.get_json()
 | 
			
		||||
        equipment = load_equipment('examples/2019-demo-equipment.json')
 | 
			
		||||
        topo_json = load_json('examples/2019-demo-topology.json')
 | 
			
		||||
        network = network_from_json(topo_json, equipment)
 | 
			
		||||
        try:
 | 
			
		||||
            propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment)
 | 
			
		||||
            # Generate the output
 | 
			
		||||
            result = []
 | 
			
		||||
            #assumes that list of rqs and list of propgatedpths have same order
 | 
			
		||||
            for i, pth in enumerate(propagatedpths):
 | 
			
		||||
                result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i]))
 | 
			
		||||
 | 
			
		||||
            return {"result":path_result_json(result)}, 201
 | 
			
		||||
        except ServiceError as this_e:
 | 
			
		||||
            msg = f'Service error: {this_e}'
 | 
			
		||||
            return {"result": msg}, 400
 | 
			
		||||
 | 
			
		||||
API.add_resource(GnpyAPI, '/gnpy-experimental')
 | 
			
		||||
 | 
			
		||||
def main(args):
 | 
			
		||||
    """ main function that calls all functions
 | 
			
		||||
    """
 | 
			
		||||
    LOGGER.info(f'Computing path requests {args.service_filename} into JSON format')
 | 
			
		||||
    print('\x1b[1;34;40m' +\
 | 
			
		||||
          f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m')
 | 
			
		||||
    # for debug
 | 
			
		||||
    # print( args.eqpt_filename)
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        data = load_requests(args.service_filename, args.eqpt_filename, args.bidir)
 | 
			
		||||
        equipment = load_equipment(args.eqpt_filename)
 | 
			
		||||
        network = load_network(args.network_filename, equipment)
 | 
			
		||||
    except EquipmentConfigError as this_e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {this_e}')
 | 
			
		||||
        exit(1)
 | 
			
		||||
    except NetworkTopologyError as this_e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {this_e}')
 | 
			
		||||
        exit(1)
 | 
			
		||||
    except ConfigurationError as this_e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {this_e}')
 | 
			
		||||
        exit(1)
 | 
			
		||||
    except ServiceError as this_e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
 | 
			
		||||
        exit(1)
 | 
			
		||||
    # input_str = raw_input("How will you use your program: c:[cli] , a:[api] ?")
 | 
			
		||||
    # print(input_str)
 | 
			
		||||
    #
 | 
			
		||||
    if ((args.rest == 1) and (args.output is None)):
 | 
			
		||||
        print('you have chosen the rest mode')
 | 
			
		||||
        APP.run(host='0.0.0.0', port=5000, debug=True)
 | 
			
		||||
    elif ((args.rest > 1) or ((args.rest == 1) and (args.output is not None))):
 | 
			
		||||
        print('command is not well formulated')
 | 
			
		||||
    else:
 | 
			
		||||
        launch_cli(network, data, equipment)
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    ARGS = PARSER.parse_args()
 | 
			
		||||
    basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(ARGS.verbose, DEBUG))
 | 
			
		||||
    main(ARGS)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										98
									
								
								examples/raman_edfa_example_network.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								examples/raman_edfa_example_network.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
			
		||||
{
 | 
			
		||||
  "elements": [
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Site_A",
 | 
			
		||||
      "type": "Transceiver",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": "Site A",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span1",
 | 
			
		||||
      "type": "RamanFiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "temperature": 283,
 | 
			
		||||
        "raman_pumps": [
 | 
			
		||||
          {
 | 
			
		||||
            "power": 200e-3,
 | 
			
		||||
            "frequency": 205e12,
 | 
			
		||||
            "propagation_direction": "counterprop"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "power": 206e-3,
 | 
			
		||||
            "frequency": 201e12,
 | 
			
		||||
            "propagation_direction": "counterprop"
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Edfa1",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 15.0,
 | 
			
		||||
        "delta_p": -2,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Site_B",
 | 
			
		||||
      "type": "Transceiver",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": "Site B",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "connections": [
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Site_A",
 | 
			
		||||
      "to_node": "Span1"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Span1",
 | 
			
		||||
      "to_node": "Edfa1"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Edfa1",
 | 
			
		||||
      "to_node": "Site_B"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										180
									
								
								examples/serviceDemov1.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								examples/serviceDemov1.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
			
		||||
{
 | 
			
		||||
  "path-request": [
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "0",
 | 
			
		||||
      "source": "trx site_a",
 | 
			
		||||
      "destination": "trx site_b",
 | 
			
		||||
      "src-tp-id": "trx site_a",
 | 
			
		||||
      "dst-tp-id": "trx site_b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager",
 | 
			
		||||
          "trx_mode": null,
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 100000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "1",
 | 
			
		||||
      "source": "trx site_a",
 | 
			
		||||
      "destination": "trx site_b",
 | 
			
		||||
      "src-tp-id": "trx site_a",
 | 
			
		||||
      "dst-tp-id": "trx site_b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager",
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 200000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "Span1ab",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "STRICT"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "2",
 | 
			
		||||
      "source": "trx site_a",
 | 
			
		||||
      "destination": "trx site_b",
 | 
			
		||||
      "src-tp-id": "trx site_a",
 | 
			
		||||
      "dst-tp-id": "trx site_b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager",
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 200000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm site_c",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "STRICT"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "3",
 | 
			
		||||
      "source": "trx site_a",
 | 
			
		||||
      "destination": "trx site_b",
 | 
			
		||||
      "src-tp-id": "trx site_a",
 | 
			
		||||
      "dst-tp-id": "trx site_b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager",
 | 
			
		||||
          "trx_mode": null,
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 100000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "4",
 | 
			
		||||
      "source": "trx site_a",
 | 
			
		||||
      "destination": "trx site_b",
 | 
			
		||||
      "src-tp-id": "trx site_a",
 | 
			
		||||
      "dst-tp-id": "trx site_b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager",
 | 
			
		||||
          "trx_mode": null,
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 100000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "synchronization": [
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "x",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "false",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "3",
 | 
			
		||||
          "0"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "y",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "false",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "4",
 | 
			
		||||
          "3",
 | 
			
		||||
          "0"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								examples/sim_params.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								examples/sim_params.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
{
 | 
			
		||||
  "raman_computed_channels": [1, 18, 37, 56, 75],
 | 
			
		||||
  "raman_parameters": {
 | 
			
		||||
    "flag_raman": true,
 | 
			
		||||
    "space_resolution": 10e3,
 | 
			
		||||
    "tolerance": 1e-8
 | 
			
		||||
  },
 | 
			
		||||
  "nli_parameters": {
 | 
			
		||||
  	"nli_method_name": "ggn_spectrally_separated",
 | 
			
		||||
  	"wdm_grid_size": 50e9,
 | 
			
		||||
  	"dispersion_tolerance": 1,
 | 
			
		||||
  	"phase_shift_tollerance": 0.1
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,8 @@
 | 
			
		||||
      0.0359549,
 | 
			
		||||
      5.82851
 | 
			
		||||
      ],
 | 
			
		||||
      "f_min": 191.35e12,
 | 
			
		||||
      "f_max": 196.1e12,
 | 
			
		||||
      "nf_ripple": [
 | 
			
		||||
      -0.3110761646066259,
 | 
			
		||||
      -0.3110761646066259,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										703
									
								
								examples/topoDemov1.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										703
									
								
								examples/topoDemov1.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,703 @@
 | 
			
		||||
{
 | 
			
		||||
  "elements": [
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx site_a",
 | 
			
		||||
      "type": "Transceiver",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": "Site a",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm site_a",
 | 
			
		||||
      "type": "Roadm",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "target_pch_out_db": -20,
 | 
			
		||||
        "restrictions": {
 | 
			
		||||
          "preamp_variety_list": [],
 | 
			
		||||
          "booster_variety_list": []
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": "Site a",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span1ab",
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 100.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span1ba",
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 100.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span2ab",
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span2ba",
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm site_b",
 | 
			
		||||
      "type": "Roadm",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "target_pch_out_db": -20,
 | 
			
		||||
        "restrictions": {
 | 
			
		||||
          "preamp_variety_list": [],
 | 
			
		||||
          "booster_variety_list": []
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": "Site b",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx site_b",
 | 
			
		||||
      "type": "Transceiver",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": "Site b",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "booster1 site_a",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_medium_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 19.0,
 | 
			
		||||
        "delta_p": -1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site a",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "preamp site_b",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 18.0,
 | 
			
		||||
        "delta_p": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site b",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "booster1 site_b",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_medium_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 19.0,
 | 
			
		||||
        "delta_p": -1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site b",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "preamp1 site_a",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 18.0,
 | 
			
		||||
        "delta_p": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site_a",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "booster2 site_a",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_medium_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 19.0,
 | 
			
		||||
        "delta_p": -1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site a",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "preamp2 site_b",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 18.0,
 | 
			
		||||
        "delta_p": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site_b",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "booster2 site_b",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_medium_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 19.0,
 | 
			
		||||
        "delta_p": -1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site b",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "preamp2 site_a",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 18.0,
 | 
			
		||||
        "delta_p": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site_a",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "booster3 site_a",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_medium_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 19.0,
 | 
			
		||||
        "delta_p": -1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site a",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "preamp3 site_b",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 18.0,
 | 
			
		||||
        "delta_p": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site_b",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "booster3 site_b",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_medium_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 19.0,
 | 
			
		||||
        "delta_p": -1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site b",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "preamp3 site_a",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 18.0,
 | 
			
		||||
        "delta_p": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site_a",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm site_c",
 | 
			
		||||
      "type": "Roadm",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "target_pch_out_db": -20,
 | 
			
		||||
        "restrictions": {
 | 
			
		||||
          "preamp_variety_list": [],
 | 
			
		||||
          "booster_variety_list": []
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": "Site c",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "booster1 site_c",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_medium_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 19.0,
 | 
			
		||||
        "delta_p": -1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site c",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "preamp1 site_c",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 18.0,
 | 
			
		||||
        "delta_p": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site_c",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "booster2 site_c",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_medium_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 19.0,
 | 
			
		||||
        "delta_p": -1.0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site c",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "preamp2 site_c",
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "std_low_gain",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 18.0,
 | 
			
		||||
        "delta_p": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0,
 | 
			
		||||
          "city": "Site_c",
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span1ac",
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span1ca",
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span1bc",
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span1cb",
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "connections": [
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx site_a",
 | 
			
		||||
      "to_node": "roadm site_a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm site_a",
 | 
			
		||||
      "to_node": "booster1 site_a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "booster1 site_a",
 | 
			
		||||
      "to_node": "Span1ab"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Span1ab",
 | 
			
		||||
      "to_node": "preamp site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "preamp site_b",
 | 
			
		||||
      "to_node": "roadm site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm site_b",
 | 
			
		||||
      "to_node": "trx site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm site_a",
 | 
			
		||||
      "to_node": "booster2 site_a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "booster2 site_a",
 | 
			
		||||
      "to_node": "Span2ab"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Span2ab",
 | 
			
		||||
      "to_node": "preamp2 site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "preamp2 site_b",
 | 
			
		||||
      "to_node": "roadm site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm site_b",
 | 
			
		||||
      "to_node": "booster1 site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "booster1 site_b",
 | 
			
		||||
      "to_node": "Span1ba"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Span1ba",
 | 
			
		||||
      "to_node": "preamp1 site_a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "preamp1 site_a",
 | 
			
		||||
      "to_node": "roadm site_a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm site_b",
 | 
			
		||||
      "to_node": "booster2 site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "booster2 site_b",
 | 
			
		||||
      "to_node": "Span2ba"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Span2ba",
 | 
			
		||||
      "to_node": "preamp2 site_a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "preamp2 site_a",
 | 
			
		||||
      "to_node": "roadm site_a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm site_a",
 | 
			
		||||
      "to_node": "booster3 site_a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "booster3 site_a",
 | 
			
		||||
      "to_node": "Span1ac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Span1ac",
 | 
			
		||||
      "to_node": "preamp1 site_c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "preamp1 site_c",
 | 
			
		||||
      "to_node": "roadm site_c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm site_c",
 | 
			
		||||
      "to_node": "booster1 site_c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "booster1 site_c",
 | 
			
		||||
      "to_node": "Span1cb"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Span1cb",
 | 
			
		||||
      "to_node": "preamp3 site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "preamp3 site_b",
 | 
			
		||||
      "to_node": "roadm site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm site_b",
 | 
			
		||||
      "to_node": "booster3 site_b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "booster3 site_b",
 | 
			
		||||
      "to_node": "Span1bc"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Span1bc",
 | 
			
		||||
      "to_node": "preamp2 site_c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "preamp2 site_c",
 | 
			
		||||
      "to_node": "roadm site_c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm site_c",
 | 
			
		||||
      "to_node": "booster2 site_c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "booster2 site_c",
 | 
			
		||||
      "to_node": "Span1ca"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Span1ca",
 | 
			
		||||
      "to_node": "preamp3 site_a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "preamp3 site_a",
 | 
			
		||||
      "to_node": "roadm site_a"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -18,14 +18,16 @@ from pathlib import Path
 | 
			
		||||
from json import loads
 | 
			
		||||
from collections import Counter
 | 
			
		||||
from logging import getLogger, basicConfig, INFO, ERROR, DEBUG
 | 
			
		||||
from numpy import linspace, mean
 | 
			
		||||
from numpy import linspace, mean, log10
 | 
			
		||||
from matplotlib.pyplot import show, axis, figure, title, text
 | 
			
		||||
from networkx import (draw_networkx_nodes, draw_networkx_edges,
 | 
			
		||||
                      draw_networkx_labels, dijkstra_path)
 | 
			
		||||
from gnpy.core.network import load_network, build_network, save_network
 | 
			
		||||
from gnpy.core.elements import Transceiver, Fiber, Edfa, Roadm
 | 
			
		||||
from gnpy.core.network import load_network, build_network, save_network, load_sim_params, configure_network
 | 
			
		||||
from gnpy.core.elements import Transceiver, Fiber, RamanFiber, Edfa, Roadm
 | 
			
		||||
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
 | 
			
		||||
from gnpy.core.request import Path_request, RequestParams, compute_constrained_path, propagate2
 | 
			
		||||
from gnpy.core.exceptions import ConfigurationError, EquipmentConfigError, NetworkTopologyError
 | 
			
		||||
import gnpy.core.ansi_escapes as ansi_escapes
 | 
			
		||||
 | 
			
		||||
logger = getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
@@ -97,7 +99,7 @@ def plot_results(network, path, source, destination, infos):
 | 
			
		||||
    show()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(network, equipment, source, destination, req = None):
 | 
			
		||||
def main(network, equipment, source, destination, sim_params, req=None):
 | 
			
		||||
    result_dicts = {}
 | 
			
		||||
    network_data = [{
 | 
			
		||||
                    'network_name'  : str(args.filename),
 | 
			
		||||
@@ -106,8 +108,8 @@ def main(network, equipment, source, destination, req = None):
 | 
			
		||||
                    }]
 | 
			
		||||
    result_dicts.update({'network': network_data})
 | 
			
		||||
    design_data = [{
 | 
			
		||||
                    'power_mode'        : equipment['Spans']['default'].power_mode,
 | 
			
		||||
                    'span_power_range'  : equipment['Spans']['default'].delta_power_range_db,
 | 
			
		||||
                    'power_mode'        : equipment['Span']['default'].power_mode,
 | 
			
		||||
                    'span_power_range'  : equipment['Span']['default'].delta_power_range_db,
 | 
			
		||||
                    'design_pch'        : equipment['SI']['default'].power_dbm,
 | 
			
		||||
                    'baud_rate'         : equipment['SI']['default'].baud_rate
 | 
			
		||||
                    }]
 | 
			
		||||
@@ -115,17 +117,23 @@ def main(network, equipment, source, destination, req = None):
 | 
			
		||||
    simulation_data = []
 | 
			
		||||
    result_dicts.update({'simulation results': simulation_data})
 | 
			
		||||
 | 
			
		||||
    power_mode = equipment['Spans']['default'].power_mode
 | 
			
		||||
    power_mode = equipment['Span']['default'].power_mode
 | 
			
		||||
    print('\n'.join([f'Power mode is set to {power_mode}',
 | 
			
		||||
                     f'=> it can be modified in eqpt_config.json - Spans']))
 | 
			
		||||
                     f'=> it can be modified in eqpt_config.json - Span']))
 | 
			
		||||
 | 
			
		||||
    pref_ch_db = lin2db(req.power*1e3) #reference channel power / span (SL=20dB)
 | 
			
		||||
    pref_total_db = pref_ch_db + lin2db(req.nb_channel) #reference total power / span (SL=20dB)
 | 
			
		||||
    build_network(network, equipment, pref_ch_db, pref_total_db)
 | 
			
		||||
    path = compute_constrained_path(network, req)
 | 
			
		||||
 | 
			
		||||
    spans = [s.length for s in path if isinstance(s, Fiber)]
 | 
			
		||||
    print(f'\nThere are {len(spans)} fiber spans over {sum(spans):.0f}m between {source.uid} and {destination.uid}')
 | 
			
		||||
    if len([s.length for s in path if isinstance(s, RamanFiber)]):
 | 
			
		||||
        if sim_params is None:
 | 
			
		||||
            print(f'{ansi_escapes.red}Invocation error:{ansi_escapes.reset} RamanFiber requires passing simulation params via --sim-params')
 | 
			
		||||
            exit(1)
 | 
			
		||||
        configure_network(network, sim_params)
 | 
			
		||||
 | 
			
		||||
    spans = [s.length for s in path if isinstance(s, RamanFiber) or isinstance(s, Fiber)]
 | 
			
		||||
    print(f'\nThere are {len(spans)} fiber spans over {sum(spans)/1000:.0f} km between {source.uid} and {destination.uid}')
 | 
			
		||||
    print(f'\nNow propagating between {source.uid} and {destination.uid}:')
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
@@ -136,25 +144,47 @@ def main(network, equipment, source, destination, req = None):
 | 
			
		||||
        print('invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]')
 | 
			
		||||
        power_range = [0]
 | 
			
		||||
 | 
			
		||||
    if not power_mode:
 | 
			
		||||
        #power cannot be changed in gain mode
 | 
			
		||||
        power_range = [0]
 | 
			
		||||
    for dp_db in power_range:
 | 
			
		||||
        req.power = db2lin(pref_ch_db + dp_db)*1e-3
 | 
			
		||||
        print(f'\nPropagating with input power = {lin2db(req.power*1e3):.2f}dBm :')
 | 
			
		||||
        infos = propagate2(path, req, equipment, show=len(power_range)==1)
 | 
			
		||||
        print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :')
 | 
			
		||||
        print(destination)
 | 
			
		||||
        if power_mode:
 | 
			
		||||
            print(f'\nPropagating with input power = {ansi_escapes.cyan}{lin2db(req.power*1e3):.2f} dBm{ansi_escapes.reset}:')
 | 
			
		||||
        else:
 | 
			
		||||
            print(f'\nPropagating in {ansi_escapes.cyan}gain mode{ansi_escapes.reset}: power cannot be set manually')
 | 
			
		||||
        infos = propagate2(path, req, equipment)
 | 
			
		||||
        if len(power_range) == 1:
 | 
			
		||||
            for elem in path:
 | 
			
		||||
                print(elem)
 | 
			
		||||
            if power_mode:
 | 
			
		||||
                print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:')
 | 
			
		||||
            else:
 | 
			
		||||
                print(f'\nTransmission results:')
 | 
			
		||||
            print(f'  Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}')
 | 
			
		||||
        else:
 | 
			
		||||
            print(path[-1])
 | 
			
		||||
 | 
			
		||||
        #print(f'\n !!!!!!!!!!!!!!!!!     TEST POINT         !!!!!!!!!!!!!!!!!!!!!')
 | 
			
		||||
        #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}')
 | 
			
		||||
        # => use "in" or "out" parameter
 | 
			
		||||
        # => use "nli" or "ase" or "signal" or "total" parameter
 | 
			
		||||
 | 
			
		||||
        simulation_data.append({
 | 
			
		||||
                    'Pch_dBm'               : pref_ch_db + dp_db,
 | 
			
		||||
                    'OSNR_ASE_0.1nm'        : round(mean(destination.osnr_ase_01nm),2),
 | 
			
		||||
                    'OSNR_ASE_signal_bw'    : round(mean(destination.osnr_ase),2),
 | 
			
		||||
                    'SNR_nli_signal_bw'     : round(mean(destination.osnr_nli),2),
 | 
			
		||||
                    'SNR_total_signal_bw'   : round(mean(destination.snr),2)
 | 
			
		||||
                            })
 | 
			
		||||
        # => use "nli" or "ase" or "signal" or "total" parameter        
 | 
			
		||||
        if power_mode:
 | 
			
		||||
            simulation_data.append({
 | 
			
		||||
                        'Pch_dBm'               : pref_ch_db + dp_db,
 | 
			
		||||
                        'OSNR_ASE_0.1nm'        : round(mean(destination.osnr_ase_01nm),2),
 | 
			
		||||
                        'OSNR_ASE_signal_bw'    : round(mean(destination.osnr_ase),2),
 | 
			
		||||
                        'SNR_nli_signal_bw'     : round(mean(destination.osnr_nli),2),
 | 
			
		||||
                        'SNR_total_signal_bw'   : round(mean(destination.snr),2)
 | 
			
		||||
                                })
 | 
			
		||||
        else:
 | 
			
		||||
            simulation_data.append({
 | 
			
		||||
                        'gain_mode'             : 'power canot be set',
 | 
			
		||||
                        'OSNR_ASE_0.1nm'        : round(mean(destination.osnr_ase_01nm),2),
 | 
			
		||||
                        'OSNR_ASE_signal_bw'    : round(mean(destination.osnr_ase),2),
 | 
			
		||||
                        'SNR_nli_signal_bw'     : round(mean(destination.osnr_nli),2),
 | 
			
		||||
                        'SNR_total_signal_bw'   : round(mean(destination.snr),2)
 | 
			
		||||
                                })
 | 
			
		||||
    write_csv(result_dicts, 'simulation_result.csv')
 | 
			
		||||
    return path, infos
 | 
			
		||||
 | 
			
		||||
@@ -162,6 +192,9 @@ def main(network, equipment, source, destination, req = None):
 | 
			
		||||
parser = ArgumentParser()
 | 
			
		||||
parser.add_argument('-e', '--equipment', type=Path,
 | 
			
		||||
                    default=Path(__file__).parent / 'eqpt_config.json')
 | 
			
		||||
parser.add_argument('--sim-params', type=Path,
 | 
			
		||||
                    default=None, help='Path to the JSON containing simulation parameters (required for Raman)')
 | 
			
		||||
parser.add_argument('--show-channels', action='store_true', help='Show final per-channel OSNR summary')
 | 
			
		||||
parser.add_argument('-pl', '--plot', action='store_true')
 | 
			
		||||
parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence')
 | 
			
		||||
parser.add_argument('-l', '--list-nodes', action='store_true', help='list all transceiver nodes')
 | 
			
		||||
@@ -177,8 +210,19 @@ if __name__ == '__main__':
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
    basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, DEBUG))
 | 
			
		||||
 | 
			
		||||
    equipment = load_equipment(args.equipment)
 | 
			
		||||
    network = load_network(args.filename, equipment, args.names_matching)
 | 
			
		||||
    try:
 | 
			
		||||
        equipment = load_equipment(args.equipment)
 | 
			
		||||
        network = load_network(args.filename, equipment, args.names_matching)
 | 
			
		||||
        sim_params = load_sim_params(args.sim_params) if args.sim_params is not None else None
 | 
			
		||||
    except EquipmentConfigError as e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {e}')
 | 
			
		||||
        exit(1)
 | 
			
		||||
    except NetworkTopologyError as e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}')
 | 
			
		||||
        exit(1)
 | 
			
		||||
    except ConfigurationError as e:
 | 
			
		||||
        print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}')
 | 
			
		||||
        exit(1)
 | 
			
		||||
 | 
			
		||||
    if args.plot:
 | 
			
		||||
        plot_baseline(network)
 | 
			
		||||
@@ -238,6 +282,7 @@ if __name__ == '__main__':
 | 
			
		||||
    params['trx_mode'] = ''
 | 
			
		||||
    params['source'] = source.uid
 | 
			
		||||
    params['destination'] = destination.uid
 | 
			
		||||
    params['bidir'] = False
 | 
			
		||||
    params['nodes_list'] = [destination.uid]
 | 
			
		||||
    params['loose_list'] = ['strict']
 | 
			
		||||
    params['format'] = ''
 | 
			
		||||
@@ -247,9 +292,19 @@ if __name__ == '__main__':
 | 
			
		||||
        trx_params['power'] = db2lin(float(args.power))*1e-3
 | 
			
		||||
    params.update(trx_params)
 | 
			
		||||
    req = Path_request(**params)
 | 
			
		||||
    path, infos = main(network, equipment, source, destination, req)
 | 
			
		||||
    path, infos = main(network, equipment, source, destination, sim_params, req)
 | 
			
		||||
    save_network(args.filename, network)
 | 
			
		||||
 | 
			
		||||
    if args.show_channels:
 | 
			
		||||
        print('\nThe total SNR per channel at the end of the line is:')
 | 
			
		||||
        print('{:>5}{:>26}{:>26}{:>28}{:>28}{:>28}' \
 | 
			
		||||
            .format('Ch. #', 'Channel frequency (THz)', 'Channel power (dBm)', 'OSNR ASE (signal bw, dB)', 'SNR NLI (signal bw, dB)', 'SNR total (signal bw, dB)'))
 | 
			
		||||
        for final_carrier, ch_osnr, ch_snr_nl, ch_snr in zip(infos[path[-1]][1].carriers, path[-1].osnr_ase, path[-1].osnr_nli, path[-1].snr):
 | 
			
		||||
            ch_freq = final_carrier.frequency * 1e-12
 | 
			
		||||
            ch_power = lin2db(final_carrier.power.signal*1e3)
 | 
			
		||||
            print('{:5}{:26.2f}{:26.2f}{:28.2f}{:28.2f}{:28.2f}' \
 | 
			
		||||
                .format(final_carrier.channel_number, round(ch_freq, 2), round(ch_power, 2), round(ch_osnr, 2), round(ch_snr_nl, 2), round(ch_snr, 2)))
 | 
			
		||||
 | 
			
		||||
    if not args.source:
 | 
			
		||||
        print(f'\n(No source node specified: picked {source.uid})')
 | 
			
		||||
    elif not valid_source:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								gnpy/core/ansi_escapes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								gnpy/core/ansi_escapes.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
gnpy.core.ansi_escapes
 | 
			
		||||
======================
 | 
			
		||||
 | 
			
		||||
A random subset of ANSI terminal escape codes for colored messages
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
red = '\x1b[1;31;40m'
 | 
			
		||||
cyan = '\x1b[1;36;40m'
 | 
			
		||||
reset = '\x1b[0m'
 | 
			
		||||
@@ -31,6 +31,8 @@ from itertools import chain
 | 
			
		||||
from json import dumps
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from difflib import get_close_matches
 | 
			
		||||
from gnpy.core.utils import silent_remove
 | 
			
		||||
from gnpy.core.exceptions import NetworkTopologyError
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
 | 
			
		||||
@@ -54,7 +56,9 @@ class Node(object):
 | 
			
		||||
        'region':       '',
 | 
			
		||||
        'latitude':     0,
 | 
			
		||||
        'longitude':    0,
 | 
			
		||||
        'node_type':    'ILA'
 | 
			
		||||
        'node_type':    'ILA',
 | 
			
		||||
        'booster_restriction' : '',
 | 
			
		||||
        'preamp_restriction'  : ''
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
class Link(object):
 | 
			
		||||
@@ -111,11 +115,12 @@ class Eqpt(object):
 | 
			
		||||
    {
 | 
			
		||||
            'from_city':        '',
 | 
			
		||||
            'to_city':          '',
 | 
			
		||||
            'east_amp_type':         '',
 | 
			
		||||
            'east_att_in':           0,
 | 
			
		||||
            'east_amp_gain':         0,
 | 
			
		||||
            'east_tilt':             0,
 | 
			
		||||
            'east_att_out':          0
 | 
			
		||||
            'east_amp_type':    '',
 | 
			
		||||
            'east_att_in':      0,
 | 
			
		||||
            'east_amp_gain':    None,
 | 
			
		||||
            'east_amp_dp':      None,
 | 
			
		||||
            'east_tilt':        0,
 | 
			
		||||
            'east_att_out':     None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -163,8 +168,8 @@ def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in):
 | 
			
		||||
            slice_out = read_slice(my_sheet, start_line+iteration, slice_in, h0)
 | 
			
		||||
            iteration += 1
 | 
			
		||||
        if slice_out == (-1, -1):
 | 
			
		||||
            if h0 == 'east':
 | 
			
		||||
                print(f'\x1b[1;31;40m'+f'CRITICAL: missing _east_ header above other headers (hierarchical) _ ABORT'+ '\x1b[0m')
 | 
			
		||||
            if h0 in ('east', 'Node A', 'Node Z', 'City') :
 | 
			
		||||
                print(f'\x1b[1;31;40m'+f'CRITICAL: missing _{h0}_ header: EXECUTION ENDS'+ '\x1b[0m')
 | 
			
		||||
                exit()
 | 
			
		||||
            else:
 | 
			
		||||
                print(f'missing header {h0}')
 | 
			
		||||
@@ -234,7 +239,6 @@ def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):
 | 
			
		||||
 | 
			
		||||
def convert_file(input_filename, names_matching=False, filter_region=[]):
 | 
			
		||||
    nodes, links, eqpts = parse_excel(input_filename)
 | 
			
		||||
 | 
			
		||||
    if filter_region:
 | 
			
		||||
        nodes = [n for n in nodes if n.region.lower() in filter_region]
 | 
			
		||||
        cities = {n.city for n in nodes}
 | 
			
		||||
@@ -243,10 +247,8 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
 | 
			
		||||
        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]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    global nodes_by_city
 | 
			
		||||
    nodes_by_city = {n.city: n for n in nodes}
 | 
			
		||||
 | 
			
		||||
    #create matching dictionary for node name mismatch analysis
 | 
			
		||||
 | 
			
		||||
    cities = {''.join(c.strip() for c in n.city.split('C+L')).lower(): n.city for n in nodes}
 | 
			
		||||
@@ -297,7 +299,22 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
 | 
			
		||||
                                        'latitude':  x.latitude,
 | 
			
		||||
                                        'longitude': x.longitude}},
 | 
			
		||||
              'type': 'Roadm'}
 | 
			
		||||
             for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] +
 | 
			
		||||
             for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' \
 | 
			
		||||
                 and x.booster_restriction == '' and x.preamp_restriction == ''] +
 | 
			
		||||
            [{'uid': f'roadm {x.city}',
 | 
			
		||||
              'params' : {
 | 
			
		||||
                'restrictions': {
 | 
			
		||||
                  'preamp_variety_list': silent_remove(x.preamp_restriction.split(' | '),''),
 | 
			
		||||
                  'booster_variety_list': silent_remove(x.booster_restriction.split(' | '),'')
 | 
			
		||||
                  }
 | 
			
		||||
              },
 | 
			
		||||
              'metadata': {'location': {'city':      x.city,
 | 
			
		||||
                                        'region':    x.region,
 | 
			
		||||
                                        'latitude':  x.latitude,
 | 
			
		||||
                                        'longitude': x.longitude}},
 | 
			
		||||
              'type': 'Roadm'}
 | 
			
		||||
             for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' and \
 | 
			
		||||
                 (x.booster_restriction != '' or x.preamp_restriction != '')] +
 | 
			
		||||
            [{'uid': f'west fused spans in {x.city}',
 | 
			
		||||
              'metadata': {'location': {'city':      x.city,
 | 
			
		||||
                                        'region':    x.region,
 | 
			
		||||
@@ -344,10 +361,12 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
 | 
			
		||||
              'type': 'Edfa',
 | 
			
		||||
              'type_variety': e.east_amp_type,
 | 
			
		||||
              'operational': {'gain_target': e.east_amp_gain,
 | 
			
		||||
                              'delta_p':     e.east_amp_dp,
 | 
			
		||||
                              'tilt_target': e.east_tilt,
 | 
			
		||||
                              'out_voa'    : e.east_att_out}
 | 
			
		||||
            }
 | 
			
		||||
             for e in eqpts if e.east_amp_type.lower() != ''] +
 | 
			
		||||
             }
 | 
			
		||||
             for e in eqpts if (e.east_amp_type.lower() != '' and \
 | 
			
		||||
                                e.east_amp_type.lower() != 'fused')] +
 | 
			
		||||
            [{'uid': f'west edfa in {e.from_city} to {e.to_city}',
 | 
			
		||||
              'metadata': {'location': {'city':      nodes_by_city[e.from_city].city,
 | 
			
		||||
                                        'region':    nodes_by_city[e.from_city].region,
 | 
			
		||||
@@ -356,10 +375,34 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
 | 
			
		||||
              'type': 'Edfa',
 | 
			
		||||
              'type_variety': e.west_amp_type,
 | 
			
		||||
              'operational': {'gain_target': e.west_amp_gain,
 | 
			
		||||
                              'delta_p':     e.west_amp_dp,
 | 
			
		||||
                              'tilt_target': e.west_tilt,
 | 
			
		||||
                              'out_voa'    : e.west_att_out}
 | 
			
		||||
              }
 | 
			
		||||
             for e in eqpts if e.west_amp_type.lower() != ''],
 | 
			
		||||
             }
 | 
			
		||||
             for e in eqpts if (e.west_amp_type.lower() != '' and \
 | 
			
		||||
                                e.west_amp_type.lower() != 'fused')] +
 | 
			
		||||
            # fused edfa variety is a hack to indicate that there should not be
 | 
			
		||||
            # booster amplifier out the roadm.
 | 
			
		||||
            # If user specifies ILA in Nodes sheet and fused in Eqpt sheet, then assumes that
 | 
			
		||||
            # this is a fused nodes.
 | 
			
		||||
            [{'uid': f'east edfa in {e.from_city} to {e.to_city}',
 | 
			
		||||
              'metadata': {'location': {'city':      nodes_by_city[e.from_city].city,
 | 
			
		||||
                                        'region':    nodes_by_city[e.from_city].region,
 | 
			
		||||
                                        'latitude':  nodes_by_city[e.from_city].latitude,
 | 
			
		||||
                                        'longitude': nodes_by_city[e.from_city].longitude}},
 | 
			
		||||
              'type': 'Fused',
 | 
			
		||||
              'params': {'loss': 0}
 | 
			
		||||
             }
 | 
			
		||||
             for e in eqpts if e.east_amp_type.lower() == 'fused'] +
 | 
			
		||||
            [{'uid': f'west edfa in {e.from_city} to {e.to_city}',
 | 
			
		||||
              'metadata': {'location': {'city':      nodes_by_city[e.from_city].city,
 | 
			
		||||
                                        'region':    nodes_by_city[e.from_city].region,
 | 
			
		||||
                                        'latitude':  nodes_by_city[e.from_city].latitude,
 | 
			
		||||
                                        'longitude': nodes_by_city[e.from_city].longitude}},
 | 
			
		||||
              'type': 'Fused',
 | 
			
		||||
              'params': {'loss': 0}
 | 
			
		||||
             }
 | 
			
		||||
             for e in eqpts if e.west_amp_type.lower() == 'fused'],
 | 
			
		||||
        'connections':
 | 
			
		||||
            list(chain.from_iterable([eqpt_connection_by_city(n.city)
 | 
			
		||||
            for n in nodes]))
 | 
			
		||||
@@ -411,7 +454,9 @@ def parse_excel(input_filename):
 | 
			
		||||
        'Region':       'region',
 | 
			
		||||
        'Latitude':     'latitude',
 | 
			
		||||
        'Longitude':    'longitude',
 | 
			
		||||
        'Type':         'node_type'
 | 
			
		||||
        'Type':         'node_type',
 | 
			
		||||
        'Booster_restriction': 'booster_restriction',
 | 
			
		||||
        'Preamp_restriction': 'preamp_restriction'
 | 
			
		||||
    }
 | 
			
		||||
    eqpt_headers = \
 | 
			
		||||
    {  'Node A': 'from_city',
 | 
			
		||||
@@ -420,6 +465,7 @@ def parse_excel(input_filename):
 | 
			
		||||
            'amp type':         'east_amp_type',
 | 
			
		||||
            'att_in':           'east_att_in',
 | 
			
		||||
            'amp gain':         'east_amp_gain',
 | 
			
		||||
            'delta p':          'east_amp_dp',
 | 
			
		||||
            'tilt':             'east_tilt',
 | 
			
		||||
            'att_out':          'east_att_out'
 | 
			
		||||
       },
 | 
			
		||||
@@ -427,6 +473,7 @@ def parse_excel(input_filename):
 | 
			
		||||
            'amp type':         'west_amp_type',
 | 
			
		||||
            'att_in':           'west_att_in',
 | 
			
		||||
            'amp gain':         'west_amp_gain',
 | 
			
		||||
            'delta p':          'west_amp_dp',
 | 
			
		||||
            'tilt':             'west_tilt',
 | 
			
		||||
            'att_out':          'west_att_out'
 | 
			
		||||
       }
 | 
			
		||||
@@ -462,10 +509,13 @@ def parse_excel(input_filename):
 | 
			
		||||
    # sanity check
 | 
			
		||||
    all_cities = Counter(n.city for n in nodes)
 | 
			
		||||
    if len(all_cities) != len(nodes):
 | 
			
		||||
        ValueError(f'Duplicate city: {all_cities}')
 | 
			
		||||
    if any(ln.from_city not in all_cities or
 | 
			
		||||
           ln.to_city   not in all_cities for ln in links):
 | 
			
		||||
        ValueError(f'Bad link.')
 | 
			
		||||
        raise ValueError(f'Duplicate city: {all_cities}')
 | 
			
		||||
    bad_links = []
 | 
			
		||||
    for lnk in links:
 | 
			
		||||
        if lnk.from_city not in all_cities or lnk.to_city not in all_cities:
 | 
			
		||||
            bad_links.append([lnk.from_city, lnk.to_city])
 | 
			
		||||
    if bad_links:
 | 
			
		||||
        raise NetworkTopologyError(f'Bad link(s): {bad_links}.')
 | 
			
		||||
 | 
			
		||||
    return nodes, links, eqpts
 | 
			
		||||
 | 
			
		||||
@@ -566,12 +616,12 @@ def midpoint(city_a, city_b):
 | 
			
		||||
#output_json_file_name = 'coronet_conus_example.json'
 | 
			
		||||
#TODO get column size automatically from tupple size
 | 
			
		||||
 | 
			
		||||
NODES_COLUMN = 7
 | 
			
		||||
NODES_COLUMN = 10
 | 
			
		||||
NODES_LINE = 4
 | 
			
		||||
LINKS_COLUMN = 16
 | 
			
		||||
LINKS_LINE = 3
 | 
			
		||||
EQPTS_LINE = 3
 | 
			
		||||
EQPTS_COLUMN = 12
 | 
			
		||||
EQPTS_COLUMN = 14
 | 
			
		||||
parser = ArgumentParser()
 | 
			
		||||
parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls')
 | 
			
		||||
parser.add_argument('-f', '--filter-region', action='append', default=[])
 | 
			
		||||
 
 | 
			
		||||
@@ -7,25 +7,26 @@ gnpy.core.elements
 | 
			
		||||
 | 
			
		||||
This module contains standard network elements.
 | 
			
		||||
 | 
			
		||||
A network element is a Python callable. It takes a .info.SpectralInformation
 | 
			
		||||
A network element is a Python callable. It takes a :class:`.info.SpectralInformation`
 | 
			
		||||
object and returns a copy with appropriate fields affected. This structure
 | 
			
		||||
represents spectral information that is "propogated" by this network element.
 | 
			
		||||
Network elements must have only a local "view" of the network and propogate
 | 
			
		||||
SpectralInformation using only this information. They should be independent and
 | 
			
		||||
:class:`.info.SpectralInformation` using only this information. They should be independent and
 | 
			
		||||
self-contained.
 | 
			
		||||
 | 
			
		||||
Network elements MUST implement two attributes .uid and .name representing a
 | 
			
		||||
unique identifier and a printable name.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
from numpy import abs, arange, arcsinh, array, exp, divide, errstate
 | 
			
		||||
from numpy import abs, arange, array, exp, divide, errstate
 | 
			
		||||
from numpy import interp, log10, mean, pi, polyfit, polyval, sum
 | 
			
		||||
from scipy.constants import c, h
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
 | 
			
		||||
from gnpy.core.node import Node
 | 
			
		||||
from gnpy.core.units import UNITS
 | 
			
		||||
from gnpy.core.utils import lin2db, db2lin, itufs, snr_sum
 | 
			
		||||
from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum
 | 
			
		||||
from gnpy.core.science_utils import propagate_raman_fiber, _psi
 | 
			
		||||
 | 
			
		||||
class Transceiver(Node):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
@@ -41,7 +42,6 @@ class Transceiver(Node):
 | 
			
		||||
        with errstate(divide='ignore'):
 | 
			
		||||
            self.baud_rate = [c.baud_rate for c in spectral_info.carriers]
 | 
			
		||||
            ratio_01nm = [lin2db(12.5e9/b_rate) for b_rate in self.baud_rate]
 | 
			
		||||
            
 | 
			
		||||
        #set raw values to record original calculation, before update_snr()            
 | 
			
		||||
            self.raw_osnr_ase = [lin2db(divide(c.power.signal, c.power.ase))
 | 
			
		||||
                            for c in spectral_info.carriers]
 | 
			
		||||
@@ -51,11 +51,14 @@ class Transceiver(Node):
 | 
			
		||||
                             for c in spectral_info.carriers]
 | 
			
		||||
            self.raw_snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase)) 
 | 
			
		||||
                        for c in spectral_info.carriers]
 | 
			
		||||
            self.raw_snr_01nm = [snr - ratio for snr, ratio
 | 
			
		||||
                                  in zip(self.raw_snr, ratio_01nm)]
 | 
			
		||||
 | 
			
		||||
            self.osnr_ase = self.raw_osnr_ase
 | 
			
		||||
            self.osnr_ase_01nm = self.raw_osnr_ase_01nm
 | 
			
		||||
            self.osnr_nli = self.raw_osnr_nli
 | 
			
		||||
            self.snr = self.raw_snr
 | 
			
		||||
            self.snr_01nm = self.raw_snr_01nm
 | 
			
		||||
                        
 | 
			
		||||
    def update_snr(self, *args):
 | 
			
		||||
        """
 | 
			
		||||
@@ -75,6 +78,8 @@ class Transceiver(Node):
 | 
			
		||||
                        self.raw_snr, self.baud_rate))
 | 
			
		||||
        self.osnr_ase_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added), 
 | 
			
		||||
                        self.raw_osnr_ase_01nm))
 | 
			
		||||
        self.snr_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added), 
 | 
			
		||||
                        self.raw_snr_01nm))
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def to_json(self):
 | 
			
		||||
@@ -100,40 +105,45 @@ class Transceiver(Node):
 | 
			
		||||
        snr = round(mean(self.snr),2)
 | 
			
		||||
        osnr_ase = round(mean(self.osnr_ase),2)
 | 
			
		||||
        osnr_ase_01nm = round(mean(self.osnr_ase_01nm), 2)
 | 
			
		||||
        snr_01nm = round(mean(self.snr_01nm),2)
 | 
			
		||||
 | 
			
		||||
        return '\n'.join([f'{type(self).__name__} {self.uid}',
 | 
			
		||||
 | 
			
		||||
                          f'  OSNR ASE (0.1nm, dB):      {osnr_ase_01nm:.2f}',
 | 
			
		||||
                          f'  OSNR ASE (signal bw, dB):  {osnr_ase:.2f}',
 | 
			
		||||
                          f'  SNR total (signal bw, dB): {snr:.2f}'])
 | 
			
		||||
 | 
			
		||||
                          f'  SNR total (signal bw, dB): {snr:.2f}',
 | 
			
		||||
                          f'  SNR total (0.1nm, dB): {snr_01nm:.2f}'])
 | 
			
		||||
 | 
			
		||||
    def __call__(self, spectral_info):
 | 
			
		||||
        self._calc_snr(spectral_info)
 | 
			
		||||
        return spectral_info
 | 
			
		||||
 | 
			
		||||
RoadmParams = namedtuple('RoadmParams', 'loss')
 | 
			
		||||
RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr restrictions per_degree_target_pch_out_db')
 | 
			
		||||
 | 
			
		||||
class Roadm(Node):
 | 
			
		||||
    def __init__(self, *args, params=None, **kwargs):
 | 
			
		||||
        if params is None:
 | 
			
		||||
            # default loss value if not mentioned in loaded network json
 | 
			
		||||
            params = {'loss':None}
 | 
			
		||||
    def __init__(self, *args, params, **kwargs):
 | 
			
		||||
        if 'per_degree_target_pch_out_db' not in params.keys():
 | 
			
		||||
            params['per_degree_target_pch_out_db'] = []
 | 
			
		||||
        super().__init__(*args, params=RoadmParams(**params), **kwargs)
 | 
			
		||||
        self.loss = self.params.loss
 | 
			
		||||
        self.target_pch_out_db = None #set in Networks.py by def set_roadm_loss
 | 
			
		||||
        self.effective_pch_out_db = None
 | 
			
		||||
        self.effective_loss = None #set in self.propagate
 | 
			
		||||
        self.loss = 0 #auto-design interest
 | 
			
		||||
        self.effective_loss = None
 | 
			
		||||
        self.effective_pch_out_db = self.params.target_pch_out_db
 | 
			
		||||
        self.passive = True
 | 
			
		||||
        self.restrictions = self.params.restrictions
 | 
			
		||||
        self.per_degree_target_pch_out_db = self.params.per_degree_target_pch_out_db
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def to_json(self):
 | 
			
		||||
        return {'uid'       : self.uid,
 | 
			
		||||
                'type'      : type(self).__name__,
 | 
			
		||||
                'params'    : {'loss' : self.loss},
 | 
			
		||||
                'params'    : {
 | 
			
		||||
                    'target_pch_out_db' : self.effective_pch_out_db,
 | 
			
		||||
                    'restrictions'      : self.restrictions,
 | 
			
		||||
                    'per_degree_target_pch_out_db': self.per_degree_target_pch_out_db
 | 
			
		||||
                    },
 | 
			
		||||
                'metadata'      : {
 | 
			
		||||
                    'location': self.metadata['location']._asdict()
 | 
			
		||||
                                    }
 | 
			
		||||
                                }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
@@ -141,35 +151,45 @@ class Roadm(Node):
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return '\n'.join([f'{type(self).__name__} {self.uid}',
 | 
			
		||||
                          f'  loss (dB):     {self.effective_loss:.2f}',
 | 
			
		||||
                          f'  pch out (dBm): {self.effective_pch_out_db!r}'])
 | 
			
		||||
                          f'  effective loss (dB):  {self.effective_loss:.2f}',
 | 
			
		||||
                          f'  pch out (dBm):        {self.effective_pch_out_db!r}'])
 | 
			
		||||
 | 
			
		||||
    def propagate(self, pref, *carriers):
 | 
			
		||||
    def propagate(self, pref, *carriers, degree):
 | 
			
		||||
        #pin_target and loss are read from eqpt_config.json['Roadm']
 | 
			
		||||
        #all ingress channels in xpress are set to this power level
 | 
			
		||||
        #but add channels are not, so we define an effective loss
 | 
			
		||||
        #in the case of add channels
 | 
			
		||||
        if self.target_pch_out_db:
 | 
			
		||||
            self.effective_loss = pref.pi - self.target_pch_out_db
 | 
			
		||||
        if self.per_degree_target_pch_out_db:
 | 
			
		||||
            # find the target power on this degree
 | 
			
		||||
            try:
 | 
			
		||||
                temp = next(el['target_pch_out_db'] \
 | 
			
		||||
                    for el in self.per_degree_target_pch_out_db if el['to_node']==degree)
 | 
			
		||||
            except StopIteration:
 | 
			
		||||
                # if no target power is defined on this degree use the global one
 | 
			
		||||
                temp = self.params.target_pch_out_db
 | 
			
		||||
        else:
 | 
			
		||||
             self.effective_loss = self.loss
 | 
			
		||||
        self.effective_pch_out_db = pref.pi - self.effective_loss
 | 
			
		||||
        attenuation = db2lin(self.effective_loss)
 | 
			
		||||
 | 
			
		||||
        for carrier in carriers:
 | 
			
		||||
            # if no per degree target power are defined, use the global one
 | 
			
		||||
            temp = self.params.target_pch_out_db
 | 
			
		||||
        self.effective_pch_out_db = min(pref.p_spani, temp)
 | 
			
		||||
        self.effective_loss = pref.p_spani - self.effective_pch_out_db
 | 
			
		||||
        carriers_power = array([c.power.signal +c.power.nli+c.power.ase for c in carriers])
 | 
			
		||||
        carriers_att = list(map(lambda x : lin2db(x*1e3)-self.effective_pch_out_db, carriers_power))
 | 
			
		||||
        exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default = 0)
 | 
			
		||||
        carriers_att = list(map(lambda x: db2lin(x+exceeding_att), carriers_att))
 | 
			
		||||
        for carrier_att, carrier in zip(carriers_att, carriers) :
 | 
			
		||||
            pwr = carrier.power
 | 
			
		||||
            pwr = pwr._replace(signal=pwr.signal/attenuation,
 | 
			
		||||
                               nonlinear_interference=pwr.nli/attenuation,
 | 
			
		||||
                               amplified_spontaneous_emission=pwr.ase/attenuation)
 | 
			
		||||
            pwr = pwr._replace( signal = pwr.signal/carrier_att,
 | 
			
		||||
                                nli = pwr.nli/carrier_att,
 | 
			
		||||
                                ase = pwr.ase/carrier_att)
 | 
			
		||||
            yield carrier._replace(power=pwr)
 | 
			
		||||
 | 
			
		||||
    def update_pref(self, pref):
 | 
			
		||||
        return pref._replace(p_span0=pref.p0, p_spani=self.effective_pch_out_db)
 | 
			
		||||
        return pref._replace(p_span0=pref.p_span0, p_spani=self.effective_pch_out_db)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, spectral_info):
 | 
			
		||||
        carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers))
 | 
			
		||||
    def __call__(self, spectral_info, degree):
 | 
			
		||||
        carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers, degree=degree))
 | 
			
		||||
        pref = self.update_pref(spectral_info.pref)
 | 
			
		||||
        return spectral_info.update(carriers=carriers, pref=pref)
 | 
			
		||||
        return spectral_info._replace(carriers=carriers, pref=pref)
 | 
			
		||||
 | 
			
		||||
FusedParams = namedtuple('FusedParams', 'loss')
 | 
			
		||||
 | 
			
		||||
@@ -186,6 +206,9 @@ class Fused(Node):
 | 
			
		||||
    def to_json(self):
 | 
			
		||||
        return {'uid'       : self.uid,
 | 
			
		||||
                'type'      : type(self).__name__,
 | 
			
		||||
                'params'    :{
 | 
			
		||||
                    'loss': self.loss
 | 
			
		||||
                },
 | 
			
		||||
                'metadata'      : {
 | 
			
		||||
                    'location': self.metadata['location']._asdict()
 | 
			
		||||
                                    }
 | 
			
		||||
@@ -204,17 +227,17 @@ class Fused(Node):
 | 
			
		||||
        for carrier in carriers:
 | 
			
		||||
            pwr = carrier.power
 | 
			
		||||
            pwr = pwr._replace(signal=pwr.signal/attenuation,
 | 
			
		||||
                               nonlinear_interference=pwr.nli/attenuation,
 | 
			
		||||
                               amplified_spontaneous_emission=pwr.ase/attenuation)
 | 
			
		||||
                               nli=pwr.nli/attenuation,
 | 
			
		||||
                               ase=pwr.ase/attenuation)
 | 
			
		||||
            yield carrier._replace(power=pwr)
 | 
			
		||||
 | 
			
		||||
    def update_pref(self, pref):
 | 
			
		||||
        return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
 | 
			
		||||
        return pref._replace(p_span0=pref.p_span0, p_spani=pref.p_spani - self.loss)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, spectral_info):
 | 
			
		||||
        carriers = tuple(self.propagate(*spectral_info.carriers))
 | 
			
		||||
        pref = self.update_pref(spectral_info.pref)
 | 
			
		||||
        return spectral_info.update(carriers=carriers, pref=pref)
 | 
			
		||||
        return spectral_info._replace(carriers=carriers, pref=pref)
 | 
			
		||||
 | 
			
		||||
FiberParams = namedtuple('FiberParams', 'type_variety length loss_coef length_units \
 | 
			
		||||
                                         att_in con_in con_out dispersion gamma')
 | 
			
		||||
@@ -283,12 +306,12 @@ class Fiber(Node):
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def fiber_loss(self):
 | 
			
		||||
        # dB fiber loss, not including padding attenuator
 | 
			
		||||
        """Fiber loss in dB, not including padding attenuator"""
 | 
			
		||||
        return self.loss_coef * self.length + self.con_in + self.con_out
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def loss(self):
 | 
			
		||||
        #total loss incluiding padding att_in: useful for polymorphism with roadm loss
 | 
			
		||||
        """total loss including padding att_in: useful for polymorphism with roadm loss"""
 | 
			
		||||
        return self.loss_coef * self.length + self.con_in + self.con_out + self.att_in
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
@@ -313,16 +336,13 @@ class Fiber(Node):
 | 
			
		||||
 | 
			
		||||
    def carriers(self, loc, attr):
 | 
			
		||||
        """retrieve carriers information
 | 
			
		||||
        loc = (in, out) of the class element
 | 
			
		||||
        attr = (ase, nli, signal, total) power information"""
 | 
			
		||||
 | 
			
		||||
        :param loc: (in, out) of the class element
 | 
			
		||||
        :param attr: (ase, nli, signal, total) power information
 | 
			
		||||
        """
 | 
			
		||||
        if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')):
 | 
			
		||||
            yield None
 | 
			
		||||
            return
 | 
			
		||||
        power_dict = {
 | 
			
		||||
                        'nli':      'nonlinear_interference',
 | 
			
		||||
                        'ase':      'amplified_spontaneous_emission'
 | 
			
		||||
                    }
 | 
			
		||||
        attr = power_dict.get(attr, attr)
 | 
			
		||||
        loc_attr = 'carriers_'+loc
 | 
			
		||||
        for c in getattr(self, loc_attr) :
 | 
			
		||||
            if attr == 'total':
 | 
			
		||||
@@ -330,46 +350,30 @@ class Fiber(Node):
 | 
			
		||||
            else:
 | 
			
		||||
                yield c.power._asdict().get(attr, None)
 | 
			
		||||
 | 
			
		||||
    def beta2(self, ref_wavelength=None):
 | 
			
		||||
        """ Returns beta2 from dispersion parameter.
 | 
			
		||||
    def beta2(self, ref_wavelength=1550e-9):
 | 
			
		||||
        """Returns beta2 from dispersion parameter.
 | 
			
		||||
        Dispersion is entered in ps/nm/km.
 | 
			
		||||
        Disperion can be a numpy array or a single value.  If a
 | 
			
		||||
        value ref_wavelength is not entered 1550e-9m will be assumed.
 | 
			
		||||
        ref_wavelength can be a numpy array.
 | 
			
		||||
        Disperion can be a numpy array or a single value.
 | 
			
		||||
 | 
			
		||||
        :param ref_wavelength: can be a numpy array; default: 1550nm
 | 
			
		||||
        """
 | 
			
		||||
        # TODO|jla: discuss beta2 as method or attribute
 | 
			
		||||
        wl = 1550e-9 if ref_wavelength is None else ref_wavelength
 | 
			
		||||
        D = abs(self.dispersion)
 | 
			
		||||
        b2 = (wl ** 2) * D / (2 * pi * c)  # 10^21 scales [ps^2/km]
 | 
			
		||||
        b2 = (ref_wavelength ** 2) * D / (2 * pi * c)  # 10^21 scales [ps^2/km]
 | 
			
		||||
        return b2 # s/Hz/m
 | 
			
		||||
 | 
			
		||||
    def dbkm_2_lin(self):
 | 
			
		||||
        """ calculates the linear loss coefficient
 | 
			
		||||
        """
 | 
			
		||||
        # alpha_pcoef is linear loss coefficient in dB/km^-1
 | 
			
		||||
        # alpha_acoef is linear loss field amplitude coefficient in m^-1
 | 
			
		||||
        """calculates the linear loss coefficient"""
 | 
			
		||||
        # linear loss coefficient in dB/km^-1
 | 
			
		||||
        alpha_pcoef = self.loss_coef
 | 
			
		||||
        # linear loss field amplitude coefficient in m^-1
 | 
			
		||||
        alpha_acoef = alpha_pcoef / (2 * 10 * log10(exp(1)))
 | 
			
		||||
        return alpha_pcoef, alpha_acoef
 | 
			
		||||
 | 
			
		||||
    def _psi(self, carrier, interfering_carrier):
 | 
			
		||||
        """ Calculates eq. 123 from	arXiv:1209.0394.
 | 
			
		||||
        """
 | 
			
		||||
        if carrier.num_chan == interfering_carrier.num_chan: # SCI
 | 
			
		||||
            psi = arcsinh(0.5 * pi**2 * self.asymptotic_length
 | 
			
		||||
                              * abs(self.beta2()) * carrier.baud_rate**2)
 | 
			
		||||
        else: # XCI
 | 
			
		||||
            delta_f = carrier.freq - interfering_carrier.freq
 | 
			
		||||
            psi = arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
 | 
			
		||||
                                * carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
 | 
			
		||||
            psi -= arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
 | 
			
		||||
                                 * carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
 | 
			
		||||
 | 
			
		||||
        return psi
 | 
			
		||||
 | 
			
		||||
    def _gn_analytic(self, carrier, *carriers):
 | 
			
		||||
        """ Computes the nonlinear interference power on a single carrier.
 | 
			
		||||
        The method uses eq. 120 from arXiv:1209.0394.
 | 
			
		||||
        """Computes the nonlinear interference power on a single carrier.
 | 
			
		||||
        The method uses eq. 120 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__.
 | 
			
		||||
 | 
			
		||||
        :param carrier: the signal under analysis
 | 
			
		||||
        :param carriers: the full WDM comb
 | 
			
		||||
        :return: carrier_nli: the amount of nonlinear interference in W on the under analysis
 | 
			
		||||
@@ -377,7 +381,7 @@ class Fiber(Node):
 | 
			
		||||
 | 
			
		||||
        g_nli = 0
 | 
			
		||||
        for interfering_carrier in carriers:
 | 
			
		||||
            psi = self._psi(carrier, interfering_carrier)
 | 
			
		||||
            psi = _psi(carrier, interfering_carrier, beta2=self.beta2(), asymptotic_length=self.asymptotic_length)
 | 
			
		||||
            g_nli += (interfering_carrier.power.signal/interfering_carrier.baud_rate)**2 \
 | 
			
		||||
                     * (carrier.power.signal/carrier.baud_rate) * psi
 | 
			
		||||
 | 
			
		||||
@@ -396,8 +400,8 @@ class Fiber(Node):
 | 
			
		||||
        for carrier in carriers:
 | 
			
		||||
            pwr = carrier.power
 | 
			
		||||
            pwr = pwr._replace(signal=pwr.signal/attenuation,
 | 
			
		||||
                               nonlinear_interference=pwr.nli/attenuation,
 | 
			
		||||
                               amplified_spontaneous_emission=pwr.ase/attenuation)
 | 
			
		||||
                               nli=pwr.nli/attenuation,
 | 
			
		||||
                               ase=pwr.ase/attenuation)
 | 
			
		||||
            carrier = carrier._replace(power=pwr)
 | 
			
		||||
            chan.append(carrier)
 | 
			
		||||
 | 
			
		||||
@@ -409,20 +413,77 @@ class Fiber(Node):
 | 
			
		||||
            pwr = carrier.power
 | 
			
		||||
            carrier_nli = self._gn_analytic(carrier, *carriers)
 | 
			
		||||
            pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation/attenuation,
 | 
			
		||||
                               nonlinear_interference=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation,
 | 
			
		||||
                               amplified_spontaneous_emission=pwr.ase/self.lin_attenuation/attenuation)
 | 
			
		||||
                               nli=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation,
 | 
			
		||||
                               ase=pwr.ase/self.lin_attenuation/attenuation)
 | 
			
		||||
            yield carrier._replace(power=pwr)
 | 
			
		||||
 | 
			
		||||
    def update_pref(self, pref):
 | 
			
		||||
        self.pch_out_db = round(pref.pi - self.loss, 2)
 | 
			
		||||
        return pref._replace(p_span0=pref.p0, p_spani=self.pch_out_db)
 | 
			
		||||
        self.pch_out_db = round(pref.p_spani - self.loss, 2)
 | 
			
		||||
        return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, spectral_info):
 | 
			
		||||
        self.carriers_in = spectral_info.carriers
 | 
			
		||||
        carriers = tuple(self.propagate(*spectral_info.carriers))
 | 
			
		||||
        pref = self.update_pref(spectral_info.pref)
 | 
			
		||||
        self.carriers_out = carriers
 | 
			
		||||
        return spectral_info.update(carriers=carriers, pref=pref)
 | 
			
		||||
        return spectral_info._replace(carriers=carriers, pref=pref)
 | 
			
		||||
 | 
			
		||||
RamanFiberParams = namedtuple('RamanFiberParams', 'type_variety length loss_coef length_units \
 | 
			
		||||
                                         att_in con_in con_out dispersion gamma raman_efficiency')
 | 
			
		||||
 | 
			
		||||
class RamanFiber(Fiber):
 | 
			
		||||
    def __init__(self, *args, params=None, **kwargs):
 | 
			
		||||
        if params is None:
 | 
			
		||||
            params = {}
 | 
			
		||||
        if 'con_in' not in params:
 | 
			
		||||
            # if not defined in the network json connector loss in/out
 | 
			
		||||
            # the None value will be updated in network.py[build_network]
 | 
			
		||||
            # with default values from eqpt_config.json[Spans]
 | 
			
		||||
            params['con_in'] = None
 | 
			
		||||
            params['con_out'] = None
 | 
			
		||||
        if 'att_in' not in params:
 | 
			
		||||
            #fixed attenuator for padding
 | 
			
		||||
            params['att_in'] = 0
 | 
			
		||||
 | 
			
		||||
        # TODO: can we re-use the Fiber constructor in a better way?
 | 
			
		||||
        Node.__init__(self, *args, params=RamanFiberParams(**params), **kwargs)
 | 
			
		||||
        self.type_variety = self.params.type_variety
 | 
			
		||||
        self.length = self.params.length * UNITS[self.params.length_units] # in m
 | 
			
		||||
        self.loss_coef = self.params.loss_coef * 1e-3 # lineic loss dB/m
 | 
			
		||||
        self.lin_loss_coef = self.params.loss_coef / (20 * log10(exp(1)))
 | 
			
		||||
        self.att_in = self.params.att_in
 | 
			
		||||
        self.con_in = self.params.con_in
 | 
			
		||||
        self.con_out = self.params.con_out
 | 
			
		||||
        self.dispersion = self.params.dispersion  # s/m/m
 | 
			
		||||
        self.gamma = self.params.gamma # 1/W/m
 | 
			
		||||
        self.pch_out_db = None
 | 
			
		||||
        self.carriers_in = None
 | 
			
		||||
        self.carriers_out = None
 | 
			
		||||
        # TODO|jla: discuss factor 2 in the linear lineic attenuation
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def sim_params(self):
 | 
			
		||||
        return self._sim_params
 | 
			
		||||
 | 
			
		||||
    @sim_params.setter
 | 
			
		||||
    def sim_params(self, sim_params=None):
 | 
			
		||||
        self._sim_params = sim_params
 | 
			
		||||
 | 
			
		||||
    def update_pref(self, pref, *carriers):
 | 
			
		||||
        pch_out_db = lin2db(mean([carrier.power.signal for carrier in carriers])) + 30
 | 
			
		||||
        self.pch_out_db = round(pch_out_db, 2)
 | 
			
		||||
        return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, spectral_info):
 | 
			
		||||
        self.carriers_in = spectral_info.carriers
 | 
			
		||||
        carriers = tuple(self.propagate(*spectral_info.carriers))
 | 
			
		||||
        pref = self.update_pref(spectral_info.pref, *carriers)
 | 
			
		||||
        self.carriers_out = carriers
 | 
			
		||||
        return spectral_info._replace(carriers=carriers, pref=pref)
 | 
			
		||||
 | 
			
		||||
    def propagate(self, *carriers):
 | 
			
		||||
        for propagated_carrier in propagate_raman_fiber(self, *carriers):
 | 
			
		||||
            yield propagated_carrier
 | 
			
		||||
 | 
			
		||||
class EdfaParams:
 | 
			
		||||
    def __init__(self, **params):
 | 
			
		||||
@@ -430,16 +491,16 @@ class EdfaParams:
 | 
			
		||||
        if params == {}:
 | 
			
		||||
            self.type_variety = ''
 | 
			
		||||
            self.type_def = ''
 | 
			
		||||
            self.gain_flatmax = 0
 | 
			
		||||
            self.gain_min = 0
 | 
			
		||||
            self.p_max = 0
 | 
			
		||||
            self.nf_model = None
 | 
			
		||||
            self.nf_fit_coeff = None
 | 
			
		||||
            self.nf_ripple = None
 | 
			
		||||
            self.dgt = None
 | 
			
		||||
            self.gain_ripple = None
 | 
			
		||||
            self.out_voa_auto = False
 | 
			
		||||
            self.allowed_for_design = None
 | 
			
		||||
            # self.gain_flatmax = 0
 | 
			
		||||
            # self.gain_min = 0
 | 
			
		||||
            # self.p_max = 0
 | 
			
		||||
            # self.nf_model = None
 | 
			
		||||
            # self.nf_fit_coeff = None
 | 
			
		||||
            # self.nf_ripple = None
 | 
			
		||||
            # self.dgt = None
 | 
			
		||||
            # self.gain_ripple = None
 | 
			
		||||
            # self.out_voa_auto = False
 | 
			
		||||
            # self.allowed_for_design = None
 | 
			
		||||
 | 
			
		||||
    def update_params(self, kwargs):
 | 
			
		||||
        for k,v in kwargs.items() :
 | 
			
		||||
@@ -447,22 +508,33 @@ class EdfaParams:
 | 
			
		||||
                if isinstance(v, dict) else v)
 | 
			
		||||
 | 
			
		||||
class EdfaOperational:
 | 
			
		||||
    def __init__(self, gain_target, tilt_target, out_voa=None):
 | 
			
		||||
        self.gain_target = gain_target
 | 
			
		||||
        self.tilt_target = tilt_target
 | 
			
		||||
        self.out_voa = out_voa
 | 
			
		||||
    default_values = \
 | 
			
		||||
    {
 | 
			
		||||
        'gain_target':      None,
 | 
			
		||||
        'delta_p':          None,
 | 
			
		||||
        'out_voa':          None,        
 | 
			
		||||
        'tilt_target':      0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **operational):
 | 
			
		||||
        self.update_attr(operational)
 | 
			
		||||
 | 
			
		||||
    def update_attr(self, kwargs):
 | 
			
		||||
        clean_kwargs = {k:v for k,v in kwargs.items() if v !=''}
 | 
			
		||||
        for k,v in self.default_values.items():
 | 
			
		||||
            setattr(self, k, clean_kwargs.get(k,v))
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return (f'{type(self).__name__}('
 | 
			
		||||
                f'gain_target={self.gain_target!r}, '
 | 
			
		||||
                f'tilt_target={self.tilt_target!r})')
 | 
			
		||||
 | 
			
		||||
class Edfa(Node):
 | 
			
		||||
    def __init__(self, *args, params={}, operational={}, **kwargs):
 | 
			
		||||
        #TBC is this useful? put in comment for now:
 | 
			
		||||
        #if params is None:
 | 
			
		||||
        #    params = {}
 | 
			
		||||
        #if operational is None:
 | 
			
		||||
        #    operational = {}
 | 
			
		||||
    def __init__(self, *args, params=None, operational=None, **kwargs):
 | 
			
		||||
        if params is None:
 | 
			
		||||
            params = {}
 | 
			
		||||
        if operational is None:
 | 
			
		||||
            operational = {}
 | 
			
		||||
        super().__init__(
 | 
			
		||||
            *args,
 | 
			
		||||
            params=EdfaParams(**params),
 | 
			
		||||
@@ -479,14 +551,16 @@ class Edfa(Node):
 | 
			
		||||
        self.pin_db = None
 | 
			
		||||
        self.nch = None
 | 
			
		||||
        self.pout_db = None
 | 
			
		||||
        self.dp_db = None #delta P with Pref (power swwep) in power mode
 | 
			
		||||
        self.target_pch_out_db = None
 | 
			
		||||
        self.effective_pch_out_db = None
 | 
			
		||||
        self.passive = False
 | 
			
		||||
        self.effective_gain = self.operational.gain_target
 | 
			
		||||
        self.att_in = None
 | 
			
		||||
        self.carriers_in = None
 | 
			
		||||
        self.carriers_out = None
 | 
			
		||||
        self.effective_gain = self.operational.gain_target
 | 
			
		||||
        self.delta_p = self.operational.delta_p #delta P with Pref (power swwep) in power mode
 | 
			
		||||
        self.tilt_target = self.operational.tilt_target
 | 
			
		||||
        self.out_voa = self.operational.out_voa
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def to_json(self):
 | 
			
		||||
@@ -494,9 +568,10 @@ class Edfa(Node):
 | 
			
		||||
                'type'          : type(self).__name__,
 | 
			
		||||
                'type_variety'  : self.params.type_variety,
 | 
			
		||||
                'operational'   : {
 | 
			
		||||
                    'gain_target' : self.operational.gain_target,
 | 
			
		||||
                    'tilt_target' : self.operational.tilt_target,
 | 
			
		||||
                    'out_voa'     : self.operational.out_voa
 | 
			
		||||
                    'gain_target' : self.effective_gain,
 | 
			
		||||
                    'delta_p'     : self.delta_p,
 | 
			
		||||
                    'tilt_target' : self.tilt_target,
 | 
			
		||||
                    'out_voa'     : self.out_voa
 | 
			
		||||
                },
 | 
			
		||||
                'metadata'      : {
 | 
			
		||||
                    'location': self.metadata['location']._asdict()
 | 
			
		||||
@@ -528,23 +603,20 @@ class Edfa(Node):
 | 
			
		||||
                          f'  pad att_in (dB):        {self.att_in:.2f}',
 | 
			
		||||
                          f'  Power In (dBm):         {self.pin_db:.2f}',
 | 
			
		||||
                          f'  Power Out (dBm):        {self.pout_db:.2f}',
 | 
			
		||||
                          f'  Delta_P (dB):           {self.dp_db!r}',
 | 
			
		||||
                          f'  Delta_P (dB):           {self.delta_p!r}',
 | 
			
		||||
                          f'  target pch (dBm):       {self.target_pch_out_db!r}',
 | 
			
		||||
                          f'  effective pch (dBm):    {self.effective_pch_out_db!r}',
 | 
			
		||||
                          f'  output VOA (dB):        {self.operational.out_voa:.2f}'])
 | 
			
		||||
                          f'  output VOA (dB):        {self.out_voa:.2f}'])
 | 
			
		||||
 | 
			
		||||
    def carriers(self, loc, attr):
 | 
			
		||||
        """retrieve carriers information
 | 
			
		||||
        loc = (in, out) of the class element
 | 
			
		||||
        attr = (ase, nli, signal, total) power information"""
 | 
			
		||||
 | 
			
		||||
        :param loc: (in, out) of the class element
 | 
			
		||||
        :param attr: (ase, nli, signal, total) power information
 | 
			
		||||
        """
 | 
			
		||||
        if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')):
 | 
			
		||||
            yield None
 | 
			
		||||
            return
 | 
			
		||||
        power_dict = {
 | 
			
		||||
                        'nli':      'nonlinear_interference',
 | 
			
		||||
                        'ase':      'amplified_spontaneous_emission'
 | 
			
		||||
                    }
 | 
			
		||||
        attr = power_dict.get(attr, attr)
 | 
			
		||||
        loc_attr = 'carriers_'+loc
 | 
			
		||||
        for c in getattr(self, loc_attr) :
 | 
			
		||||
            if attr == 'total':
 | 
			
		||||
@@ -553,69 +625,106 @@ class Edfa(Node):
 | 
			
		||||
                yield c.power._asdict().get(attr, None)
 | 
			
		||||
 | 
			
		||||
    def interpol_params(self, frequencies, pin, baud_rates, pref):
 | 
			
		||||
        """interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from json
 | 
			
		||||
        """interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from JSON
 | 
			
		||||
        set the edfa class __init__ None parameters :
 | 
			
		||||
                self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple
 | 
			
		||||
        """
 | 
			
		||||
        # TODO|jla: read amplifier actual frequencies from additional params in json
 | 
			
		||||
        amplifier_freq = itufs(0.05) * 1e12 # Hz
 | 
			
		||||
        amplifier_freq = arrange_frequencies(len(self.params.dgt), self.params.f_min, self.params.f_max) # Hz
 | 
			
		||||
        self.channel_freq = frequencies
 | 
			
		||||
        self.interpol_dgt = interp(self.channel_freq, amplifier_freq, self.params.dgt)
 | 
			
		||||
 | 
			
		||||
        self.interpol_gain_ripple = interp(self.channel_freq, amplifier_freq, self.params.gain_ripple)
 | 
			
		||||
        self.interpol_nf_ripple =interp(self.channel_freq, amplifier_freq, self.params.nf_ripple)
 | 
			
		||||
 | 
			
		||||
        self.nch = frequencies.size
 | 
			
		||||
        self.pin_db = lin2db(sum(pin*1e3))
 | 
			
		||||
        
 | 
			
		||||
        """in power mode: dp_db is defined and can be used to calculate the power target
 | 
			
		||||
        """in power mode: delta_p is defined and can be used to calculate the power target
 | 
			
		||||
        This power target is used calculate the amplifier gain"""
 | 
			
		||||
        if self.dp_db is not None:
 | 
			
		||||
            self.target_pch_out_db = round(self.dp_db + pref.p0, 2)
 | 
			
		||||
            self.effective_gain = self.target_pch_out_db - pref.pi
 | 
			
		||||
        else:
 | 
			
		||||
            self.effective_gain = self.operational.gain_target
 | 
			
		||||
        
 | 
			
		||||
        """check power saturation and correct target_gain accordingly:"""
 | 
			
		||||
        self.effective_gain = min(self.effective_gain, self.params.p_max - self.pin_db)
 | 
			
		||||
        self.effective_pch_out_db = round(pref.pi + self.effective_gain, 2)
 | 
			
		||||
        if self.delta_p is not None:
 | 
			
		||||
            self.target_pch_out_db = round(self.delta_p + pref.p_span0, 2)
 | 
			
		||||
            self.effective_gain = self.target_pch_out_db - pref.p_spani
 | 
			
		||||
 | 
			
		||||
        """check power saturation and correct effective gain & power accordingly:"""            
 | 
			
		||||
        self.effective_gain = min(  
 | 
			
		||||
                                    self.effective_gain, 
 | 
			
		||||
                                    self.params.p_max - (pref.p_spani + pref.neq_ch)
 | 
			
		||||
                                    )
 | 
			
		||||
        #print(self.uid, self.effective_gain, self.operational.gain_target)
 | 
			
		||||
        self.effective_pch_out_db = round(pref.p_spani + self.effective_gain, 2)
 | 
			
		||||
 | 
			
		||||
        """check power saturation and correct target_gain accordingly:"""
 | 
			
		||||
        #print(self.uid, self.effective_gain, self.pin_db, pref.p_spani)
 | 
			
		||||
        self.nf = self._calc_nf()
 | 
			
		||||
        self.gprofile = self._gain_profile(pin)
 | 
			
		||||
 | 
			
		||||
        pout = (pin + self.noise_profile(baud_rates))*db2lin(self.gprofile)
 | 
			
		||||
        self.pout_db = lin2db(sum(pout*1e3))
 | 
			
		||||
        self.operational.gain_target = self.effective_gain
 | 
			
		||||
        # ase & nli are only calculated in signal bandwidth
 | 
			
		||||
        #    pout_db is not the absolute full output power (negligible if sufficient channels)
 | 
			
		||||
 | 
			
		||||
    def _nf(self, type_def, nf_model, nf_fit_coeff, gain_min, gain_flatmax, gain_target):
 | 
			
		||||
        #if hybrid raman, use edfa_gain_flatmax attribute, else use gain_flatmax 
 | 
			
		||||
        #gain_flatmax = getattr(params, 'edfa_gain_flatmax', params.gain_flatmax)
 | 
			
		||||
        pad = max(gain_min - gain_target, 0)
 | 
			
		||||
        gain_target += pad
 | 
			
		||||
        dg = max(gain_flatmax - gain_target, 0)
 | 
			
		||||
        if type_def == 'variable_gain':
 | 
			
		||||
            g1a = gain_target - nf_model.delta_p - dg
 | 
			
		||||
            nf_avg = lin2db(db2lin(nf_model.nf1) + db2lin(nf_model.nf2)/db2lin(g1a))            
 | 
			
		||||
        elif type_def == 'fixed_gain':
 | 
			
		||||
            nf_avg = nf_model.nf0
 | 
			
		||||
        elif type_def == 'openroadm':
 | 
			
		||||
            pin_ch = self.pin_db - lin2db(self.nch)
 | 
			
		||||
            # model OSNR = f(Pin)
 | 
			
		||||
            nf_avg = pin_ch - polyval(nf_model.nf_coef, pin_ch) + 58
 | 
			
		||||
        elif type_def == 'advanced_model':
 | 
			
		||||
            nf_avg = polyval(nf_fit_coeff, -dg)
 | 
			
		||||
        else:
 | 
			
		||||
            assert False, "Unrecognized amplifier type, this should have been checked by the JSON loader"
 | 
			
		||||
        return nf_avg+pad, pad
 | 
			
		||||
 | 
			
		||||
    def _calc_nf(self, avg = False):
 | 
			
		||||
        """nf calculation based on 2 models: self.params.nf_model.enabled from json import:
 | 
			
		||||
        True => 2 stages amp modelling based on precalculated nf1, nf2 and delta_p in build_OA_json
 | 
			
		||||
        False => polynomial fit based on self.params.nf_fit_coeff"""
 | 
			
		||||
        # TODO|jla: TBD alarm rising or input VOA padding in case
 | 
			
		||||
        # gain_min > gain_target TBD:
 | 
			
		||||
        pad = max(self.params.gain_min - self.effective_gain, 0)
 | 
			
		||||
        self.att_in = pad
 | 
			
		||||
        gain_target = self.effective_gain + pad
 | 
			
		||||
        dg = max(self.params.gain_flatmax - gain_target, 0)
 | 
			
		||||
        if self.params.type_def == 'variable_gain':
 | 
			
		||||
            g1a = gain_target - self.params.nf_model.delta_p - dg
 | 
			
		||||
            nf_avg = lin2db(db2lin(self.params.nf_model.nf1) + db2lin(self.params.nf_model.nf2)/db2lin(g1a))
 | 
			
		||||
        elif self.params.type_def == 'fixed_gain':
 | 
			
		||||
            nf_avg = self.params.nf_model.nf0
 | 
			
		||||
        elif self.params.type_def == 'openroadm':
 | 
			
		||||
            pin_ch = self.pin_db - lin2db(self.nch)
 | 
			
		||||
            # model OSNR = f(Pin)
 | 
			
		||||
            nf_avg = pin_ch - polyval(self.params.nf_model.nf_coef, pin_ch) + 58
 | 
			
		||||
        else:
 | 
			
		||||
            nf_avg = polyval(self.params.nf_fit_coeff, -dg)
 | 
			
		||||
        if self.params.type_def == 'dual_stage':
 | 
			
		||||
            g1 = self.params.preamp_gain_flatmax
 | 
			
		||||
            g2 = self.effective_gain - g1
 | 
			
		||||
            nf1_avg, pad = self._nf( self.params.preamp_type_def, 
 | 
			
		||||
                                self.params.preamp_nf_model,
 | 
			
		||||
                                self.params.preamp_nf_fit_coeff,
 | 
			
		||||
                                self.params.preamp_gain_min,
 | 
			
		||||
                                self.params.preamp_gain_flatmax, 
 | 
			
		||||
                                g1)
 | 
			
		||||
            #no padding expected for the 1stage because g1 = gain_max
 | 
			
		||||
            nf2_avg, pad = self._nf( self.params.booster_type_def,
 | 
			
		||||
                                self.params.booster_nf_model,
 | 
			
		||||
                                self.params.booster_nf_fit_coeff,
 | 
			
		||||
                                self.params.booster_gain_min,
 | 
			
		||||
                                self.params.booster_gain_flatmax, 
 | 
			
		||||
                                g2)
 | 
			
		||||
            nf_avg = lin2db(db2lin(nf1_avg) + db2lin(nf2_avg-g1))
 | 
			
		||||
            #no padding expected for the 1stage because g1 = gain_max            
 | 
			
		||||
            pad = 0
 | 
			
		||||
        else:      
 | 
			
		||||
            nf_avg, pad = self._nf(  self.params.type_def,
 | 
			
		||||
                                self.params.nf_model,
 | 
			
		||||
                                self.params.nf_fit_coeff,
 | 
			
		||||
                                self.params.gain_min,
 | 
			
		||||
                                self.params.gain_flatmax,
 | 
			
		||||
                                self.effective_gain)
 | 
			
		||||
 | 
			
		||||
        self.att_in = pad # not used to attenuate carriers, only used in _repr_ and _str_
 | 
			
		||||
        if avg:
 | 
			
		||||
            return nf_avg + pad
 | 
			
		||||
            return nf_avg
 | 
			
		||||
        else:
 | 
			
		||||
            return self.interpol_nf_ripple + nf_avg + pad # input VOA = 1 for 1 NF degradation
 | 
			
		||||
            return self.interpol_nf_ripple + nf_avg # input VOA = 1 for 1 NF degradation
 | 
			
		||||
 | 
			
		||||
    def noise_profile(self, df):
 | 
			
		||||
        """ noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
 | 
			
		||||
        """noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
 | 
			
		||||
        noise is calculated at amplifier input
 | 
			
		||||
 | 
			
		||||
        :bw: signal bandwidth = baud rate in Hz
 | 
			
		||||
@@ -705,7 +814,7 @@ class Edfa(Node):
 | 
			
		||||
 | 
			
		||||
        # Calculate the target slope - currently assumes equal spaced channels
 | 
			
		||||
        # TODO|jla: support arbitrary channel spacing
 | 
			
		||||
        targ_slope = self.operational.tilt_target / (len(nb_channel) - 1)
 | 
			
		||||
        targ_slope = self.tilt_target / (len(nb_channel) - 1)
 | 
			
		||||
 | 
			
		||||
        # first estimate of DGT scaling
 | 
			
		||||
        if abs(dgt_slope) > 0.001: # check for zero value due to flat dgt
 | 
			
		||||
@@ -770,7 +879,7 @@ class Edfa(Node):
 | 
			
		||||
        return g1st - voa + array(self.interpol_dgt) * dgts3
 | 
			
		||||
 | 
			
		||||
    def propagate(self, pref, *carriers):
 | 
			
		||||
        """add ase noise to the propagating carriers of SpectralInformation"""
 | 
			
		||||
        """add ASE noise to the propagating carriers of :class:`.info.SpectralInformation`"""
 | 
			
		||||
        pin = array([c.power.signal+c.power.nli+c.power.ase for c in carriers]) # pin in W
 | 
			
		||||
        freq = array([c.frequency for c in carriers])
 | 
			
		||||
        brate = array([c.baud_rate for c in carriers])
 | 
			
		||||
@@ -779,22 +888,22 @@ class Edfa(Node):
 | 
			
		||||
 | 
			
		||||
        gains = db2lin(self.gprofile)
 | 
			
		||||
        carrier_ases = self.noise_profile(brate)
 | 
			
		||||
        att = db2lin(self.operational.out_voa)
 | 
			
		||||
        att = db2lin(self.out_voa)
 | 
			
		||||
 | 
			
		||||
        for gain, carrier_ase, carrier in zip(gains, carrier_ases, carriers):
 | 
			
		||||
            pwr = carrier.power
 | 
			
		||||
            pwr = pwr._replace(signal=pwr.signal*gain/att,
 | 
			
		||||
                               nonlinear_interference=pwr.nli*gain/att,
 | 
			
		||||
                               amplified_spontaneous_emission=(pwr.ase+carrier_ase)*gain/att)
 | 
			
		||||
                               nli=pwr.nli*gain/att,
 | 
			
		||||
                               ase=(pwr.ase+carrier_ase)*gain/att)
 | 
			
		||||
            yield carrier._replace(power=pwr)
 | 
			
		||||
 | 
			
		||||
    def update_pref(self, pref):
 | 
			
		||||
        return pref._replace(p_span0=pref.p0,
 | 
			
		||||
                            p_spani=pref.pi + self.effective_gain - self.operational.out_voa)
 | 
			
		||||
        return pref._replace(p_span0=pref.p_span0,
 | 
			
		||||
                            p_spani=pref.p_spani + self.effective_gain - self.out_voa)
 | 
			
		||||
 | 
			
		||||
    def __call__(self, spectral_info):
 | 
			
		||||
        self.carriers_in = spectral_info.carriers
 | 
			
		||||
        carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers))
 | 
			
		||||
        pref = self.update_pref(spectral_info.pref)
 | 
			
		||||
        self.carriers_out = carriers
 | 
			
		||||
        return spectral_info.update(carriers=carriers, pref=pref)
 | 
			
		||||
        return spectral_info._replace(carriers=carriers, pref=pref)
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ This module contains functionality for specifying equipment.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
from numpy import clip, polyval
 | 
			
		||||
from sys import exit
 | 
			
		||||
from operator import itemgetter
 | 
			
		||||
from math import isclose
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
@@ -17,64 +16,169 @@ from json import load
 | 
			
		||||
from gnpy.core.utils import lin2db, db2lin, load_json
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
from gnpy.core.elements import Edfa
 | 
			
		||||
from gnpy.core.exceptions import EquipmentConfigError
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
Model_vg = namedtuple('Model_vg', 'nf1 nf2 delta_p')
 | 
			
		||||
Model_fg = namedtuple('Model_fg', 'nf0')
 | 
			
		||||
Model_openroadm = namedtuple('Model_openroadm', 'nf_coef')
 | 
			
		||||
Fiber = namedtuple('Fiber', 'type_variety dispersion gamma')
 | 
			
		||||
Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \
 | 
			
		||||
                             max_loss padding EOL con_in con_out')
 | 
			
		||||
Transceiver = namedtuple('Transceiver', 'type_variety frequency mode')
 | 
			
		||||
Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target add_drop_osnr')
 | 
			
		||||
SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \
 | 
			
		||||
                       power_dbm power_range_db tx_osnr sys_margins')
 | 
			
		||||
AmpBase = namedtuple(
 | 
			
		||||
    'AmpBase',
 | 
			
		||||
    'type_variety type_def gain_flatmax gain_min p_max'
 | 
			
		||||
    ' nf_model nf_fit_coeff nf_ripple dgt gain_ripple out_voa_auto allowed_for_design')
 | 
			
		||||
class Amp(AmpBase):
 | 
			
		||||
    def __new__(cls,
 | 
			
		||||
            type_variety, type_def, gain_flatmax, gain_min, p_max, nf_model=None,
 | 
			
		||||
            nf_fit_coeff=None, nf_ripple=None, dgt=None, gain_ripple=None,
 | 
			
		||||
             out_voa_auto=False, allowed_for_design=True):
 | 
			
		||||
        return super().__new__(cls,
 | 
			
		||||
            type_variety, type_def, gain_flatmax, gain_min, p_max,
 | 
			
		||||
            nf_model, nf_fit_coeff, nf_ripple, dgt, gain_ripple,
 | 
			
		||||
            out_voa_auto, allowed_for_design)
 | 
			
		||||
Model_hybrid = namedtuple('Model_hybrid', 'nf_ram gain_ram edfa_variety')
 | 
			
		||||
Model_dual_stage = namedtuple('Model_dual_stage', 'preamp_variety booster_variety')
 | 
			
		||||
 | 
			
		||||
class common:
 | 
			
		||||
    def update_attr(self, default_values, kwargs, name):
 | 
			
		||||
        clean_kwargs = {k:v for k, v in kwargs.items() if v != ''}
 | 
			
		||||
        for k, v in default_values.items():
 | 
			
		||||
            setattr(self, k, clean_kwargs.get(k, v))
 | 
			
		||||
            if k not in clean_kwargs and name != 'Amp':
 | 
			
		||||
                print(f'\x1b[1;31;40m'+
 | 
			
		||||
                      f'\n WARNING missing {k} attribute in eqpt_config.json[{name}]'+
 | 
			
		||||
                      f'\n default value is {k} = {v}'+
 | 
			
		||||
                      f'\x1b[0m')
 | 
			
		||||
                time.sleep(1)
 | 
			
		||||
 | 
			
		||||
class SI(common):
 | 
			
		||||
    default_values =\
 | 
			
		||||
    {
 | 
			
		||||
        "f_min":            191.35e12,
 | 
			
		||||
        "f_max":            196.1e12,
 | 
			
		||||
        "baud_rate":        32e9,
 | 
			
		||||
        "spacing":          50e9,
 | 
			
		||||
        "power_dbm":        0,
 | 
			
		||||
        "power_range_db":   [0, 0, 0.5],
 | 
			
		||||
        "roll_off":         0.15,
 | 
			
		||||
        "tx_osnr":          45,
 | 
			
		||||
        "sys_margins":      0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.update_attr(self.default_values, kwargs, 'SI')
 | 
			
		||||
 | 
			
		||||
class Span(common):
 | 
			
		||||
    default_values = \
 | 
			
		||||
    {
 | 
			
		||||
        'power_mode':                       True,
 | 
			
		||||
        'delta_power_range_db':             None,
 | 
			
		||||
        'max_fiber_lineic_loss_for_raman':  0.25,
 | 
			
		||||
        'target_extended_gain':             2.5,
 | 
			
		||||
        'max_length':                       150,
 | 
			
		||||
        'length_units':                     'km',
 | 
			
		||||
        'max_loss':                         None,
 | 
			
		||||
        'padding':                          10,
 | 
			
		||||
        'EOL':                              0,
 | 
			
		||||
        'con_in':                           0,
 | 
			
		||||
        'con_out':                          0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.update_attr(self.default_values, kwargs, 'Span')
 | 
			
		||||
 | 
			
		||||
class Roadm(common):
 | 
			
		||||
    default_values = \
 | 
			
		||||
    {
 | 
			
		||||
        'target_pch_out_db':   -17,
 | 
			
		||||
        'add_drop_osnr':       100,
 | 
			
		||||
        'restrictions': {
 | 
			
		||||
            'preamp_variety_list':[],
 | 
			
		||||
            'booster_variety_list':[]
 | 
			
		||||
            }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.update_attr(self.default_values, kwargs, 'Roadm')
 | 
			
		||||
 | 
			
		||||
class Transceiver(common):
 | 
			
		||||
    default_values = \
 | 
			
		||||
    {
 | 
			
		||||
        'type_variety': None,
 | 
			
		||||
        'frequency':    None,
 | 
			
		||||
        'mode':         {}
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.update_attr(self.default_values, kwargs, 'Transceiver')
 | 
			
		||||
 | 
			
		||||
class Fiber(common):
 | 
			
		||||
    default_values = \
 | 
			
		||||
    {
 | 
			
		||||
        'type_variety':  '',
 | 
			
		||||
        'dispersion':    None,
 | 
			
		||||
        'gamma':         0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.update_attr(self.default_values, kwargs, 'Fiber')
 | 
			
		||||
 | 
			
		||||
class RamanFiber(common):
 | 
			
		||||
    default_values = \
 | 
			
		||||
    {
 | 
			
		||||
        'type_variety':  '',
 | 
			
		||||
        'dispersion':    None,
 | 
			
		||||
        'gamma':         0,
 | 
			
		||||
        'raman_efficiency': None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.update_attr(self.default_values, kwargs, 'RamanFiber')
 | 
			
		||||
        for param in ('cr', 'frequency_offset'):
 | 
			
		||||
            if param not in self.raman_efficiency:
 | 
			
		||||
                raise EquipmentConfigError(f'RamanFiber.raman_efficiency: missing "{param}" parameter')
 | 
			
		||||
        if self.raman_efficiency['frequency_offset'] != sorted(self.raman_efficiency['frequency_offset']):
 | 
			
		||||
            raise EquipmentConfigError(f'RamanFiber.raman_efficiency.frequency_offset is not sorted')
 | 
			
		||||
 | 
			
		||||
class Amp(common):
 | 
			
		||||
    default_values = \
 | 
			
		||||
    {
 | 
			
		||||
        'f_min':                191.35e12,
 | 
			
		||||
        'f_max':                196.1e12,
 | 
			
		||||
        'type_variety':         '',
 | 
			
		||||
        'type_def':             '',
 | 
			
		||||
        'gain_flatmax':         None,
 | 
			
		||||
        'gain_min':             None,
 | 
			
		||||
        'p_max':                None,
 | 
			
		||||
        'nf_model':             None,
 | 
			
		||||
        'dual_stage_model':     None,
 | 
			
		||||
        'nf_fit_coeff':         None,
 | 
			
		||||
        'nf_ripple':            None,
 | 
			
		||||
        'dgt':                  None,
 | 
			
		||||
        'gain_ripple':          None,
 | 
			
		||||
        'out_voa_auto':         False,
 | 
			
		||||
        'allowed_for_design':   False,
 | 
			
		||||
        'raman':                False
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.update_attr(self.default_values, kwargs, 'Amp')
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_advanced_json(cls, filename, **kwargs):
 | 
			
		||||
        with open(filename, encoding='utf-8') as f:
 | 
			
		||||
            json_data = load(f)
 | 
			
		||||
        return cls(**{**kwargs, **json_data, 'type_def':None, 'nf_model':None})
 | 
			
		||||
    def from_json(cls, filename, **kwargs):
 | 
			
		||||
        config = Path(filename).parent / 'default_edfa_config.json'
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_default_json(cls, filename, **kwargs):
 | 
			
		||||
        with open(filename, encoding='utf-8') as f:
 | 
			
		||||
            json_data = load(f)
 | 
			
		||||
        type_variety = kwargs['type_variety']
 | 
			
		||||
        type_def = kwargs.get('type_def', 'variable_gain') #default compatibility with older json eqpt files
 | 
			
		||||
        type_def = kwargs.get('type_def', 'variable_gain') # default compatibility with older json eqpt files
 | 
			
		||||
        nf_def = None
 | 
			
		||||
        dual_stage_def = None
 | 
			
		||||
 | 
			
		||||
        if type_def == 'fixed_gain':
 | 
			
		||||
            try:
 | 
			
		||||
                nf0 = kwargs.pop('nf0')
 | 
			
		||||
            except KeyError: #nf0 is expected for a fixed gain amp
 | 
			
		||||
                print(f'missing nf0 value input for amplifier: {type_variety} in eqpt_config.json')
 | 
			
		||||
                exit()
 | 
			
		||||
            try: #remove all remaining nf inputs
 | 
			
		||||
                del kwargs['nf_min']
 | 
			
		||||
                del kwargs['nf_max']
 | 
			
		||||
            except KeyError: pass #nf_min and nf_max are not needed for fixed gain amp
 | 
			
		||||
                raise EquipmentConfigError(f'missing nf0 value input for amplifier: {type_variety} in equipment config')
 | 
			
		||||
            for k in ('nf_min', 'nf_max'):
 | 
			
		||||
                try:
 | 
			
		||||
                    del kwargs[k]
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    pass
 | 
			
		||||
            nf_def = Model_fg(nf0)
 | 
			
		||||
        elif type_def == 'advanced_model':
 | 
			
		||||
            config = Path(filename).parent / kwargs.pop('advanced_config_from_json')
 | 
			
		||||
        elif type_def == 'variable_gain':
 | 
			
		||||
            gain_min, gain_max = kwargs['gain_min'], kwargs['gain_flatmax']
 | 
			
		||||
            try: #nf_min and nf_max are expected for a variable gain amp
 | 
			
		||||
                nf_min = kwargs.pop('nf_min')
 | 
			
		||||
                nf_max = kwargs.pop('nf_max')
 | 
			
		||||
            except KeyError:
 | 
			
		||||
                print(f'missing nf_min/max value input for amplifier: {type_variety} in eqpt_config.json')
 | 
			
		||||
                exit()
 | 
			
		||||
                raise EquipmentConfigError(f'missing nf_min or nf_max value input for amplifier: {type_variety} in equipment config')
 | 
			
		||||
            try: #remove all remaining nf inputs
 | 
			
		||||
                del kwargs['nf0']
 | 
			
		||||
            except KeyError: pass #nf0 is not needed for variable gain amp
 | 
			
		||||
@@ -84,19 +188,28 @@ class Amp(AmpBase):
 | 
			
		||||
            try:
 | 
			
		||||
                nf_coef = kwargs.pop('nf_coef')
 | 
			
		||||
            except KeyError: #nf_coef is expected for openroadm amp
 | 
			
		||||
                print(f'missing nf_coef input for amplifier: {type_variety} in eqpt_config.json')
 | 
			
		||||
                exit()
 | 
			
		||||
                raise EquipmentConfigError(f'missing nf_coef input for amplifier: {type_variety} in equipment config')
 | 
			
		||||
            nf_def = Model_openroadm(nf_coef)
 | 
			
		||||
        return cls(**{**kwargs, **json_data, 'nf_model': nf_def})
 | 
			
		||||
        elif type_def == 'dual_stage':
 | 
			
		||||
            try: #nf_ram and gain_ram are expected for a hybrid amp
 | 
			
		||||
                preamp_variety = kwargs.pop('preamp_variety')
 | 
			
		||||
                booster_variety = kwargs.pop('booster_variety')
 | 
			
		||||
            except KeyError:
 | 
			
		||||
                raise EquipmentConfigError(f'missing preamp/booster variety input for amplifier: {type_variety} in equipment config')
 | 
			
		||||
            dual_stage_def = Model_dual_stage(preamp_variety, booster_variety)
 | 
			
		||||
 | 
			
		||||
        with open(config, encoding='utf-8') as f:
 | 
			
		||||
            json_data = load(f)
 | 
			
		||||
 | 
			
		||||
        return cls(**{**kwargs, **json_data,
 | 
			
		||||
            'nf_model': nf_def, 'dual_stage_model': dual_stage_def})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
 | 
			
		||||
    if nf_min < -10:
 | 
			
		||||
        print(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}')
 | 
			
		||||
        exit()
 | 
			
		||||
        raise EquipmentConfigError(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}')
 | 
			
		||||
    if nf_max < -10:
 | 
			
		||||
        print(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}')
 | 
			
		||||
        exit()
 | 
			
		||||
        raise EquipmentConfigError(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}')
 | 
			
		||||
 | 
			
		||||
    # NF estimation model based on nf_min and nf_max
 | 
			
		||||
    # delta_p:  max power dB difference between first and second stage coils
 | 
			
		||||
@@ -111,8 +224,7 @@ def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
 | 
			
		||||
    nf1 = lin2db(db2lin(nf_min) - db2lin(nf2)/db2lin(g1a_max))
 | 
			
		||||
 | 
			
		||||
    if nf1 < 4:
 | 
			
		||||
        print(f'First coil value too low {nf1} for amplifier {type_variety}')
 | 
			
		||||
        exit()
 | 
			
		||||
        raise EquipmentConfigError(f'First coil value too low {nf1} for amplifier {type_variety}')
 | 
			
		||||
 | 
			
		||||
    # Check 1 dB < delta_p < 6 dB to ensure nf_min and nf_max values make sense.
 | 
			
		||||
    # There shouldn't be high nf differences between the two coils:
 | 
			
		||||
@@ -123,21 +235,18 @@ def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
 | 
			
		||||
        g1a_max = lin2db(db2lin(nf2) / (db2lin(nf_min) - db2lin(nf1)))
 | 
			
		||||
        delta_p = gain_max - g1a_max
 | 
			
		||||
        g1a_min = gain_min - (gain_max-gain_min) - delta_p
 | 
			
		||||
        if not 1 < delta_p < 6:
 | 
			
		||||
            print(f'Computed \N{greek capital letter delta}P invalid \
 | 
			
		||||
        if not 1 < delta_p < 11:
 | 
			
		||||
            raise EquipmentConfigError(f'Computed \N{greek capital letter delta}P invalid \
 | 
			
		||||
                \n 1st coil vs 2nd coil calculated DeltaP {delta_p:.2f} for \
 | 
			
		||||
                \n amplifier {type_variety} is not valid: revise inputs \
 | 
			
		||||
                \n calculated 1st coil NF = {nf1:.2f}, 2nd coil NF = {nf2:.2f}')
 | 
			
		||||
            exit()
 | 
			
		||||
    # Check calculated values for nf1 and nf2
 | 
			
		||||
    calc_nf_min = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_max))
 | 
			
		||||
    if not isclose(nf_min, calc_nf_min, abs_tol=0.01):
 | 
			
		||||
        print(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}')
 | 
			
		||||
        exit()
 | 
			
		||||
        raise EquipmentConfigError(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}')
 | 
			
		||||
    calc_nf_max = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_min))
 | 
			
		||||
    if not isclose(nf_max, calc_nf_max, abs_tol=0.01):
 | 
			
		||||
        print(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}')
 | 
			
		||||
        exit()
 | 
			
		||||
        raise EquipmentConfigError(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}')
 | 
			
		||||
 | 
			
		||||
    return nf1, nf2, delta_p
 | 
			
		||||
 | 
			
		||||
@@ -145,7 +254,7 @@ def edfa_nf(gain_target, variety_type, equipment):
 | 
			
		||||
    amp_params = equipment['Edfa'][variety_type]
 | 
			
		||||
    amp = Edfa(
 | 
			
		||||
            uid = f'calc_NF',
 | 
			
		||||
            params = amp_params._asdict(),
 | 
			
		||||
            params = amp_params.__dict__,
 | 
			
		||||
            operational = {
 | 
			
		||||
                'gain_target': gain_target,
 | 
			
		||||
                'tilt_target': 0
 | 
			
		||||
@@ -159,7 +268,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
 | 
			
		||||
    """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
 | 
			
		||||
@@ -172,20 +281,18 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
 | 
			
		||||
            trx_params = {**mode_params}
 | 
			
		||||
            # sanity check: spacing baudrate must be smaller than min spacing
 | 
			
		||||
            if trx_params['baud_rate'] > trx_params['min_spacing'] :
 | 
			
		||||
                msg = 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}.'
 | 
			
		||||
                print(msg)
 | 
			
		||||
                exit()
 | 
			
		||||
                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} 
 | 
			
		||||
                           "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']
 | 
			
		||||
 | 
			
		||||
@@ -195,9 +302,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
 | 
			
		||||
        # print(f'spacing {temp}')
 | 
			
		||||
    except StopIteration :
 | 
			
		||||
        if error_message:
 | 
			
		||||
            print(f'could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library')
 | 
			
		||||
            print('Computation stopped.')
 | 
			
		||||
            exit()
 | 
			
		||||
            raise EquipmentConfigError(f'Computation stoped: could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library')
 | 
			
		||||
        else:
 | 
			
		||||
            # default transponder charcteristics
 | 
			
		||||
            # mainly used with transmission_main_example.py
 | 
			
		||||
@@ -214,7 +319,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
 | 
			
		||||
            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
 | 
			
		||||
@@ -222,8 +327,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
 | 
			
		||||
def automatic_spacing(baud_rate):
 | 
			
		||||
    """return the min possible channel spacing for a given baud rate"""
 | 
			
		||||
    # TODO : this should parametrized in a cfg file
 | 
			
		||||
    spacing_list = [(33e9,37.5e9), (38e9,50e9), (50e9,62.5e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples
 | 
			
		||||
                                                #[(max_baud_rate, spacing_for_this_baud_rate)]
 | 
			
		||||
    # list of possible tuples [(max_baud_rate, spacing_for_this_baud_rate)]
 | 
			
		||||
    spacing_list = [(33e9, 37.5e9), (38e9, 50e9), (50e9, 62.5e9), (67e9, 75e9), (92e9, 100e9)]
 | 
			
		||||
    return min((s[1] for s in spacing_list if s[0] > baud_rate), default=baud_rate*1.2)
 | 
			
		||||
 | 
			
		||||
def automatic_nch(f_min, f_max, spacing):
 | 
			
		||||
@@ -243,6 +348,34 @@ def update_trx_osnr(equipment):
 | 
			
		||||
            m['OSNR'] = m['OSNR'] + equipment['SI']['default'].sys_margins
 | 
			
		||||
    return equipment
 | 
			
		||||
 | 
			
		||||
def update_dual_stage(equipment):
 | 
			
		||||
    edfa_dict = equipment['Edfa']
 | 
			
		||||
    for edfa in edfa_dict.values():
 | 
			
		||||
        if edfa.type_def == 'dual_stage':
 | 
			
		||||
            edfa_preamp = edfa_dict[edfa.dual_stage_model.preamp_variety]
 | 
			
		||||
            edfa_booster = edfa_dict[edfa.dual_stage_model.booster_variety]
 | 
			
		||||
            for key, value in edfa_preamp.__dict__.items():
 | 
			
		||||
                attr_k = 'preamp_' + key
 | 
			
		||||
                setattr(edfa, attr_k, value)
 | 
			
		||||
            for key, value in edfa_booster.__dict__.items():
 | 
			
		||||
                attr_k = 'booster_' + key
 | 
			
		||||
                setattr(edfa, attr_k, value)
 | 
			
		||||
            edfa.p_max = edfa_booster.p_max
 | 
			
		||||
            edfa.gain_flatmax = edfa_booster.gain_flatmax + edfa_preamp.gain_flatmax
 | 
			
		||||
            if edfa.gain_min < edfa_preamp.gain_min:
 | 
			
		||||
                raise EquipmentConfigError(f'Dual stage {edfa.type_variety} min gain is lower than its preamp min gain')
 | 
			
		||||
    return equipment
 | 
			
		||||
 | 
			
		||||
def roadm_restrictions_sanity_check(equipment):
 | 
			
		||||
    """ verifies that booster and preamp restrictions specified in roadm equipment are listed
 | 
			
		||||
    in the edfa.
 | 
			
		||||
    """
 | 
			
		||||
    restrictions = equipment['Roadm']['default'].restrictions['booster_variety_list'] + \
 | 
			
		||||
        equipment['Roadm']['default'].restrictions['preamp_variety_list']
 | 
			
		||||
    for amp_name in restrictions:
 | 
			
		||||
        if amp_name not in equipment['Edfa']:
 | 
			
		||||
            raise EquipmentConfigError(f'ROADM restriction {amp_name} does not refer to a defined EDFA name')
 | 
			
		||||
 | 
			
		||||
def equipment_from_json(json_data, filename):
 | 
			
		||||
    """build global dictionnary eqpt_library that stores all eqpt characteristics:
 | 
			
		||||
    edfa type type_variety, fiber type_variety
 | 
			
		||||
@@ -257,15 +390,12 @@ def equipment_from_json(json_data, filename):
 | 
			
		||||
        equipment[key] = {}
 | 
			
		||||
        typ = globals()[key]
 | 
			
		||||
        for entry in entries:
 | 
			
		||||
            subkey = entry.get('type_variety', 'default')           
 | 
			
		||||
            subkey = entry.get('type_variety', 'default')
 | 
			
		||||
            if key == 'Edfa':
 | 
			
		||||
                if 'advanced_config_from_json' in entry:
 | 
			
		||||
                    config = Path(filename).parent / entry.pop('advanced_config_from_json')
 | 
			
		||||
                    equipment[key][subkey] = Amp.from_advanced_json(config, **entry)
 | 
			
		||||
                else:
 | 
			
		||||
                    config = Path(filename).parent / 'default_edfa_config.json'
 | 
			
		||||
                    equipment[key][subkey] = Amp.from_default_json(config, **entry)
 | 
			
		||||
            else:                
 | 
			
		||||
                equipment[key][subkey] = Amp.from_json(filename, **entry)
 | 
			
		||||
            else:
 | 
			
		||||
                equipment[key][subkey] = typ(**entry)
 | 
			
		||||
    equipment = update_trx_osnr(equipment)
 | 
			
		||||
    equipment = update_dual_stage(equipment)
 | 
			
		||||
    roadm_restrictions_sanity_check(equipment)
 | 
			
		||||
    return equipment
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								gnpy/core/exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								gnpy/core/exceptions.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
gnpy.core.exceptions
 | 
			
		||||
====================
 | 
			
		||||
 | 
			
		||||
Exceptions thrown by other gnpy modules
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ConfigurationError(Exception):
 | 
			
		||||
    '''User-provided configuration contains an error'''
 | 
			
		||||
 | 
			
		||||
class EquipmentConfigError(ConfigurationError):
 | 
			
		||||
    '''Incomplete or wrong configuration within the equipment library'''
 | 
			
		||||
 | 
			
		||||
class NetworkTopologyError(ConfigurationError):
 | 
			
		||||
    '''Topology of user-provided network is wrong'''
 | 
			
		||||
 | 
			
		||||
class ServiceError(Exception):
 | 
			
		||||
    '''Service of user-provided request is wrong'''
 | 
			
		||||
 | 
			
		||||
class DisjunctionError(ServiceError):
 | 
			
		||||
    '''Disjunction of user-provided request can not be satisfied'''
 | 
			
		||||
 | 
			
		||||
class SpectrumError(Exception):
 | 
			
		||||
    '''Spectrum errors of the program'''
 | 
			
		||||
 | 
			
		||||
@@ -5,7 +5,7 @@
 | 
			
		||||
gnpy.core.info
 | 
			
		||||
==============
 | 
			
		||||
 | 
			
		||||
This module contains classes for modelling SpectralInformation.
 | 
			
		||||
This module contains classes for modelling :class:`SpectralInformation`.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -16,53 +16,34 @@ from json import loads
 | 
			
		||||
from gnpy.core.utils import load_json
 | 
			
		||||
from gnpy.core.equipment import automatic_nch, automatic_spacing
 | 
			
		||||
 | 
			
		||||
class ConvenienceAccess:
 | 
			
		||||
 | 
			
		||||
    def __init_subclass__(cls):
 | 
			
		||||
        for abbrev, field in getattr(cls, '_ABBREVS', {}).items():
 | 
			
		||||
            setattr(cls, abbrev, property(lambda self, f=field: getattr(self, f)))
 | 
			
		||||
 | 
			
		||||
    def update(self, **kwargs):
 | 
			
		||||
        for abbrev, field in getattr(self, '_ABBREVS', {}).items():
 | 
			
		||||
            if abbrev in kwargs:
 | 
			
		||||
                kwargs[field] = kwargs.pop(abbrev)
 | 
			
		||||
        return self._replace(**kwargs)
 | 
			
		||||
class Power(namedtuple('Power', 'signal nli ase')):
 | 
			
		||||
    """carriers power in W"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Power(namedtuple('Power', 'signal nonlinear_interference amplified_spontaneous_emission'), ConvenienceAccess):
 | 
			
		||||
 | 
			
		||||
    _ABBREVS = {'nli': 'nonlinear_interference',
 | 
			
		||||
                'ase': 'amplified_spontaneous_emission',}
 | 
			
		||||
class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power')):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power'), ConvenienceAccess):
 | 
			
		||||
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"""
 | 
			
		||||
 | 
			
		||||
    _ABBREVS = {'channel':  'channel_number',
 | 
			
		||||
                'num_chan': 'channel_number',
 | 
			
		||||
                'ffs':      'frequency',
 | 
			
		||||
                'freq':     'frequency',}
 | 
			
		||||
 | 
			
		||||
class Pref(namedtuple('Pref', 'p_span0, p_spani'), ConvenienceAccess):
 | 
			
		||||
class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers')):
 | 
			
		||||
 | 
			
		||||
    _ABBREVS = {'p0' :  'p_span0',
 | 
			
		||||
                'pi' :  'p_spani'}
 | 
			
		||||
 | 
			
		||||
class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers'), ConvenienceAccess):
 | 
			
		||||
 | 
			
		||||
    def __new__(cls, pref=Pref(0, 0), *carriers):
 | 
			
		||||
    def __new__(cls, pref, carriers):
 | 
			
		||||
        return super().__new__(cls, pref, carriers)
 | 
			
		||||
 | 
			
		||||
def merge_input_spectral_information(*si):
 | 
			
		||||
    """mix channel combs of different baud rates and power"""
 | 
			
		||||
    #TODO
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
def create_input_spectral_information(f_min, f_max, roll_off, baud_rate, power, spacing):
 | 
			
		||||
    # pref in dB : convert power lin into power in dB
 | 
			
		||||
    pref = lin2db(power * 1e3)
 | 
			
		||||
    si = SpectralInformation(pref=Pref(pref, pref))
 | 
			
		||||
    nb_channel = automatic_nch(f_min, f_max, spacing)
 | 
			
		||||
    si = si.update(carriers=[
 | 
			
		||||
    si = SpectralInformation(
 | 
			
		||||
        pref=Pref(pref, pref, lin2db(nb_channel)),
 | 
			
		||||
        carriers=[
 | 
			
		||||
            Channel(f, (f_min+spacing*f),
 | 
			
		||||
            baud_rate, roll_off, Power(power, 0, 0)) for f in range(1,nb_channel+1)
 | 
			
		||||
            ])
 | 
			
		||||
@@ -81,11 +62,11 @@ if __name__ == '__main__':
 | 
			
		||||
    si = SpectralInformation()
 | 
			
		||||
    spacing = 0.05 # THz
 | 
			
		||||
 | 
			
		||||
    si = si.update(carriers=tuple(Channel(f+1, 191.3+spacing*(f+1), 32e9, 0.15, Power(1e-3, f, 1)) for f in range(96)))
 | 
			
		||||
    si = si._replace(carriers=tuple(Channel(f+1, 191.3+spacing*(f+1), 32e9, 0.15, Power(1e-3, f, 1)) for f in range(96)))
 | 
			
		||||
 | 
			
		||||
    print(f'si = {si}')
 | 
			
		||||
    print(f'si = {si.carriers[0].power.nli}')
 | 
			
		||||
    print(f'si = {si.carriers[20].power.nli}')
 | 
			
		||||
    si2 = si.update(carriers=tuple(c.update(power = c.power.update(nli = c.power.nli * 1e5))
 | 
			
		||||
    si2 = si._replace(carriers=tuple(c._replace(power = c.power._replace(nli = c.power.nli * 1e5))
 | 
			
		||||
                              for c in si.carriers))
 | 
			
		||||
    print(f'si2 = {si2}')
 | 
			
		||||
 
 | 
			
		||||
@@ -14,13 +14,15 @@ from numpy import arange
 | 
			
		||||
from scipy.interpolate import interp1d
 | 
			
		||||
from logging import getLogger
 | 
			
		||||
from os import path
 | 
			
		||||
from operator import itemgetter
 | 
			
		||||
from operator import itemgetter, attrgetter
 | 
			
		||||
from gnpy.core import elements
 | 
			
		||||
from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused
 | 
			
		||||
from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused, RamanFiber
 | 
			
		||||
from gnpy.core.equipment import edfa_nf
 | 
			
		||||
from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError
 | 
			
		||||
from gnpy.core.units import UNITS
 | 
			
		||||
from gnpy.core.utils import load_json, save_json, round2float, db2lin, lin2db
 | 
			
		||||
from sys import exit
 | 
			
		||||
from gnpy.core.utils import (load_json, save_json, round2float, db2lin,
 | 
			
		||||
                            merge_amplifier_restrictions)
 | 
			
		||||
from gnpy.core.science_utils import SimParams
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
 | 
			
		||||
logger = getLogger(__name__)
 | 
			
		||||
@@ -52,11 +54,12 @@ def network_from_json(json_data, equipment):
 | 
			
		||||
        variety = el_config.pop('type_variety', 'default')
 | 
			
		||||
        if typ in equipment and variety in equipment[typ]:
 | 
			
		||||
            extra_params = equipment[typ][variety]
 | 
			
		||||
            el_config.setdefault('params', {}).update(extra_params._asdict())
 | 
			
		||||
        elif typ in ['Edfa', 'Fiber']: #catch it now because the code will crash later!
 | 
			
		||||
            print( f'The {typ} of variety type {variety} was not recognized:'
 | 
			
		||||
            temp = el_config.setdefault('params', {})
 | 
			
		||||
            temp = merge_amplifier_restrictions(temp, extra_params.__dict__)
 | 
			
		||||
            el_config['params'] = temp
 | 
			
		||||
        elif typ in ['Edfa', 'Fiber']: # catch it now because the code will crash later!
 | 
			
		||||
            raise ConfigurationError(f'The {typ} of variety type {variety} was not recognized:'
 | 
			
		||||
                    '\nplease check it is properly defined in the eqpt_config json file')
 | 
			
		||||
            exit()
 | 
			
		||||
        cls = getattr(elements, typ)
 | 
			
		||||
        el = cls(**el_config)
 | 
			
		||||
        g.add_node(el)
 | 
			
		||||
@@ -66,11 +69,13 @@ def network_from_json(json_data, equipment):
 | 
			
		||||
    for cx in json_data['connections']:
 | 
			
		||||
        from_node, to_node = cx['from_node'], cx['to_node']
 | 
			
		||||
        try:
 | 
			
		||||
            g.add_edge(nodes[from_node], nodes[to_node])
 | 
			
		||||
            if isinstance(nodes[from_node], Fiber):
 | 
			
		||||
                edge_length = nodes[from_node].params.length
 | 
			
		||||
            else:
 | 
			
		||||
                edge_length = 0.01
 | 
			
		||||
            g.add_edge(nodes[from_node], nodes[to_node], weight = edge_length)
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            msg = f'In {__name__} network_from_json function:\n\tcan not find {from_node} or {to_node} defined in {cx}'
 | 
			
		||||
            print(msg)
 | 
			
		||||
            exit(1)
 | 
			
		||||
            raise NetworkTopologyError(f'can not find {from_node} or {to_node} defined in {cx}')
 | 
			
		||||
 | 
			
		||||
    return g
 | 
			
		||||
 | 
			
		||||
@@ -87,16 +92,27 @@ def network_to_json(network):
 | 
			
		||||
    data.update(connections)
 | 
			
		||||
    return data
 | 
			
		||||
 | 
			
		||||
def select_edfa(gain_target, power_target, equipment):
 | 
			
		||||
def select_edfa(raman_allowed, gain_target, power_target, equipment, uid, restrictions=None):
 | 
			
		||||
    """amplifer selection algorithm
 | 
			
		||||
    @Orange Jean-Luc Augé
 | 
			
		||||
    """
 | 
			
		||||
    Edfa_list = namedtuple('Edfa_list', 'variety power gain nf')
 | 
			
		||||
    TARGET_EXTENDED_GAIN = 2.1
 | 
			
		||||
    #MAX_EXTENDED_GAIN = 5
 | 
			
		||||
    edfa_dict = equipment['Edfa']
 | 
			
		||||
    Edfa_list = namedtuple('Edfa_list', 'variety power gain_min nf')
 | 
			
		||||
    TARGET_EXTENDED_GAIN = equipment['Span']['default'].target_extended_gain
 | 
			
		||||
 | 
			
		||||
    # for roadm restriction only: create a dict including not allowed for design amps
 | 
			
		||||
    # because main use case is to have specific radm amp which are not allowed for ILA
 | 
			
		||||
    # with the auto design
 | 
			
		||||
    edfa_dict = {name: amp for (name, amp) in equipment['Edfa'].items()
 | 
			
		||||
        if restrictions is None or name in restrictions}
 | 
			
		||||
 | 
			
		||||
    pin = power_target - gain_target
 | 
			
		||||
 | 
			
		||||
    # create 2 list of available amplifiers with relevant attributes for their selection
 | 
			
		||||
 | 
			
		||||
    # edfa list with:
 | 
			
		||||
    # extended gain min allowance of 3dB: could be parametrized, but a bit complex
 | 
			
		||||
    # extended gain max allowance TARGET_EXTENDED_GAIN is coming from eqpt_config.json
 | 
			
		||||
    # power attribut include power AND gain limitations
 | 
			
		||||
    edfa_list = [Edfa_list(
 | 
			
		||||
                variety=edfa_variety,
 | 
			
		||||
                power=min(
 | 
			
		||||
@@ -106,88 +122,118 @@ def select_edfa(gain_target, power_target, equipment):
 | 
			
		||||
                    edfa.p_max
 | 
			
		||||
                    )
 | 
			
		||||
                    -power_target,
 | 
			
		||||
                gain=edfa.gain_flatmax-gain_target,
 | 
			
		||||
                gain_min=
 | 
			
		||||
                    gain_target+3
 | 
			
		||||
                    -edfa.gain_min,
 | 
			
		||||
                nf=edfa_nf(gain_target, edfa_variety, equipment)) \
 | 
			
		||||
                for edfa_variety, edfa in edfa_dict.items()
 | 
			
		||||
                if edfa.allowed_for_design]
 | 
			
		||||
                if ((edfa.allowed_for_design or restrictions is not None) and not edfa.raman)]
 | 
			
		||||
 | 
			
		||||
    acceptable_gain_list = \
 | 
			
		||||
    list(filter(lambda x : x.gain>-TARGET_EXTENDED_GAIN, edfa_list))
 | 
			
		||||
    if len(acceptable_gain_list) < 1:
 | 
			
		||||
        #no amplifier satisfies the required gain, so pick the highest gain:
 | 
			
		||||
        gain_max = max(edfa_list, key=itemgetter(2)).gain
 | 
			
		||||
        #pick up all amplifiers that share this max gain:
 | 
			
		||||
        acceptable_gain_list = \
 | 
			
		||||
        list(filter(lambda x : x.gain-gain_max>-0.1, edfa_list))
 | 
			
		||||
    acceptable_power_list = \
 | 
			
		||||
    list(filter(lambda x : x.power>=0, acceptable_gain_list))
 | 
			
		||||
    #consider a Raman list because of different gain_min requirement: 
 | 
			
		||||
    #do not allow extended gain min for Raman
 | 
			
		||||
    raman_list = [Edfa_list(
 | 
			
		||||
                variety=edfa_variety,
 | 
			
		||||
                power=min(
 | 
			
		||||
                    pin
 | 
			
		||||
                    +edfa.gain_flatmax
 | 
			
		||||
                    +TARGET_EXTENDED_GAIN,
 | 
			
		||||
                    edfa.p_max
 | 
			
		||||
                    )
 | 
			
		||||
                    -power_target,
 | 
			
		||||
                gain_min=
 | 
			
		||||
                    gain_target
 | 
			
		||||
                    -edfa.gain_min,
 | 
			
		||||
                nf=edfa_nf(gain_target, edfa_variety, equipment))
 | 
			
		||||
                for edfa_variety, edfa in edfa_dict.items()
 | 
			
		||||
                if (edfa.allowed_for_design and edfa.raman)] \
 | 
			
		||||
                if raman_allowed else []
 | 
			
		||||
 | 
			
		||||
    #merge raman and edfa lists
 | 
			
		||||
    amp_list = edfa_list + raman_list
 | 
			
		||||
 | 
			
		||||
    #filter on min gain limitation: 
 | 
			
		||||
    acceptable_gain_min_list = [x for x in amp_list if x.gain_min>0]
 | 
			
		||||
 | 
			
		||||
    if len(acceptable_gain_min_list) < 1:
 | 
			
		||||
        #do not take this empty list into account for the rest of the code
 | 
			
		||||
        #but issue a warning to the user and do not consider Raman
 | 
			
		||||
        #Raman below min gain should not be allowed because i is meant to be a design requirement
 | 
			
		||||
        #and raman padding at the amplifier input is impossible!
 | 
			
		||||
 | 
			
		||||
        if len(edfa_list) < 1:
 | 
			
		||||
            raise ConfigurationError(f'auto_design could not find any amplifier \
 | 
			
		||||
                    to satisfy min gain requirement in node {uid} \
 | 
			
		||||
                    please increase span fiber padding')
 | 
			
		||||
        else:
 | 
			
		||||
            # TODO: convert to logging
 | 
			
		||||
            print(
 | 
			
		||||
                f'\x1b[1;31;40m'\
 | 
			
		||||
                + f'WARNING: target gain in node {uid} is below all available amplifiers min gain: \
 | 
			
		||||
                    amplifier input padding will be assumed, consider increase span fiber padding instead'\
 | 
			
		||||
                + '\x1b[0m'
 | 
			
		||||
                )
 | 
			
		||||
            acceptable_gain_min_list = edfa_list
 | 
			
		||||
 | 
			
		||||
    #filter on gain+power limitation:
 | 
			
		||||
    #this list checks both the gain and the power requirement
 | 
			
		||||
    #because of the way .power is calculated in the list
 | 
			
		||||
    acceptable_power_list = [x for x in acceptable_gain_min_list if x.power>0]
 | 
			
		||||
    if len(acceptable_power_list) < 1:
 | 
			
		||||
        #no amplifier satisfies the required power, so pick the highest power:
 | 
			
		||||
        power_max = \
 | 
			
		||||
        max(acceptable_gain_list, key=itemgetter(1)).power
 | 
			
		||||
        #pick up all amplifiers that share this max gain:
 | 
			
		||||
        acceptable_power_list = \
 | 
			
		||||
        list(filter(lambda x : x.power-power_max>-0.1, acceptable_gain_list))
 | 
			
		||||
        #no amplifier satisfies the required power, so pick the highest power(s):
 | 
			
		||||
        power_max = max(acceptable_gain_min_list, key=attrgetter('power')).power
 | 
			
		||||
        #check and pick if other amplifiers may have a similar gain/power
 | 
			
		||||
        #allow a 0.3dB power range 
 | 
			
		||||
        #this allows to chose an amplifier with a better NF subsequentely
 | 
			
		||||
        acceptable_power_list = [x for x in acceptable_gain_min_list
 | 
			
		||||
                                 if x.power-power_max>-0.3]
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    # gain and power requirements are resolved,
 | 
			
		||||
    #       =>chose the amp with the best NF among the acceptable ones:
 | 
			
		||||
    return min(acceptable_power_list, key=itemgetter(3)).variety #filter on NF
 | 
			
		||||
    selected_edfa = min(acceptable_power_list, key=attrgetter('nf')) #filter on NF
 | 
			
		||||
    #check what are the gain and power limitations of this amp
 | 
			
		||||
    power_reduction = round(min(selected_edfa.power, 0),2)
 | 
			
		||||
    if power_reduction < -0.5:
 | 
			
		||||
        print(
 | 
			
		||||
            f'\x1b[1;31;40m'\
 | 
			
		||||
            + f'WARNING: target gain and power in node {uid}\n \
 | 
			
		||||
    is beyond all available amplifiers capabilities and/or extended_gain_range:\n\
 | 
			
		||||
    a power reduction of {power_reduction} is applied\n'\
 | 
			
		||||
            + '\x1b[0m'
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def set_roadm_loss(network, equipment, pref_ch_db):
 | 
			
		||||
    roadms = [roadm for roadm in network if isinstance(roadm, Roadm)]
 | 
			
		||||
    power_mode = equipment['Spans']['default'].power_mode
 | 
			
		||||
    default_roadm_loss = equipment['Roadms']['default'].gain_mode_default_loss
 | 
			
		||||
    pout_target = equipment['Roadms']['default'].power_mode_pout_target
 | 
			
		||||
    roadm_loss = pref_ch_db - pout_target
 | 
			
		||||
    return selected_edfa.variety, power_reduction
 | 
			
		||||
 | 
			
		||||
    for roadm in roadms:
 | 
			
		||||
        if power_mode:
 | 
			
		||||
            roadm.loss = roadm_loss
 | 
			
		||||
            roadm.target_pch_out_db = pout_target
 | 
			
		||||
        elif roadm.loss == None:
 | 
			
		||||
            roadm.loss = default_roadm_loss
 | 
			
		||||
 | 
			
		||||
def target_power(dp_from_gain, network, node, equipment): #get_fiber_dp
 | 
			
		||||
def target_power(network, node, equipment): #get_fiber_dp
 | 
			
		||||
    SPAN_LOSS_REF = 20
 | 
			
		||||
    POWER_SLOPE = 0.3
 | 
			
		||||
    power_mode = equipment['Spans']['default'].power_mode
 | 
			
		||||
    dp_range = list(equipment['Spans']['default'].delta_power_range_db)
 | 
			
		||||
    power_mode = equipment['Span']['default'].power_mode
 | 
			
		||||
    dp_range = list(equipment['Span']['default'].delta_power_range_db)
 | 
			
		||||
    node_loss = span_loss(network, node)
 | 
			
		||||
 | 
			
		||||
    dp_gain_mode = 0
 | 
			
		||||
    try:
 | 
			
		||||
        dp_power_mode = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2])
 | 
			
		||||
        dp_power_mode = max(dp_range[0], dp_power_mode)
 | 
			
		||||
        dp_power_mode = min(dp_range[1], dp_power_mode)
 | 
			
		||||
        dp = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2])
 | 
			
		||||
        dp = max(dp_range[0], dp)
 | 
			
		||||
        dp = min(dp_range[1], dp)
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        print(f'invalid delta_power_range_db definition in eqpt_config[Spans]'
 | 
			
		||||
        raise ConfigurationError(f'invalid delta_power_range_db definition in eqpt_config[Span]'
 | 
			
		||||
              f'delta_power_range_db: [lower_bound, upper_bound, step]')
 | 
			
		||||
        exit()
 | 
			
		||||
 | 
			
		||||
    if dp_from_gain:
 | 
			
		||||
        dp_power_mode = dp_from_gain
 | 
			
		||||
        dp_gain_mode = dp_from_gain
 | 
			
		||||
    if isinstance(node, Roadm):
 | 
			
		||||
        dp_power_mode = 0
 | 
			
		||||
 | 
			
		||||
    dp = dp_power_mode if power_mode else dp_gain_mode
 | 
			
		||||
    #print(f'{repr(node)} delta power in:\n{dp}dB')
 | 
			
		||||
        dp = 0
 | 
			
		||||
 | 
			
		||||
    return dp
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def prev_node_generator(network, node):
 | 
			
		||||
    """fused spans interest:
 | 
			
		||||
    iterate over all predecessors while they are Fused or Fiber type"""
 | 
			
		||||
    try:
 | 
			
		||||
        prev_node = next(n for n in network.predecessors(node))
 | 
			
		||||
    except StopIteration:
 | 
			
		||||
        msg = f'In {__name__} prev_node_generator function:\n\t{node.uid} is not properly connected, please check network topology'
 | 
			
		||||
        print(msg)
 | 
			
		||||
        logger.critical(msg)
 | 
			
		||||
        exit(1)
 | 
			
		||||
        raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology')
 | 
			
		||||
    # yield and re-iterate
 | 
			
		||||
    if isinstance(prev_node, Fused) or isinstance(node, Fused):
 | 
			
		||||
    if isinstance(prev_node, Fused) or isinstance(node, Fused) and not isinstance(prev_node, Roadm):
 | 
			
		||||
        yield prev_node
 | 
			
		||||
        yield from prev_node_generator(network, prev_node)
 | 
			
		||||
    else:
 | 
			
		||||
@@ -199,10 +245,9 @@ def next_node_generator(network, node):
 | 
			
		||||
    try:
 | 
			
		||||
        next_node = next(n for n in network.successors(node))
 | 
			
		||||
    except StopIteration:
 | 
			
		||||
        print(f'In {__name__} next_node_generator function:\n\t{node.uid}  is not properly connected, please check network topology')
 | 
			
		||||
        exit(1)        
 | 
			
		||||
        raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology')
 | 
			
		||||
    # yield and re-iterate
 | 
			
		||||
    if isinstance(next_node, Fused) or isinstance(node, Fused):
 | 
			
		||||
    if isinstance(next_node, Fused) or isinstance(node, Fused) and not isinstance(next_node, Roadm):
 | 
			
		||||
        yield next_node
 | 
			
		||||
        yield from next_node_generator(network, next_node)
 | 
			
		||||
    else:
 | 
			
		||||
@@ -244,23 +289,22 @@ def find_last_node(network, node):
 | 
			
		||||
        pass
 | 
			
		||||
    return this_node
 | 
			
		||||
 | 
			
		||||
def set_amplifier_voa(amp, pref_total_db, power_mode):
 | 
			
		||||
    VOA_MARGIN = 0
 | 
			
		||||
    if amp.operational.out_voa is None:
 | 
			
		||||
def set_amplifier_voa(amp, power_target, power_mode):
 | 
			
		||||
    VOA_MARGIN = 1 #do not maximize the VOA optimization
 | 
			
		||||
    if amp.out_voa is None:
 | 
			
		||||
        if power_mode:
 | 
			
		||||
            gain_target = amp.operational.gain_target
 | 
			
		||||
            pout = pref_total_db + amp.dp_db
 | 
			
		||||
            voa = min(amp.params.p_max-pout,
 | 
			
		||||
                      amp.params.gain_flatmax-amp.operational.gain_target)
 | 
			
		||||
            voa = round2float(max(voa, 0), 0.5) - VOA_MARGIN if amp.params.out_voa_auto else 0
 | 
			
		||||
            amp.dp_db = amp.dp_db + voa
 | 
			
		||||
            amp.operational.gain_target = amp.operational.gain_target + voa
 | 
			
		||||
            gain_target = amp.effective_gain
 | 
			
		||||
            voa = min(amp.params.p_max-power_target,
 | 
			
		||||
                      amp.params.gain_flatmax-amp.effective_gain)
 | 
			
		||||
            voa = max(round2float(max(voa, 0), 0.5) - VOA_MARGIN, 0) if amp.params.out_voa_auto else 0
 | 
			
		||||
            amp.delta_p = amp.delta_p + voa
 | 
			
		||||
            amp.effective_gain = amp.effective_gain + voa
 | 
			
		||||
        else:
 | 
			
		||||
            voa = 0 # no output voa optimization in gain mode
 | 
			
		||||
        amp.operational.out_voa = voa
 | 
			
		||||
        amp.out_voa = voa
 | 
			
		||||
 | 
			
		||||
def set_egress_amplifier(network, roadm, equipment, pref_total_db):
 | 
			
		||||
    power_mode = equipment['Spans']['default'].power_mode
 | 
			
		||||
    power_mode = equipment['Span']['default'].power_mode
 | 
			
		||||
    next_oms = (n for n in network.successors(roadm) if not isinstance(n, Transceiver))
 | 
			
		||||
    for oms in next_oms:
 | 
			
		||||
        #go through all the OMS departing from the Roadm
 | 
			
		||||
@@ -271,30 +315,82 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
 | 
			
		||||
        #     node = find_last_node(next_node)
 | 
			
		||||
        #     next_node = next(n for n in network.successors(node))
 | 
			
		||||
        #     next_node = find_last_node(next_node)
 | 
			
		||||
        prev_dp = 0
 | 
			
		||||
        dp = 0
 | 
			
		||||
        
 | 
			
		||||
        if node.per_degree_target_pch_out_db:
 | 
			
		||||
            # find the target power on this degree
 | 
			
		||||
            try:
 | 
			
		||||
                prev_dp = next(el["target_pch_out_db"] for el in \
 | 
			
		||||
                    node.per_degree_target_pch_out_db if el["to_node"]==next_node.uid)
 | 
			
		||||
            except StopIteration:
 | 
			
		||||
                # if no target power is defined on this degree use the global one
 | 
			
		||||
                prev_dp = getattr(node.params, 'target_pch_out_db', 0)
 | 
			
		||||
        else:
 | 
			
		||||
            # if no per degree target power is given use the global one
 | 
			
		||||
            prev_dp = getattr(node.params, 'target_pch_out_db', 0)
 | 
			
		||||
        dp = prev_dp
 | 
			
		||||
        prev_voa = 0
 | 
			
		||||
        voa = 0
 | 
			
		||||
        while True:
 | 
			
		||||
        #go through all nodes in the OMS (loop until next Roadm instance)
 | 
			
		||||
            if isinstance(node, Edfa):
 | 
			
		||||
                node_loss = span_loss(network, prev_node)
 | 
			
		||||
                dp_from_gain = prev_dp + node.operational.gain_target - node_loss \
 | 
			
		||||
                    if node.operational.gain_target > 0 else None
 | 
			
		||||
                dp = target_power(dp_from_gain, network, next_node, equipment)
 | 
			
		||||
                gain_target = node_loss + dp - prev_dp
 | 
			
		||||
                voa = node.out_voa if node.out_voa else 0
 | 
			
		||||
                if node.delta_p is None:
 | 
			
		||||
                    dp = target_power(network, next_node, equipment)
 | 
			
		||||
                else:
 | 
			
		||||
                    dp = node.delta_p
 | 
			
		||||
                gain_from_dp = node_loss + dp - prev_dp + prev_voa
 | 
			
		||||
                if node.effective_gain is None or power_mode:
 | 
			
		||||
                    gain_target = gain_from_dp
 | 
			
		||||
                else: #gain mode with effective_gain 
 | 
			
		||||
                    gain_target = node.effective_gain
 | 
			
		||||
                    dp = prev_dp - node_loss + gain_target
 | 
			
		||||
 | 
			
		||||
                if power_mode:
 | 
			
		||||
                    node.dp_db = dp
 | 
			
		||||
                node.operational.gain_target = gain_target
 | 
			
		||||
                power_target = pref_total_db + dp         
 | 
			
		||||
 | 
			
		||||
                if node.params.type_variety == '':
 | 
			
		||||
                    power_target = pref_total_db + dp
 | 
			
		||||
                    edfa_variety = select_edfa(gain_target, power_target, equipment)
 | 
			
		||||
                raman_allowed = False
 | 
			
		||||
                if isinstance(prev_node, Fiber):
 | 
			
		||||
                    max_fiber_lineic_loss_for_raman = \
 | 
			
		||||
                            equipment['Span']['default'].max_fiber_lineic_loss_for_raman
 | 
			
		||||
                    raman_allowed = prev_node.params.loss_coef < max_fiber_lineic_loss_for_raman
 | 
			
		||||
 | 
			
		||||
                # implementation of restrictions on roadm boosters
 | 
			
		||||
                if isinstance(prev_node,Roadm):
 | 
			
		||||
                    if prev_node.restrictions['booster_variety_list']:
 | 
			
		||||
                        restrictions = prev_node.restrictions['booster_variety_list']
 | 
			
		||||
                    else:
 | 
			
		||||
                        restrictions = None
 | 
			
		||||
                elif isinstance(next_node,Roadm):
 | 
			
		||||
                    # implementation of restrictions on roadm preamp
 | 
			
		||||
                    if next_node.restrictions['preamp_variety_list']:
 | 
			
		||||
                        restrictions = next_node.restrictions['preamp_variety_list']
 | 
			
		||||
                    else:
 | 
			
		||||
                        restrictions = None
 | 
			
		||||
                else:
 | 
			
		||||
                    restrictions = None
 | 
			
		||||
 | 
			
		||||
                if node.params.type_variety == '':                   
 | 
			
		||||
                    edfa_variety, power_reduction = select_edfa(raman_allowed, 
 | 
			
		||||
                                   gain_target, power_target, equipment, node.uid, restrictions)
 | 
			
		||||
                    extra_params = equipment['Edfa'][edfa_variety]
 | 
			
		||||
                    node.params.update_params(extra_params._asdict())
 | 
			
		||||
                set_amplifier_voa(node, pref_total_db, power_mode)
 | 
			
		||||
                    node.params.update_params(extra_params.__dict__)
 | 
			
		||||
                    dp += power_reduction
 | 
			
		||||
                    gain_target += power_reduction
 | 
			
		||||
                elif node.params.raman and not raman_allowed:
 | 
			
		||||
                    print(
 | 
			
		||||
                        f'\x1b[1;31;40m'\
 | 
			
		||||
                        + f'WARNING: raman is used in node {node.uid}\n \
 | 
			
		||||
                but fiber lineic loss is above threshold\n'\
 | 
			
		||||
                        + '\x1b[0m'
 | 
			
		||||
                        )                    
 | 
			
		||||
                                
 | 
			
		||||
                node.delta_p = dp if power_mode else None
 | 
			
		||||
                node.effective_gain = gain_target
 | 
			
		||||
                set_amplifier_voa(node, power_target, power_mode)
 | 
			
		||||
            if isinstance(next_node, Roadm) or isinstance(next_node, Transceiver):
 | 
			
		||||
                break
 | 
			
		||||
            prev_dp = dp
 | 
			
		||||
            prev_voa = voa
 | 
			
		||||
            prev_node = node
 | 
			
		||||
            node = next_node
 | 
			
		||||
            # print(f'{node.uid}')
 | 
			
		||||
@@ -319,12 +415,16 @@ def add_egress_amplifier(network, node):
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    operational = {
 | 
			
		||||
                        'gain_target': 0,
 | 
			
		||||
                        'gain_target': None,
 | 
			
		||||
                        'tilt_target': 0,
 | 
			
		||||
                    })
 | 
			
		||||
        network.add_node(amp)
 | 
			
		||||
        network.add_edge(node, amp)
 | 
			
		||||
        network.add_edge(amp, next_node)
 | 
			
		||||
        if isinstance(node,Fiber):
 | 
			
		||||
            edgeweight = node.params.length
 | 
			
		||||
        else:
 | 
			
		||||
            edgeweight = 0.01
 | 
			
		||||
        network.add_edge(node, amp, weight = edgeweight)
 | 
			
		||||
        network.add_edge(amp, next_node, weight = 0.01)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def calculate_new_length(fiber_length, bounds, target_length):
 | 
			
		||||
@@ -360,9 +460,7 @@ def split_fiber(network, fiber, bounds, target_length, equipment):
 | 
			
		||||
        next_node = next(network.successors(fiber))
 | 
			
		||||
        prev_node = next(network.predecessors(fiber))
 | 
			
		||||
    except StopIteration:
 | 
			
		||||
 | 
			
		||||
        print(f'In {__name__} split_fiber function:\n\t{fiber.uid}   is not properly connected, please check network topology')
 | 
			
		||||
        exit()
 | 
			
		||||
        raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology')
 | 
			
		||||
 | 
			
		||||
    network.remove_node(fiber)
 | 
			
		||||
 | 
			
		||||
@@ -385,17 +483,25 @@ def split_fiber(network, fiber, bounds, target_length, equipment):
 | 
			
		||||
                            }
 | 
			
		||||
                          },
 | 
			
		||||
                          params = fiber_params)
 | 
			
		||||
        network.add_edge(prev_node, new_span)
 | 
			
		||||
        prev_node = new_span
 | 
			
		||||
    network.add_edge(prev_node, next_node)
 | 
			
		||||
 | 
			
		||||
def add_connector_loss(fibers, con_in, con_out, EOL):
 | 
			
		||||
    for fiber in fibers:
 | 
			
		||||
        if fiber.con_in is None: fiber.con_in = con_in
 | 
			
		||||
        if fiber.con_out is None:
 | 
			
		||||
            fiber.con_out = con_out #con_out includes EOL
 | 
			
		||||
        if isinstance(prev_node,Fiber):
 | 
			
		||||
            edgeweight = prev_node.params.length
 | 
			
		||||
        else:
 | 
			
		||||
            fiber.con_out = fiber.con_out+EOL
 | 
			
		||||
            edgeweight = 0.01
 | 
			
		||||
        network.add_edge(prev_node, new_span, weight = edgeweight)
 | 
			
		||||
        prev_node = new_span
 | 
			
		||||
    if isinstance(prev_node,Fiber):
 | 
			
		||||
        edgeweight = prev_node.params.length
 | 
			
		||||
    else:
 | 
			
		||||
        edgeweight = 0.01    
 | 
			
		||||
    network.add_edge(prev_node, next_node, weight = edgeweight)
 | 
			
		||||
 | 
			
		||||
def add_connector_loss(network, fibers, default_con_in, default_con_out, EOL):
 | 
			
		||||
    for fiber in fibers:
 | 
			
		||||
        if fiber.con_in is None: fiber.con_in = default_con_in
 | 
			
		||||
        if fiber.con_out is None: fiber.con_out = default_con_out
 | 
			
		||||
        next_node = next(n for n in network.successors(fiber))
 | 
			
		||||
        if not isinstance(next_node, Fused):
 | 
			
		||||
            fiber.con_out += EOL
 | 
			
		||||
 | 
			
		||||
def add_fiber_padding(network, fibers, padding):
 | 
			
		||||
    """last_fibers = (fiber for n in network.nodes()
 | 
			
		||||
@@ -407,33 +513,32 @@ def add_fiber_padding(network, fibers, padding):
 | 
			
		||||
        try:
 | 
			
		||||
            next_node = next(network.successors(fiber))
 | 
			
		||||
        except StopIteration:
 | 
			
		||||
            msg = f'In {__name__} add_fiber_padding function:\n\t{fiber.uid}   is not properly connected, please check network topology'
 | 
			
		||||
            print(msg)
 | 
			
		||||
            logger.critical(msg)
 | 
			
		||||
            exit(1)            
 | 
			
		||||
            raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology')
 | 
			
		||||
        if this_span_loss < padding and not (isinstance(next_node, Fused)):
 | 
			
		||||
            #add a padding att_in at the input of the 1st fiber:
 | 
			
		||||
            #address the case when several fibers are spliced together
 | 
			
		||||
            first_fiber = find_first_node(network, fiber)
 | 
			
		||||
            if first_fiber.att_in is None:
 | 
			
		||||
                first_fiber.att_in = padding - this_span_loss
 | 
			
		||||
            else :
 | 
			
		||||
                first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
 | 
			
		||||
            # in order to support no booster , fused might be placed
 | 
			
		||||
            # just after a roadm: need to check that first_fiber is really a fiber
 | 
			
		||||
            if isinstance(first_fiber,Fiber):
 | 
			
		||||
                if first_fiber.att_in is None:
 | 
			
		||||
                    first_fiber.att_in = padding - this_span_loss
 | 
			
		||||
                else:
 | 
			
		||||
                    first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
 | 
			
		||||
 | 
			
		||||
def build_network(network, equipment, pref_ch_db, pref_total_db):
 | 
			
		||||
    default_span_data = equipment['Spans']['default']
 | 
			
		||||
    default_span_data = equipment['Span']['default']
 | 
			
		||||
    max_length = int(default_span_data.max_length * UNITS[default_span_data.length_units])
 | 
			
		||||
    min_length = max(int(default_span_data.padding/0.2*1e3),50_000)
 | 
			
		||||
    bounds = range(min_length, max_length)
 | 
			
		||||
    target_length = max(min_length, 90_000)
 | 
			
		||||
    con_in = default_span_data.con_in
 | 
			
		||||
    con_out = default_span_data.con_out + default_span_data.EOL
 | 
			
		||||
    default_con_in = default_span_data.con_in
 | 
			
		||||
    default_con_out = default_span_data.con_out
 | 
			
		||||
    padding = default_span_data.padding
 | 
			
		||||
 | 
			
		||||
    #set raodm loss for gain_mode before to build network
 | 
			
		||||
    set_roadm_loss(network, equipment, pref_ch_db)
 | 
			
		||||
    #set roadm loss for gain_mode before to build network
 | 
			
		||||
    fibers = [f for f in network.nodes() if isinstance(f, Fiber)]
 | 
			
		||||
    add_connector_loss(fibers, con_in, con_out, default_span_data.EOL)
 | 
			
		||||
    add_connector_loss(network, fibers, default_con_in, default_con_out, default_span_data.EOL)
 | 
			
		||||
    add_fiber_padding(network, fibers, padding)
 | 
			
		||||
    # don't group split fiber and add amp in the same loop
 | 
			
		||||
    # =>for code clarity (at the expense of speed):
 | 
			
		||||
@@ -442,6 +547,7 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
 | 
			
		||||
 | 
			
		||||
    amplified_nodes = [n for n in network.nodes()
 | 
			
		||||
                        if isinstance(n, Fiber) or isinstance(n, Roadm)]
 | 
			
		||||
 | 
			
		||||
    for node in amplified_nodes:
 | 
			
		||||
        add_egress_amplifier(network, node)
 | 
			
		||||
 | 
			
		||||
@@ -455,3 +561,11 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
 | 
			
		||||
        for t in trx:
 | 
			
		||||
            set_egress_amplifier(network, t, equipment, pref_total_db)
 | 
			
		||||
 | 
			
		||||
def load_sim_params(filename):
 | 
			
		||||
    sim_params = load_json(filename)
 | 
			
		||||
    return SimParams(params=sim_params)
 | 
			
		||||
 | 
			
		||||
def configure_network(network, sim_params):
 | 
			
		||||
    for node in network.nodes:
 | 
			
		||||
        if isinstance(node, RamanFiber):
 | 
			
		||||
            node.sim_params = sim_params
 | 
			
		||||
 
 | 
			
		||||
@@ -8,13 +8,13 @@ gnpy.core.node
 | 
			
		||||
This module contains the base class for a network element.
 | 
			
		||||
 | 
			
		||||
Strictly, a network element is any callable which accepts an immutable
 | 
			
		||||
.info.SpectralInformation object and returns a .info.SpectralInformation object
 | 
			
		||||
(a copy.)
 | 
			
		||||
:class:`.info.SpectralInformation` object and returns an :class:`.info.SpectralInformation` object
 | 
			
		||||
(a copy).
 | 
			
		||||
 | 
			
		||||
Network elements MUST implement two attributes .uid and .name representing a
 | 
			
		||||
unique identifier and a printable name.
 | 
			
		||||
 | 
			
		||||
This base class provides a mode convenient way to define a network element
 | 
			
		||||
This base class provides a more convenient way to define a network element
 | 
			
		||||
via subclassing.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
@@ -26,10 +26,12 @@ class Location(namedtuple('Location', 'latitude longitude city region')):
 | 
			
		||||
        return super().__new__(cls, latitude, longitude, city, region)
 | 
			
		||||
 | 
			
		||||
class Node:
 | 
			
		||||
    def __init__(self, uid, name=None, params=None, metadata={'location':{}}, operational=None):
 | 
			
		||||
    def __init__(self, uid, name=None, params=None, metadata=None, operational=None):
 | 
			
		||||
        if name is None:
 | 
			
		||||
            name = uid
 | 
			
		||||
        self.uid, self.name = uid, name
 | 
			
		||||
        if metadata is None:
 | 
			
		||||
            metadata = {'location': {}}
 | 
			
		||||
        if metadata and not isinstance(metadata.get('location'), Location):
 | 
			
		||||
            metadata['location'] = Location(**metadata.pop('location', {}))
 | 
			
		||||
        self.params, self.metadata, self.operational = params, metadata, operational
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1194
									
								
								gnpy/core/request.py
									
									
									
									
									
								
							
							
						
						
									
										1194
									
								
								gnpy/core/request.py
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										820
									
								
								gnpy/core/science_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										820
									
								
								gnpy/core/science_utils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,820 @@
 | 
			
		||||
import numpy as np
 | 
			
		||||
from operator import attrgetter
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
from logging import getLogger
 | 
			
		||||
import scipy.constants as ph
 | 
			
		||||
from scipy.integrate import solve_bvp
 | 
			
		||||
from scipy.integrate import cumtrapz
 | 
			
		||||
from scipy.interpolate import interp1d
 | 
			
		||||
from scipy.optimize import OptimizeResult
 | 
			
		||||
 | 
			
		||||
from gnpy.core.utils import db2lin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
logger = getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RamanParams():
 | 
			
		||||
    def __init__(self, params):
 | 
			
		||||
        self._flag_raman = params['flag_raman']
 | 
			
		||||
        self._space_resolution = params['space_resolution']
 | 
			
		||||
        self._tolerance = params['tolerance']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def flag_raman(self):
 | 
			
		||||
        return self._flag_raman
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def space_resolution(self):
 | 
			
		||||
        return self._space_resolution
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def tolerance(self):
 | 
			
		||||
        return self._tolerance
 | 
			
		||||
 | 
			
		||||
class NLIParams():
 | 
			
		||||
    def __init__(self, params):
 | 
			
		||||
        self._nli_method_name = params['nli_method_name']
 | 
			
		||||
        self._wdm_grid_size = params['wdm_grid_size']
 | 
			
		||||
        self._dispersion_tolerance = params['dispersion_tolerance']
 | 
			
		||||
        self._phase_shift_tollerance = params['phase_shift_tollerance']
 | 
			
		||||
        self._f_cut_resolution = None
 | 
			
		||||
        self._f_pump_resolution = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def nli_method_name(self):
 | 
			
		||||
        return self._nli_method_name
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def wdm_grid_size(self):
 | 
			
		||||
        return self._wdm_grid_size
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def dispersion_tolerance(self):
 | 
			
		||||
        return self._dispersion_tolerance
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def phase_shift_tollerance(self):
 | 
			
		||||
        return self._phase_shift_tollerance
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def f_cut_resolution(self):
 | 
			
		||||
        return self._f_cut_resolution
 | 
			
		||||
 | 
			
		||||
    @f_cut_resolution.setter
 | 
			
		||||
    def f_cut_resolution(self, f_cut_resolution):
 | 
			
		||||
        self._f_cut_resolution = f_cut_resolution
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def f_pump_resolution(self):
 | 
			
		||||
        return self._f_pump_resolution
 | 
			
		||||
 | 
			
		||||
    @f_pump_resolution.setter
 | 
			
		||||
    def f_pump_resolution(self, f_pump_resolution):
 | 
			
		||||
        self._f_pump_resolution = f_pump_resolution
 | 
			
		||||
 | 
			
		||||
class SimParams():
 | 
			
		||||
    def __init__(self, params):
 | 
			
		||||
        self._raman_computed_channels = params['raman_computed_channels']
 | 
			
		||||
        self._raman_params = RamanParams(params=params['raman_parameters'])
 | 
			
		||||
        self._nli_params = NLIParams(params=params['nli_parameters'])
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def raman_computed_channels(self):
 | 
			
		||||
        return self._raman_computed_channels
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def raman_params(self):
 | 
			
		||||
        return self._raman_params
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def nli_params(self):
 | 
			
		||||
        return self._nli_params
 | 
			
		||||
 | 
			
		||||
class FiberParams():
 | 
			
		||||
    def __init__(self, fiber):
 | 
			
		||||
        self._loss_coef = 2 * fiber.dbkm_2_lin()[1]
 | 
			
		||||
        self._length = fiber.length
 | 
			
		||||
        self._gamma = fiber.gamma
 | 
			
		||||
        self._beta2 = fiber.beta2()
 | 
			
		||||
        self._beta3 = fiber.beta3 if hasattr(fiber, 'beta3') else 0
 | 
			
		||||
        self._f_ref_beta = fiber.f_ref_beta if hasattr(fiber, 'f_ref_beta') else 0
 | 
			
		||||
        self._raman_efficiency = fiber.params.raman_efficiency
 | 
			
		||||
        self._temperature = fiber.operational['temperature']
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def loss_coef(self):
 | 
			
		||||
        return self._loss_coef
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def length(self):
 | 
			
		||||
        return self._length
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def gamma(self):
 | 
			
		||||
        return self._gamma
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def beta2(self):
 | 
			
		||||
        return self._beta2
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def beta3(self):
 | 
			
		||||
        return self._beta3
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def f_ref_beta(self):
 | 
			
		||||
        return self._f_ref_beta
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def raman_efficiency(self):
 | 
			
		||||
        return self._raman_efficiency
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def temperature(self):
 | 
			
		||||
        return self._temperature
 | 
			
		||||
 | 
			
		||||
    def alpha0(self, f_ref=193.5e12):
 | 
			
		||||
        """ It returns the zero element of the series expansion of attenuation coefficient alpha(f) in the
 | 
			
		||||
        reference frequency f_ref
 | 
			
		||||
 | 
			
		||||
        :param f_ref: reference frequency of series expansion [Hz]
 | 
			
		||||
        :return: alpha0: power attenuation coefficient in f_ref [Neper/m]
 | 
			
		||||
        """
 | 
			
		||||
        if not hasattr(self.loss_coef, 'alpha_power'):
 | 
			
		||||
            alpha0 = self.loss_coef
 | 
			
		||||
        else:
 | 
			
		||||
            alpha_interp = interp1d(self.loss_coef['frequency'],
 | 
			
		||||
                                    self.loss_coef['alpha_power'])
 | 
			
		||||
            alpha0 = alpha_interp(f_ref)
 | 
			
		||||
        return alpha0
 | 
			
		||||
 | 
			
		||||
pump = namedtuple('RamanPump', 'power frequency propagation_direction')
 | 
			
		||||
 | 
			
		||||
def propagate_raman_fiber(fiber, *carriers):
 | 
			
		||||
    sim_params = fiber.sim_params
 | 
			
		||||
    raman_params = fiber.sim_params.raman_params
 | 
			
		||||
    nli_params = fiber.sim_params.nli_params
 | 
			
		||||
    # apply input attenuation to carriers
 | 
			
		||||
    attenuation_in = db2lin(fiber.con_in + fiber.att_in)
 | 
			
		||||
    chan = []
 | 
			
		||||
    for carrier in carriers:
 | 
			
		||||
        pwr = carrier.power
 | 
			
		||||
        pwr = pwr._replace(signal=pwr.signal / attenuation_in,
 | 
			
		||||
                           nli=pwr.nli / attenuation_in,
 | 
			
		||||
                           ase=pwr.ase / attenuation_in)
 | 
			
		||||
        carrier = carrier._replace(power=pwr)
 | 
			
		||||
        chan.append(carrier)
 | 
			
		||||
    carriers = tuple(f for f in chan)
 | 
			
		||||
    fiber_params = FiberParams(fiber)
 | 
			
		||||
 | 
			
		||||
    # evaluate fiber attenuation involving also SRS if required by sim_params
 | 
			
		||||
    if 'raman_pumps' in fiber.operational:
 | 
			
		||||
        raman_pumps = tuple(pump(p['power'], p['frequency'], p['propagation_direction'])
 | 
			
		||||
                            for p in fiber.operational['raman_pumps'])
 | 
			
		||||
    else:
 | 
			
		||||
        raman_pumps = None
 | 
			
		||||
    raman_solver = RamanSolver(raman_params=raman_params, fiber_params=fiber_params)
 | 
			
		||||
    stimulated_raman_scattering = raman_solver.stimulated_raman_scattering(carriers=carriers,
 | 
			
		||||
                                                                           raman_pumps=raman_pumps)
 | 
			
		||||
    fiber_attenuation = (stimulated_raman_scattering.rho[:, -1])**-2
 | 
			
		||||
    if not raman_params.flag_raman:
 | 
			
		||||
        fiber_attenuation = tuple(fiber.lin_attenuation for _ in carriers)
 | 
			
		||||
 | 
			
		||||
    # evaluate Raman ASE noise if required by sim_params and if raman pumps are present
 | 
			
		||||
    if raman_params.flag_raman and raman_pumps:
 | 
			
		||||
        raman_ase = raman_solver.spontaneous_raman_scattering.power[:, -1]
 | 
			
		||||
    else:
 | 
			
		||||
        raman_ase = tuple(0 for _ in carriers)
 | 
			
		||||
 | 
			
		||||
    # evaluate nli and propagate in fiber
 | 
			
		||||
    attenuation_out = db2lin(fiber.con_out)
 | 
			
		||||
    nli_solver = NliSolver(nli_params=nli_params, fiber_params=fiber_params)
 | 
			
		||||
    nli_solver.stimulated_raman_scattering = stimulated_raman_scattering
 | 
			
		||||
 | 
			
		||||
    nli_frequencies = []
 | 
			
		||||
    computed_nli = []
 | 
			
		||||
    for carrier in (c for c in carriers if c.channel_number in sim_params.raman_computed_channels):
 | 
			
		||||
        resolution_param = frequency_resolution(carrier, carriers, sim_params, fiber_params)
 | 
			
		||||
        f_cut_resolution, f_pump_resolution, _, _ = resolution_param
 | 
			
		||||
        nli_params.f_cut_resolution = f_cut_resolution
 | 
			
		||||
        nli_params.f_pump_resolution = f_pump_resolution
 | 
			
		||||
        nli_frequencies.append(carrier.frequency)
 | 
			
		||||
        computed_nli.append(nli_solver.compute_nli(carrier, *carriers))
 | 
			
		||||
 | 
			
		||||
    new_carriers = []
 | 
			
		||||
    for carrier, attenuation, rmn_ase in zip(carriers, fiber_attenuation, raman_ase):
 | 
			
		||||
        carrier_nli = np.interp(carrier.frequency, nli_frequencies, computed_nli)
 | 
			
		||||
        pwr = carrier.power
 | 
			
		||||
        pwr = pwr._replace(signal=pwr.signal/attenuation/attenuation_out,
 | 
			
		||||
                           nli=(pwr.nli+carrier_nli)/attenuation/attenuation_out,
 | 
			
		||||
                           ase=((pwr.ase/attenuation)+rmn_ase)/attenuation_out)
 | 
			
		||||
        new_carriers.append(carrier._replace(power=pwr))
 | 
			
		||||
    return new_carriers
 | 
			
		||||
 | 
			
		||||
def frequency_resolution(carrier, carriers, sim_params, fiber_params):
 | 
			
		||||
    def _get_freq_res_k_phi(delta_count, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol):
 | 
			
		||||
        res_phi = _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol)
 | 
			
		||||
        res_k = _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol)
 | 
			
		||||
        res_dict = {'res_phi': res_phi, 'res_k': res_k}
 | 
			
		||||
        method = min(res_dict, key=res_dict.get)
 | 
			
		||||
        return res_dict[method], method, res_dict
 | 
			
		||||
 | 
			
		||||
    def _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol):
 | 
			
		||||
        return k_tol * abs(alpha0) / abs(beta2) / (1 + delta_count) / (4 * np.pi ** 2 * grid_size)
 | 
			
		||||
 | 
			
		||||
    def _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol):
 | 
			
		||||
        return phi_tol / abs(beta2) / (1 + delta_count) / delta_z / (4 * np.pi ** 2 * grid_size)
 | 
			
		||||
 | 
			
		||||
    grid_size = sim_params.nli_params.wdm_grid_size
 | 
			
		||||
    delta_z = sim_params.raman_params.space_resolution
 | 
			
		||||
    alpha0 = fiber_params.alpha0()
 | 
			
		||||
    beta2 = fiber_params.beta2
 | 
			
		||||
    k_tol = sim_params.nli_params.dispersion_tolerance
 | 
			
		||||
    phi_tol = sim_params.nli_params.phase_shift_tollerance
 | 
			
		||||
    f_pump_resolution, method_f_pump, res_dict_pump = \
 | 
			
		||||
        _get_freq_res_k_phi(0, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol)
 | 
			
		||||
    f_cut_resolution = {}
 | 
			
		||||
    method_f_cut = {}
 | 
			
		||||
    res_dict_cut = {}
 | 
			
		||||
    for cut_carrier in carriers:
 | 
			
		||||
        delta_number = cut_carrier.channel_number - carrier.channel_number
 | 
			
		||||
        delta_count = abs(delta_number)
 | 
			
		||||
        f_res, method, res_dict = \
 | 
			
		||||
            _get_freq_res_k_phi(delta_count, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol)
 | 
			
		||||
        f_cut_resolution[f'delta_{delta_number}'] = f_res
 | 
			
		||||
        method_f_cut[delta_number] = method
 | 
			
		||||
        res_dict_cut[delta_number] = res_dict
 | 
			
		||||
    return [f_cut_resolution, f_pump_resolution, (method_f_cut, method_f_pump), (res_dict_cut, res_dict_pump)]
 | 
			
		||||
 | 
			
		||||
def raised_cosine_comb(f, *carriers):
 | 
			
		||||
    """ Returns an array storing the PSD of a WDM comb of raised cosine shaped
 | 
			
		||||
    channels at the input frequencies defined in array f
 | 
			
		||||
    :param f: numpy array of frequencies in Hz
 | 
			
		||||
    :param carriers: namedtuple describing the WDM comb
 | 
			
		||||
    :return: PSD of the WDM comb evaluated over f
 | 
			
		||||
    """
 | 
			
		||||
    psd = np.zeros(np.shape(f))
 | 
			
		||||
    for carrier in carriers:
 | 
			
		||||
        f_nch = carrier.frequency
 | 
			
		||||
        g_ch = carrier.power.signal / carrier.baud_rate
 | 
			
		||||
        ts = 1 / carrier.baud_rate
 | 
			
		||||
        passband = (1 - carrier.roll_off) / (2 / carrier.baud_rate)
 | 
			
		||||
        stopband = (1 + carrier.roll_off) / (2 / carrier.baud_rate)
 | 
			
		||||
        ff = np.abs(f - f_nch)
 | 
			
		||||
        tf = ff - passband
 | 
			
		||||
        if carrier.roll_off == 0:
 | 
			
		||||
            psd = np.where(tf <= 0, g_ch, 0.) + psd
 | 
			
		||||
        else:
 | 
			
		||||
            psd = g_ch * (np.where(tf <= 0, 1., 0.) + 1 / 2 * (1 + np.cos(np.pi * ts / carrier.roll_off * tf)) *
 | 
			
		||||
                          np.where(tf > 0, 1., 0.) * np.where(np.abs(ff) <= stopband, 1., 0.)) + psd
 | 
			
		||||
    return psd
 | 
			
		||||
 | 
			
		||||
class RamanSolver:
 | 
			
		||||
    def __init__(self, raman_params=None, fiber_params=None):
 | 
			
		||||
        """ Initialize the fiber object with its physical parameters
 | 
			
		||||
        :param length: fiber length in m.
 | 
			
		||||
        :param alphap: fiber power attenuation coefficient vs frequency in 1/m. numpy array
 | 
			
		||||
        :param freq_alpha: frequency axis of alphap in Hz. numpy array
 | 
			
		||||
        :param cr_raman: Raman efficiency vs frequency offset in 1/W/m. numpy array
 | 
			
		||||
        :param freq_cr: reference frequency offset axis for cr_raman. numpy array
 | 
			
		||||
        :param raman_params: namedtuple containing the solver parameters (optional).
 | 
			
		||||
        """
 | 
			
		||||
        self.fiber_params = fiber_params
 | 
			
		||||
        self.raman_params = raman_params
 | 
			
		||||
        self._carriers = None
 | 
			
		||||
        self._stimulated_raman_scattering = None
 | 
			
		||||
        self._spontaneous_raman_scattering = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def fiber_params(self):
 | 
			
		||||
        return self._fiber_params
 | 
			
		||||
 | 
			
		||||
    @fiber_params.setter
 | 
			
		||||
    def fiber_params(self, fiber_params):
 | 
			
		||||
        self._stimulated_raman_scattering = None
 | 
			
		||||
        self._fiber_params = fiber_params
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def carriers(self):
 | 
			
		||||
        return self._carriers
 | 
			
		||||
 | 
			
		||||
    @carriers.setter
 | 
			
		||||
    def carriers(self, carriers):
 | 
			
		||||
        """
 | 
			
		||||
        :param carriers: tuple of namedtuples containing information about carriers
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        self._carriers = carriers
 | 
			
		||||
        self._stimulated_raman_scattering = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def raman_pumps(self):
 | 
			
		||||
        return self._raman_pumps
 | 
			
		||||
 | 
			
		||||
    @raman_pumps.setter
 | 
			
		||||
    def raman_pumps(self, raman_pumps):
 | 
			
		||||
        self._raman_pumps = raman_pumps
 | 
			
		||||
        self._stimulated_raman_scattering = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def raman_params(self):
 | 
			
		||||
        return self._raman_params
 | 
			
		||||
 | 
			
		||||
    @raman_params.setter
 | 
			
		||||
    def raman_params(self, raman_params):
 | 
			
		||||
        """
 | 
			
		||||
        :param raman_params: namedtuple containing the solver parameters (optional).
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
        self._raman_params = raman_params
 | 
			
		||||
        self._stimulated_raman_scattering = None
 | 
			
		||||
        self._spontaneous_raman_scattering = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def spontaneous_raman_scattering(self):
 | 
			
		||||
        if self._spontaneous_raman_scattering is None:
 | 
			
		||||
            # SET STUFF
 | 
			
		||||
            loss_coef = self.fiber_params.loss_coef
 | 
			
		||||
            raman_efficiency = self.fiber_params.raman_efficiency
 | 
			
		||||
            temperature = self.fiber_params.temperature
 | 
			
		||||
            carriers = self.carriers
 | 
			
		||||
            raman_pumps = self.raman_pumps
 | 
			
		||||
 | 
			
		||||
            logger.debug('Start computing fiber Spontaneous Raman Scattering')
 | 
			
		||||
            power_spectrum, freq_array, prop_direct, bn_array = self._compute_power_spectrum(carriers, raman_pumps)
 | 
			
		||||
 | 
			
		||||
            if not hasattr(loss_coef, 'alpha_power'):
 | 
			
		||||
                alphap_fiber = loss_coef * np.ones(freq_array.shape)
 | 
			
		||||
            else:
 | 
			
		||||
                interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power'])
 | 
			
		||||
                alphap_fiber = interp_alphap(freq_array)
 | 
			
		||||
 | 
			
		||||
            freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
 | 
			
		||||
            interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
 | 
			
		||||
            cr = interp_cr(freq_diff)
 | 
			
		||||
 | 
			
		||||
            # z propagation axis
 | 
			
		||||
            z_array = self._stimulated_raman_scattering.z
 | 
			
		||||
            ase_bc = np.zeros(freq_array.shape)
 | 
			
		||||
 | 
			
		||||
            # calculate ase power
 | 
			
		||||
            spontaneous_raman_scattering = self._int_spontaneous_raman(z_array, self._stimulated_raman_scattering.power,
 | 
			
		||||
                                                                       alphap_fiber, freq_array, cr, freq_diff, ase_bc,
 | 
			
		||||
                                                                       bn_array, temperature)
 | 
			
		||||
 | 
			
		||||
            setattr(spontaneous_raman_scattering, 'frequency', freq_array)
 | 
			
		||||
            setattr(spontaneous_raman_scattering, 'z', z_array)
 | 
			
		||||
            setattr(spontaneous_raman_scattering, 'power', spontaneous_raman_scattering.x)
 | 
			
		||||
            delattr(spontaneous_raman_scattering, 'x')
 | 
			
		||||
 | 
			
		||||
            logger.debug(spontaneous_raman_scattering.message)
 | 
			
		||||
 | 
			
		||||
            self._spontaneous_raman_scattering = spontaneous_raman_scattering
 | 
			
		||||
 | 
			
		||||
        return self._spontaneous_raman_scattering
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _compute_power_spectrum(carriers, raman_pumps=None):
 | 
			
		||||
        """
 | 
			
		||||
        Rearrangement of spectral and Raman pump information to make them compatible with Raman solver
 | 
			
		||||
        :param carriers: a tuple of namedtuples describing the transmitted channels
 | 
			
		||||
        :param raman_pumps: a namedtuple describing the Raman pumps
 | 
			
		||||
        :return:
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        # Signal power spectrum
 | 
			
		||||
        pow_array = np.array([])
 | 
			
		||||
        f_array = np.array([])
 | 
			
		||||
        noise_bandwidth_array = np.array([])
 | 
			
		||||
        for carrier in sorted(carriers, key=attrgetter('frequency')):
 | 
			
		||||
            f_array = np.append(f_array, carrier.frequency)
 | 
			
		||||
            pow_array = np.append(pow_array, carrier.power.signal)
 | 
			
		||||
            ref_bw = carrier.baud_rate
 | 
			
		||||
            noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw)
 | 
			
		||||
 | 
			
		||||
        propagation_direction = np.ones(len(f_array))
 | 
			
		||||
 | 
			
		||||
        # Raman pump power spectrum
 | 
			
		||||
        if raman_pumps:
 | 
			
		||||
            for pump in raman_pumps:
 | 
			
		||||
                pow_array = np.append(pow_array, pump.power)
 | 
			
		||||
                f_array = np.append(f_array, pump.frequency)
 | 
			
		||||
                direction = +1 if pump.propagation_direction.lower() == 'coprop' else -1
 | 
			
		||||
                propagation_direction = np.append(propagation_direction, direction)
 | 
			
		||||
                noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw)
 | 
			
		||||
 | 
			
		||||
        # Final sorting
 | 
			
		||||
        ind = np.argsort(f_array)
 | 
			
		||||
        f_array = f_array[ind]
 | 
			
		||||
        pow_array = pow_array[ind]
 | 
			
		||||
        propagation_direction = propagation_direction[ind]
 | 
			
		||||
 | 
			
		||||
        return pow_array, f_array, propagation_direction, noise_bandwidth_array
 | 
			
		||||
 | 
			
		||||
    def _int_spontaneous_raman(self, z_array, raman_matrix, alphap_fiber, freq_array, cr_raman_matrix, freq_diff, ase_bc, bn_array, temperature):
 | 
			
		||||
        spontaneous_raman_scattering = OptimizeResult()
 | 
			
		||||
 | 
			
		||||
        dx = self.raman_params.space_resolution
 | 
			
		||||
        h = ph.value('Planck constant')
 | 
			
		||||
        kb = ph.value('Boltzmann constant')
 | 
			
		||||
 | 
			
		||||
        power_ase = np.nan * np.ones(raman_matrix.shape)
 | 
			
		||||
        int_pump = cumtrapz(raman_matrix, z_array, dx=dx, axis=1, initial=0)
 | 
			
		||||
 | 
			
		||||
        for f_ind, f_ase in enumerate(freq_array):
 | 
			
		||||
            cr_raman = cr_raman_matrix[f_ind, :]
 | 
			
		||||
            vibrational_loss = f_ase / freq_array[:f_ind]
 | 
			
		||||
            eta = 1/(np.exp((h*freq_diff[f_ind, f_ind+1:])/(kb*temperature)) - 1)
 | 
			
		||||
 | 
			
		||||
            int_fiber_loss = -alphap_fiber[f_ind] * z_array
 | 
			
		||||
            int_raman_loss = np.sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(), axis=0)
 | 
			
		||||
            int_raman_gain = np.sum((cr_raman[f_ind + 1:] * int_pump[f_ind + 1:, :].transpose()).transpose(), axis=0)
 | 
			
		||||
 | 
			
		||||
            int_gain_loss = int_fiber_loss + int_raman_gain + int_raman_loss
 | 
			
		||||
 | 
			
		||||
            new_ase = np.sum((cr_raman[f_ind+1:] * (1 + eta) * raman_matrix[f_ind+1:, :].transpose()).transpose() * h * f_ase * bn_array[f_ind], axis=0)
 | 
			
		||||
 | 
			
		||||
            bc_evolution = ase_bc[f_ind] * np.exp(int_gain_loss)
 | 
			
		||||
            ase_evolution = np.exp(int_gain_loss) * cumtrapz(new_ase*np.exp(-int_gain_loss), z_array, dx=dx, initial=0)
 | 
			
		||||
 | 
			
		||||
            power_ase[f_ind, :] = bc_evolution + ase_evolution
 | 
			
		||||
 | 
			
		||||
        spontaneous_raman_scattering.x = 2 * power_ase
 | 
			
		||||
        spontaneous_raman_scattering.success = True
 | 
			
		||||
        spontaneous_raman_scattering.message = "Spontaneous Raman Scattering evaluated successfully"
 | 
			
		||||
 | 
			
		||||
        return spontaneous_raman_scattering
 | 
			
		||||
 | 
			
		||||
    def stimulated_raman_scattering(self, carriers, raman_pumps=None):
 | 
			
		||||
        """ Returns stimulated Raman scattering solution including 
 | 
			
		||||
        fiber gain/loss profile.
 | 
			
		||||
        :return: self._stimulated_raman_scattering: the SRS problem solution.
 | 
			
		||||
        scipy.interpolate.PPoly instance
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        if self._stimulated_raman_scattering is None:
 | 
			
		||||
            # fiber parameters
 | 
			
		||||
            fiber_length = self.fiber_params.length
 | 
			
		||||
            loss_coef = self.fiber_params.loss_coef
 | 
			
		||||
            if self.raman_params.flag_raman:
 | 
			
		||||
                raman_efficiency = self.fiber_params.raman_efficiency
 | 
			
		||||
            else:
 | 
			
		||||
                raman_efficiency = self.fiber_params.raman_efficiency
 | 
			
		||||
                raman_efficiency['cr'] = np.array(raman_efficiency['cr']) * 0
 | 
			
		||||
            # raman solver parameters
 | 
			
		||||
            z_resolution = self.raman_params.space_resolution
 | 
			
		||||
            tolerance = self.raman_params.tolerance
 | 
			
		||||
 | 
			
		||||
            logger.debug('Start computing fiber Stimulated Raman Scattering')
 | 
			
		||||
 | 
			
		||||
            power_spectrum, freq_array, prop_direct, _ = self._compute_power_spectrum(carriers, raman_pumps)
 | 
			
		||||
 | 
			
		||||
            if not hasattr(loss_coef, 'alpha_power'):
 | 
			
		||||
                alphap_fiber = loss_coef * np.ones(freq_array.shape)
 | 
			
		||||
            else:
 | 
			
		||||
                interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power'])
 | 
			
		||||
                alphap_fiber = interp_alphap(freq_array)
 | 
			
		||||
 | 
			
		||||
            freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
 | 
			
		||||
            interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
 | 
			
		||||
            cr = interp_cr(freq_diff)
 | 
			
		||||
 | 
			
		||||
            # z propagation axis
 | 
			
		||||
            z = np.arange(0, fiber_length+1, z_resolution)
 | 
			
		||||
 | 
			
		||||
            ode_function = lambda z, p: self._ode_stimulated_raman(z, p, alphap_fiber, freq_array, cr, prop_direct)
 | 
			
		||||
            boundary_residual = lambda ya, yb: self._residuals_stimulated_raman(ya, yb, power_spectrum, prop_direct)
 | 
			
		||||
            initial_guess_conditions = self._initial_guess_stimulated_raman(z, power_spectrum, alphap_fiber, prop_direct)
 | 
			
		||||
 | 
			
		||||
            # ODE SOLVER
 | 
			
		||||
            stimulated_raman_scattering = solve_bvp(ode_function, boundary_residual, z, initial_guess_conditions, tol=tolerance)
 | 
			
		||||
 | 
			
		||||
            rho = (stimulated_raman_scattering.y.transpose() / power_spectrum).transpose()
 | 
			
		||||
            rho = np.sqrt(rho)    # From power attenuation to field attenuation
 | 
			
		||||
            setattr(stimulated_raman_scattering, 'frequency', freq_array)
 | 
			
		||||
            setattr(stimulated_raman_scattering, 'z', stimulated_raman_scattering.x)
 | 
			
		||||
            setattr(stimulated_raman_scattering, 'rho', rho)
 | 
			
		||||
            setattr(stimulated_raman_scattering, 'power', stimulated_raman_scattering.y)
 | 
			
		||||
            delattr(stimulated_raman_scattering, 'x')
 | 
			
		||||
            delattr(stimulated_raman_scattering, 'y')
 | 
			
		||||
 | 
			
		||||
            self.carriers = carriers
 | 
			
		||||
            self.raman_pumps = raman_pumps
 | 
			
		||||
            self._stimulated_raman_scattering = stimulated_raman_scattering
 | 
			
		||||
 | 
			
		||||
        return self._stimulated_raman_scattering
 | 
			
		||||
 | 
			
		||||
    def _residuals_stimulated_raman(self, ya, yb, power_spectrum, prop_direct):
 | 
			
		||||
 | 
			
		||||
        computed_boundary_value = np.zeros(ya.size)
 | 
			
		||||
 | 
			
		||||
        for index, direction in enumerate(prop_direct):
 | 
			
		||||
            if direction == +1:
 | 
			
		||||
                computed_boundary_value[index] = ya[index]
 | 
			
		||||
            else:
 | 
			
		||||
                computed_boundary_value[index] = yb[index]
 | 
			
		||||
 | 
			
		||||
        return power_spectrum - computed_boundary_value
 | 
			
		||||
 | 
			
		||||
    def _initial_guess_stimulated_raman(self, z, power_spectrum, alphap_fiber, prop_direct):
 | 
			
		||||
        """ Computes the initial guess knowing the boundary conditions
 | 
			
		||||
        :param z: patial axis [m]. numpy array
 | 
			
		||||
        :param power_spectrum: power in each frequency slice [W].    Frequency axis is defined by freq_array. numpy array
 | 
			
		||||
        :param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array
 | 
			
		||||
        :param prop_direct: indicates the propagation direction of each power slice in power_spectrum:
 | 
			
		||||
        +1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array
 | 
			
		||||
        :return: power_guess: guess on the initial conditions [W]. The first ndarray index identifies the frequency slice,
 | 
			
		||||
        the second ndarray index identifies the step in z. ndarray
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        power_guess = np.empty((power_spectrum.size, z.size))
 | 
			
		||||
        for f_index, power_slice in enumerate(power_spectrum):
 | 
			
		||||
            if prop_direct[f_index] == +1:
 | 
			
		||||
                power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z) * power_slice
 | 
			
		||||
            else:
 | 
			
		||||
                power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z[::-1]) * power_slice
 | 
			
		||||
 | 
			
		||||
        return power_guess
 | 
			
		||||
 | 
			
		||||
    def _ode_stimulated_raman(self, z, power_spectrum, alphap_fiber, freq_array, cr_raman_matrix, prop_direct):
 | 
			
		||||
        """ Aim of ode_raman is to implement the set of ordinary differential equations (ODEs) describing the Raman effect.
 | 
			
		||||
        :param z: spatial axis (unused).
 | 
			
		||||
        :param power_spectrum: power in each frequency slice [W].    Frequency axis is defined by freq_array. numpy array. Size n
 | 
			
		||||
        :param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array. Size n
 | 
			
		||||
        :param freq_array: reference frequency axis [Hz]. numpy array. Size n
 | 
			
		||||
        :param cr_raman: Cr(f) Raman gain efficiency variation in frequency [1/W/m]. Frequency defined by freq_array. numpy ndarray. Size nxn
 | 
			
		||||
        :param prop_direct: indicates the propagation direction of each power slice in power_spectrum:
 | 
			
		||||
        +1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array. Size n
 | 
			
		||||
        :return: dP/dz: the power variation in dz [W/m]. numpy array. Size n
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        dpdz = np.nan * np.ones(power_spectrum.shape)
 | 
			
		||||
        for f_ind, power in enumerate(power_spectrum):
 | 
			
		||||
            cr_raman = cr_raman_matrix[f_ind, :]
 | 
			
		||||
            vibrational_loss = freq_array[f_ind] / freq_array[:f_ind]
 | 
			
		||||
 | 
			
		||||
            for z_ind, power_sample in enumerate(power):
 | 
			
		||||
                raman_gain = np.sum(cr_raman[f_ind+1:] * power_spectrum[f_ind+1:, z_ind])
 | 
			
		||||
                raman_loss = np.sum(vibrational_loss * cr_raman[:f_ind] * power_spectrum[:f_ind, z_ind])
 | 
			
		||||
 | 
			
		||||
                dpdz_element = prop_direct[f_ind] * (-alphap_fiber[f_ind] + raman_gain - raman_loss) * power_sample
 | 
			
		||||
                dpdz[f_ind][z_ind] = dpdz_element
 | 
			
		||||
 | 
			
		||||
        return np.vstack(dpdz)
 | 
			
		||||
 | 
			
		||||
class NliSolver:
 | 
			
		||||
    """ This class implements the NLI models.
 | 
			
		||||
        Model and method can be specified in `self.nli_params.method`.
 | 
			
		||||
        List of implemented methods:
 | 
			
		||||
        'gn_model_analytic': brute force triple integral solution
 | 
			
		||||
        'ggn_spectrally_separated_xpm_spm': XPM plus SPM
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, nli_params=None, fiber_params=None):
 | 
			
		||||
        """ Initialize the fiber object with its physical parameters
 | 
			
		||||
        """
 | 
			
		||||
        self.fiber_params = fiber_params
 | 
			
		||||
        self.nli_params = nli_params
 | 
			
		||||
        self.stimulated_raman_scattering = None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def fiber_params(self):
 | 
			
		||||
        return self._fiber_params
 | 
			
		||||
 | 
			
		||||
    @fiber_params.setter
 | 
			
		||||
    def fiber_params(self, fiber_params):
 | 
			
		||||
        self._fiber_params = fiber_params
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def stimulated_raman_scattering(self):
 | 
			
		||||
        return self._stimulated_raman_scattering
 | 
			
		||||
 | 
			
		||||
    @stimulated_raman_scattering.setter
 | 
			
		||||
    def stimulated_raman_scattering(self, stimulated_raman_scattering):
 | 
			
		||||
        self._stimulated_raman_scattering = stimulated_raman_scattering
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def nli_params(self):
 | 
			
		||||
        return self._nli_params
 | 
			
		||||
 | 
			
		||||
    @nli_params.setter
 | 
			
		||||
    def nli_params(self, nli_params):
 | 
			
		||||
        """
 | 
			
		||||
        :param model_params: namedtuple containing the parameters used to compute the NLI.
 | 
			
		||||
        """
 | 
			
		||||
        self._nli_params = nli_params
 | 
			
		||||
 | 
			
		||||
    def compute_nli(self, carrier, *carriers):
 | 
			
		||||
        """ Compute NLI power generated by the WDM comb `*carriers` on the channel under test `carrier`
 | 
			
		||||
        at the end of the fiber span.
 | 
			
		||||
        """
 | 
			
		||||
        if 'gn_model_analytic' == self.nli_params.nli_method_name.lower():
 | 
			
		||||
            carrier_nli = self._gn_analytic(carrier, *carriers)
 | 
			
		||||
        elif 'ggn_spectrally_separated' in self.nli_params.nli_method_name.lower():
 | 
			
		||||
            eta_matrix = self._compute_eta_matrix(carrier, *carriers)
 | 
			
		||||
            carrier_nli = self._carrier_nli_from_eta_matrix(eta_matrix, carrier, *carriers)
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError(f'Method {self.nli_params.method_nli} not implemented.')
 | 
			
		||||
 | 
			
		||||
        return carrier_nli
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _carrier_nli_from_eta_matrix(eta_matrix, carrier, *carriers):
 | 
			
		||||
        carrier_nli = 0
 | 
			
		||||
        for pump_carrier_1 in carriers:
 | 
			
		||||
            for pump_carrier_2 in carriers:
 | 
			
		||||
                carrier_nli += eta_matrix[pump_carrier_1.channel_number-1, pump_carrier_2.channel_number-1] * \
 | 
			
		||||
                               pump_carrier_1.power.signal * pump_carrier_2.power.signal
 | 
			
		||||
        carrier_nli *= carrier.power.signal
 | 
			
		||||
 | 
			
		||||
        return carrier_nli
 | 
			
		||||
 | 
			
		||||
    def _compute_eta_matrix(self, carrier_cut, *carriers):
 | 
			
		||||
        cut_index = carrier_cut.channel_number - 1
 | 
			
		||||
        # Matrix initialization
 | 
			
		||||
        matrix_size = max(carriers, key=lambda x: getattr(x, 'channel_number')).channel_number
 | 
			
		||||
        eta_matrix = np.zeros(shape=(matrix_size, matrix_size))
 | 
			
		||||
 | 
			
		||||
        # SPM
 | 
			
		||||
        logger.debug(f'Start computing SPM on channel #{carrier_cut.channel_number}')
 | 
			
		||||
        # SPM GGN
 | 
			
		||||
        if 'ggn' in self.nli_params.nli_method_name.lower():
 | 
			
		||||
            partial_nli = self._generalized_spectrally_separated_spm(carrier_cut)
 | 
			
		||||
        # SPM GN
 | 
			
		||||
        elif 'gn' in self.nli_params.nli_method_name.lower():
 | 
			
		||||
            partial_nli = self._gn_analytic(carrier_cut, *[carrier_cut])
 | 
			
		||||
        eta_matrix[cut_index, cut_index] = partial_nli / (carrier_cut.power.signal**3)
 | 
			
		||||
 | 
			
		||||
        # XPM
 | 
			
		||||
        for pump_carrier in carriers:
 | 
			
		||||
            pump_index = pump_carrier.channel_number - 1
 | 
			
		||||
            if not (cut_index == pump_index):
 | 
			
		||||
                logger.debug(f'Start computing XPM on channel #{carrier_cut.channel_number} '
 | 
			
		||||
                             f'from channel #{pump_carrier.channel_number}')
 | 
			
		||||
                # XPM GGN
 | 
			
		||||
                if 'ggn' in self.nli_params.nli_method_name.lower():
 | 
			
		||||
                    partial_nli = self._generalized_spectrally_separated_xpm(carrier_cut, pump_carrier)
 | 
			
		||||
                # XPM GGN
 | 
			
		||||
                elif 'gn' in self.nli_params.nli_method_name.lower():
 | 
			
		||||
                    partial_nli = self._gn_analytic(carrier_cut, *[pump_carrier])
 | 
			
		||||
                eta_matrix[pump_index, pump_index] = partial_nli /\
 | 
			
		||||
                                                     (carrier_cut.power.signal * pump_carrier.power.signal**2)
 | 
			
		||||
        return eta_matrix
 | 
			
		||||
 | 
			
		||||
    # Methods for computing GN-model
 | 
			
		||||
    def _gn_analytic(self, carrier, *carriers):
 | 
			
		||||
        """ Computes the nonlinear interference power on a single carrier.
 | 
			
		||||
        The method uses eq. 120 from arXiv:1209.0394.
 | 
			
		||||
        :param carrier: the signal under analysis
 | 
			
		||||
        :param carriers: the full WDM comb
 | 
			
		||||
        :return: carrier_nli: the amount of nonlinear interference in W on the carrier under analysis
 | 
			
		||||
        """
 | 
			
		||||
        alpha = self.fiber_params.alpha0() / 2
 | 
			
		||||
        beta2 = self.fiber_params.beta2
 | 
			
		||||
        gamma = self.fiber_params.gamma
 | 
			
		||||
        length = self.fiber_params.length
 | 
			
		||||
        effective_length = (1 - np.exp(-2 * alpha * length)) / (2 * alpha)
 | 
			
		||||
        asymptotic_length = 1 / (2 * alpha)
 | 
			
		||||
 | 
			
		||||
        g_nli = 0
 | 
			
		||||
        for interfering_carrier in carriers:
 | 
			
		||||
            g_interfearing = interfering_carrier.power.signal / interfering_carrier.baud_rate
 | 
			
		||||
            g_signal = carrier.power.signal / carrier.baud_rate
 | 
			
		||||
            g_nli += g_interfearing**2 * g_signal \
 | 
			
		||||
                * _psi(carrier, interfering_carrier, beta2=self.fiber_params.beta2, asymptotic_length=1/self.fiber_params.alpha0())
 | 
			
		||||
        g_nli *= (16.0 / 27.0) * (gamma * effective_length)**2 /\
 | 
			
		||||
                 (2 * np.pi * abs(beta2) * asymptotic_length)
 | 
			
		||||
        carrier_nli = carrier.baud_rate * g_nli
 | 
			
		||||
        return carrier_nli
 | 
			
		||||
 | 
			
		||||
    # Methods for computing the GGN-model
 | 
			
		||||
    def _generalized_spectrally_separated_spm(self, carrier):
 | 
			
		||||
        f_cut_resolution = self.nli_params.f_cut_resolution['delta_0']
 | 
			
		||||
        f_eval = carrier.frequency
 | 
			
		||||
        g_cut = (carrier.power.signal / carrier.baud_rate)
 | 
			
		||||
 | 
			
		||||
        spm_nli = carrier.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_cut**3 * \
 | 
			
		||||
                  self._generalized_psi(carrier, carrier, f_eval, f_cut_resolution, f_cut_resolution)
 | 
			
		||||
        return spm_nli
 | 
			
		||||
 | 
			
		||||
    def _generalized_spectrally_separated_xpm(self, carrier_cut, pump_carrier):
 | 
			
		||||
        delta_index = pump_carrier.channel_number - carrier_cut.channel_number
 | 
			
		||||
        f_cut_resolution = self.nli_params.f_cut_resolution[f'delta_{delta_index}']
 | 
			
		||||
        f_pump_resolution = self.nli_params.f_pump_resolution
 | 
			
		||||
        f_eval = carrier_cut.frequency
 | 
			
		||||
        g_pump = (pump_carrier.power.signal / pump_carrier.baud_rate)
 | 
			
		||||
        g_cut = (carrier_cut.power.signal / carrier_cut.baud_rate)
 | 
			
		||||
        frequency_offset_threshold = self._frequency_offset_threshold(pump_carrier.baud_rate)
 | 
			
		||||
        if abs(carrier_cut.frequency - pump_carrier.frequency) <= frequency_offset_threshold:
 | 
			
		||||
            xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \
 | 
			
		||||
                      2 * self._generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution)
 | 
			
		||||
        else:
 | 
			
		||||
            xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \
 | 
			
		||||
                      2 * self._fast_generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution)
 | 
			
		||||
        return xpm_nli
 | 
			
		||||
 | 
			
		||||
    def _fast_generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution):
 | 
			
		||||
        """ It computes the generalized psi function similarly to the one used in the GN model
 | 
			
		||||
        :return: generalized_psi
 | 
			
		||||
        """
 | 
			
		||||
        # Fiber parameters
 | 
			
		||||
        alpha0 = self.fiber_params.alpha0(f_eval)
 | 
			
		||||
        beta2 = self.fiber_params.beta2
 | 
			
		||||
        beta3 = self.fiber_params.beta3
 | 
			
		||||
        f_ref_beta = self.fiber_params.f_ref_beta
 | 
			
		||||
        z = self.stimulated_raman_scattering.z
 | 
			
		||||
        frequency_rho = self.stimulated_raman_scattering.frequency
 | 
			
		||||
        rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
 | 
			
		||||
        if len(frequency_rho) == 1:
 | 
			
		||||
            rho_function = lambda f: rho_norm[0, :]
 | 
			
		||||
        else:
 | 
			
		||||
            rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate')
 | 
			
		||||
        rho_norm_pump = rho_function(pump_carrier.frequency)
 | 
			
		||||
 | 
			
		||||
        f1_array = np.array([pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
 | 
			
		||||
                             pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2)])
 | 
			
		||||
        f2_array = np.arange(carrier_cut.frequency,
 | 
			
		||||
                             carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
 | 
			
		||||
                             f_cut_resolution)  # Only positive f2 is used since integrand_f2 is symmetric
 | 
			
		||||
 | 
			
		||||
        integrand_f1 = np.zeros(len(f1_array))
 | 
			
		||||
        for f1_index, f1 in enumerate(f1_array):
 | 
			
		||||
            delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
 | 
			
		||||
                         (beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
 | 
			
		||||
            integrand_f2 = self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0)
 | 
			
		||||
            integrand_f1[f1_index] = 2 * np.trapz(integrand_f2, f2_array)  # 2x since integrand_f2 is symmetric in f2
 | 
			
		||||
        generalized_psi = 0.5 * sum(integrand_f1) * pump_carrier.baud_rate
 | 
			
		||||
        return generalized_psi
 | 
			
		||||
 | 
			
		||||
    def _generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution):
 | 
			
		||||
        """ It computes the generalized psi function similarly to the one used in the GN model
 | 
			
		||||
        :return: generalized_psi
 | 
			
		||||
        """
 | 
			
		||||
        # Fiber parameters
 | 
			
		||||
        alpha0 = self.fiber_params.alpha0(f_eval)
 | 
			
		||||
        beta2 = self.fiber_params.beta2
 | 
			
		||||
        beta3 = self.fiber_params.beta3
 | 
			
		||||
        f_ref_beta = self.fiber_params.f_ref_beta
 | 
			
		||||
        z = self.stimulated_raman_scattering.z
 | 
			
		||||
        frequency_rho = self.stimulated_raman_scattering.frequency
 | 
			
		||||
        rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
 | 
			
		||||
        if len(frequency_rho) == 1:
 | 
			
		||||
            rho_function = lambda f: rho_norm[0, :]
 | 
			
		||||
        else:
 | 
			
		||||
            rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate')
 | 
			
		||||
        rho_norm_pump = rho_function(pump_carrier.frequency)
 | 
			
		||||
 | 
			
		||||
        f1_array = np.arange(pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
 | 
			
		||||
                             pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
 | 
			
		||||
                             f_pump_resolution)
 | 
			
		||||
        f2_array = np.arange(carrier_cut.frequency - (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
 | 
			
		||||
                             carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
 | 
			
		||||
                             f_cut_resolution)
 | 
			
		||||
        psd1 = raised_cosine_comb(f1_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal)
 | 
			
		||||
 | 
			
		||||
        integrand_f1 = np.zeros(len(f1_array))
 | 
			
		||||
        for f1_index, (f1, psd1_sample) in enumerate(zip(f1_array, psd1)):
 | 
			
		||||
            f3_array = f1 + f2_array - f_eval
 | 
			
		||||
            psd2 = raised_cosine_comb(f2_array, carrier_cut) * (carrier_cut.baud_rate / carrier_cut.power.signal)
 | 
			
		||||
            psd3 = raised_cosine_comb(f3_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal)
 | 
			
		||||
            ggg = psd1_sample * psd2 * psd3
 | 
			
		||||
 | 
			
		||||
            delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
 | 
			
		||||
                         (beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
 | 
			
		||||
 | 
			
		||||
            integrand_f2 = ggg * self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0)
 | 
			
		||||
            integrand_f1[f1_index] = np.trapz(integrand_f2, f2_array)
 | 
			
		||||
        generalized_psi = np.trapz(integrand_f1, f1_array)
 | 
			
		||||
        return generalized_psi
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0):
 | 
			
		||||
        w = 1j * delta_beta - alpha0
 | 
			
		||||
        generalized_rho_nli = (rho_norm_pump[-1]**2 * np.exp(w * z[-1]) - rho_norm_pump[0]**2 * np.exp(w * z[0])) / w
 | 
			
		||||
        for z_ind in range(0, len(z) - 1):
 | 
			
		||||
            derivative_rho = (rho_norm_pump[z_ind + 1]**2 - rho_norm_pump[z_ind]**2) / (z[z_ind + 1] - z[z_ind])
 | 
			
		||||
            generalized_rho_nli -= derivative_rho * (np.exp(w * z[z_ind + 1]) - np.exp(w * z[z_ind])) / (w**2)
 | 
			
		||||
        generalized_rho_nli = np.abs(generalized_rho_nli)**2
 | 
			
		||||
        return generalized_rho_nli
 | 
			
		||||
 | 
			
		||||
    def _frequency_offset_threshold(self, symbol_rate):
 | 
			
		||||
        k_ref = 5
 | 
			
		||||
        beta2_ref = 21.3e-27
 | 
			
		||||
        delta_f_ref = 50e9
 | 
			
		||||
        rs_ref = 32e9
 | 
			
		||||
        freq_offset_th = ((k_ref * delta_f_ref) * rs_ref * beta2_ref) / (self.fiber_params.beta2 * symbol_rate)
 | 
			
		||||
        return freq_offset_th
 | 
			
		||||
 | 
			
		||||
def _psi(carrier, interfering_carrier, beta2, asymptotic_length):
 | 
			
		||||
    """Calculates eq. 123 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__"""
 | 
			
		||||
 | 
			
		||||
    if carrier.channel_number == interfering_carrier.channel_number: # SCI, SPM
 | 
			
		||||
        psi = np.arcsinh(0.5 * np.pi**2 * asymptotic_length * abs(beta2) * carrier.baud_rate**2)
 | 
			
		||||
    else: # XCI, XPM
 | 
			
		||||
        delta_f = carrier.frequency - interfering_carrier.frequency
 | 
			
		||||
        psi = np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) *
 | 
			
		||||
                         carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
 | 
			
		||||
        psi -= np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) *
 | 
			
		||||
                          carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
 | 
			
		||||
    return psi
 | 
			
		||||
@@ -22,6 +22,7 @@ from json import dumps
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from gnpy.core.equipment import load_equipment
 | 
			
		||||
from gnpy.core.utils import db2lin, lin2db
 | 
			
		||||
from gnpy.core.exceptions import ServiceError
 | 
			
		||||
 | 
			
		||||
SERVICES_COLUMN = 12
 | 
			
		||||
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
 | 
			
		||||
@@ -43,17 +44,18 @@ class Element:
 | 
			
		||||
        return hash((type(self), self.uid))
 | 
			
		||||
 | 
			
		||||
class Request_element(Element):
 | 
			
		||||
    def __init__(self,Request,eqpt_filename):
 | 
			
		||||
    def __init__(self, Request, eqpt_filename, bidir):
 | 
			
		||||
        # request_id is str
 | 
			
		||||
        # excel has automatic number formatting that adds .0 on integer values
 | 
			
		||||
        # the next lines recover the pure int value, assuming this .0 is unwanted
 | 
			
		||||
        self.request_id = correct_xlrd_int_to_str_reading(Request.request_id)
 | 
			
		||||
        self.source = Request.source
 | 
			
		||||
        self.destination = Request.destination
 | 
			
		||||
        self.source = f'trx {Request.source}'
 | 
			
		||||
        self.destination = f'trx {Request.destination}'
 | 
			
		||||
        # TODO: the automatic naming generated by excel parser requires that source and dest name 
 | 
			
		||||
        # be a string starting with 'trx' : this is manually added here.
 | 
			
		||||
        self.srctpid = f'trx {Request.source}'
 | 
			
		||||
        self.dsttpid = f'trx {Request.destination}'
 | 
			
		||||
        self.bidir = bidir
 | 
			
		||||
        # test that trx_type belongs to eqpt_config.json
 | 
			
		||||
        # if not replace it with a default
 | 
			
		||||
        equipment = load_equipment(eqpt_filename)
 | 
			
		||||
@@ -73,17 +75,17 @@ class Request_element(Element):
 | 
			
		||||
                Requestmode = None
 | 
			
		||||
                self.mode = Request.mode
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Requestmode}\' in eqpt library \nComputation stopped.'
 | 
			
		||||
            msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Request.mode}\' in eqpt library \nComputation stopped.'
 | 
			
		||||
            #print(msg)
 | 
			
		||||
            logger.critical(msg)
 | 
			
		||||
            exit()
 | 
			
		||||
            raise ServiceError(msg)
 | 
			
		||||
        # excel input are in GHz and dBm
 | 
			
		||||
        if Request.spacing is not None:
 | 
			
		||||
            self.spacing = Request.spacing * 1e9
 | 
			
		||||
        else:
 | 
			
		||||
            msg = f'Request {self.request_id} missing spacing: spacing is mandatory.\ncomputation stopped'
 | 
			
		||||
            logger.critical(msg)
 | 
			
		||||
            exit()
 | 
			
		||||
            raise ServiceError(msg)
 | 
			
		||||
        if Request.power is not None:
 | 
			
		||||
            self.power =  db2lin(Request.power) * 1e-3
 | 
			
		||||
        else:
 | 
			
		||||
@@ -120,9 +122,9 @@ class Request_element(Element):
 | 
			
		||||
 | 
			
		||||
        # the excel parser applies the same hop-type to all nodes in the route nodes_list.
 | 
			
		||||
        # user can change this per node in the generated json
 | 
			
		||||
        self.loose = 'loose'
 | 
			
		||||
        self.loose = 'LOOSE'
 | 
			
		||||
        if Request.is_loose == 'no' :
 | 
			
		||||
            self.loose = 'strict'
 | 
			
		||||
            self.loose = 'STRICT'
 | 
			
		||||
        self.path_bandwidth = None
 | 
			
		||||
        if Request.path_bandwidth is not None:
 | 
			
		||||
            self.path_bandwidth = Request.path_bandwidth * 1e9
 | 
			
		||||
@@ -132,46 +134,41 @@ class Request_element(Element):
 | 
			
		||||
    uid = property(lambda self: repr(self))
 | 
			
		||||
    @property
 | 
			
		||||
    def pathrequest(self):
 | 
			
		||||
        # Default assumption for bidir is False
 | 
			
		||||
        req_dictionnary = {
 | 
			
		||||
                    'request-id':self.request_id,
 | 
			
		||||
                    'source':    self.source,
 | 
			
		||||
                    'destination':  self.destination,
 | 
			
		||||
                    'src-tp-id': self.srctpid,
 | 
			
		||||
                    'dst-tp-id': self.dsttpid,
 | 
			
		||||
                    'bidirectional': self.bidir,
 | 
			
		||||
                    'path-constraints':{
 | 
			
		||||
                        'te-bandwidth': {
 | 
			
		||||
                            'technology': 'flexi-grid',
 | 
			
		||||
                            'trx_type'  : self.trx_type,
 | 
			
		||||
                            'trx_mode'  : self.mode,
 | 
			
		||||
                            'effective-freq-slot':[{'n': 'null','m': 'null'}] ,
 | 
			
		||||
                            'effective-freq-slot':[{'N': 'null', 'M': 'null'}],
 | 
			
		||||
                            'spacing'   : self.spacing,
 | 
			
		||||
                            'max-nb-of-channel'  : self.nb_channel,
 | 
			
		||||
                            'output-power'       : self.power
 | 
			
		||||
                            # 'path_bandwidth'       : self.path_bandwidth 
 | 
			
		||||
                        }
 | 
			
		||||
                    },
 | 
			
		||||
                    'optimizations': {
 | 
			
		||||
                        'explicit-route-include-objects': [
 | 
			
		||||
                        {
 | 
			
		||||
                            'index': self.nodes_list.index(node),
 | 
			
		||||
                            'unnumbered-hop':{
 | 
			
		||||
                                'node-id': f'{node}',
 | 
			
		||||
                                'link-tp-id': 'link-tp-id is not used',
 | 
			
		||||
                                'hop-type': f'{self.loose}',
 | 
			
		||||
                                'direction': 'direction is not used'
 | 
			
		||||
                            },
 | 
			
		||||
                            'label-hop':{
 | 
			
		||||
                                'te-label': {
 | 
			
		||||
                                    'generic': 'generic is not used',
 | 
			
		||||
                                    'direction': 'direction is not used'
 | 
			
		||||
                                }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        if self.nodes_list:
 | 
			
		||||
            req_dictionnary['explicit-route-objects'] = {}
 | 
			
		||||
            temp = {'route-object-include-exclude' : [
 | 
			
		||||
                        {'explicit-route-usage': 'route-include-ero',
 | 
			
		||||
                        'index': self.nodes_list.index(node),
 | 
			
		||||
                        'num-unnum-hop': {
 | 
			
		||||
                            'node-id': f'{node}',
 | 
			
		||||
                            'link-tp-id': 'link-tp-id is not used',
 | 
			
		||||
                            'hop-type': f'{self.loose}',
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        for node in self.nodes_list
 | 
			
		||||
                    ]
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
                        for node in self.nodes_list]
 | 
			
		||||
                   }
 | 
			
		||||
            req_dictionnary['explicit-route-objects'] = temp
 | 
			
		||||
        if self.path_bandwidth is not None:
 | 
			
		||||
            req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth
 | 
			
		||||
            
 | 
			
		||||
@@ -181,33 +178,44 @@ class Request_element(Element):
 | 
			
		||||
        if self.disjoint_from :
 | 
			
		||||
            return {'synchronization-id':self.request_id,
 | 
			
		||||
                'svec': {
 | 
			
		||||
                    'relaxable' : 'False',
 | 
			
		||||
                    'link-diverse': 'True',
 | 
			
		||||
                    'node-diverse': 'True',
 | 
			
		||||
                    'relaxable' : 'false',
 | 
			
		||||
                    'disjointness': 'node link',
 | 
			
		||||
                    'request-id-number': [self.request_id]+ [n for n in self.disjoint_from]
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        else:
 | 
			
		||||
            return None
 | 
			
		||||
        # TO-DO: avoid multiple entries with same synchronisation vectors
 | 
			
		||||
    @property
 | 
			
		||||
    def json(self):
 | 
			
		||||
        return self.pathrequest , self.pathsync
 | 
			
		||||
 | 
			
		||||
def convert_service_sheet(input_filename, eqpt_filename, output_filename='', filter_region=[]):
 | 
			
		||||
def convert_service_sheet(input_filename, eqpt_filename, output_filename='', bidir=False, filter_region=None):
 | 
			
		||||
    """ converts a service sheet into a json structure
 | 
			
		||||
    """
 | 
			
		||||
    if filter_region is None:
 | 
			
		||||
        filter_region = []
 | 
			
		||||
    service = parse_excel(input_filename)
 | 
			
		||||
    req = [Request_element(n,eqpt_filename) for n in service]
 | 
			
		||||
    req = [Request_element(n, eqpt_filename, bidir) for n in service]
 | 
			
		||||
    # dumps the output into a json file with name
 | 
			
		||||
    # split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]]
 | 
			
		||||
    if output_filename=='':
 | 
			
		||||
        output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json'
 | 
			
		||||
    # for debug
 | 
			
		||||
    # print(json_filename)
 | 
			
		||||
    data = {
 | 
			
		||||
        'path-request': [n.json[0] for n in req],
 | 
			
		||||
        'synchronization': [n.json[1] for n in req
 | 
			
		||||
        if n.json[1] is not None]
 | 
			
		||||
    }
 | 
			
		||||
    # if there is no sync vector , do not write any synchronization
 | 
			
		||||
    synchro = [n.json[1] for n in req if n.json[1] is not None]
 | 
			
		||||
    if synchro:
 | 
			
		||||
        data = {
 | 
			
		||||
            'path-request': [n.json[0] for n in req],
 | 
			
		||||
            'synchronization': synchro
 | 
			
		||||
        }
 | 
			
		||||
    else:
 | 
			
		||||
        data = {
 | 
			
		||||
            'path-request': [n.json[0] for n in req]
 | 
			
		||||
            }
 | 
			
		||||
    with open(output_filename, 'w', encoding='utf-8') as f:
 | 
			
		||||
            f.write(dumps(data, indent=2, ensure_ascii=False))
 | 
			
		||||
        f.write(dumps(data, indent=2, ensure_ascii=False))
 | 
			
		||||
    return data
 | 
			
		||||
 | 
			
		||||
def correct_xlrd_int_to_str_reading(v) :
 | 
			
		||||
@@ -232,25 +240,29 @@ def parse_excel(input_filename):
 | 
			
		||||
    return services
 | 
			
		||||
 | 
			
		||||
def parse_service_sheet(service_sheet):
 | 
			
		||||
        logger.info(f'Validating headers on {service_sheet.name!r}')
 | 
			
		||||
        # add a test on field to enable the '' field case that arises when columns on the 
 | 
			
		||||
        # right hand side are used as comments or drawing in the excel sheet
 | 
			
		||||
        header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN] if len(x.value.strip())>0]
 | 
			
		||||
    """ reads each column according to authorized fieldnames. order is not important.
 | 
			
		||||
    """
 | 
			
		||||
    logger.info(f'Validating headers on {service_sheet.name!r}')
 | 
			
		||||
    # add a test on field to enable the '' field case that arises when columns on the
 | 
			
		||||
    # right hand side are used as comments or drawing in the excel sheet
 | 
			
		||||
    header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN]
 | 
			
		||||
                if len(x.value.strip()) > 0]
 | 
			
		||||
 | 
			
		||||
        # create a service_fieldname independant from the excel column order
 | 
			
		||||
        # to be compatible with any version of the sheet
 | 
			
		||||
        # the following dictionnary records the excel field names and the corresponding parameter's name
 | 
			
		||||
    # create a service_fieldname independant from the excel column order
 | 
			
		||||
    # to be compatible with any version of the sheet
 | 
			
		||||
    # the following dictionnary records the excel field names and the corresponding parameter's name
 | 
			
		||||
 | 
			
		||||
        authorized_fieldnames = {'route id':'request_id', 'Source':'source', 'Destination':'destination', \
 | 
			
		||||
         'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \
 | 
			
		||||
         'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\
 | 
			
		||||
         'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\
 | 
			
		||||
         'routing: is loose?':'is_loose', 'path bandwidth':'path_bandwidth'}
 | 
			
		||||
        try :
 | 
			
		||||
            service_fieldnames = [authorized_fieldnames[e] for e in header]
 | 
			
		||||
        except KeyError:
 | 
			
		||||
            msg = f'Malformed header on Service sheet: {header} field not in {authorized_fieldnames}'
 | 
			
		||||
            logger.critical(msg)
 | 
			
		||||
            raise ValueError(msg)
 | 
			
		||||
        for row in all_rows(service_sheet, start=5):
 | 
			
		||||
            yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames))
 | 
			
		||||
    authorized_fieldnames = {
 | 
			
		||||
        'route id':'request_id', 'Source':'source', 'Destination':'destination', \
 | 
			
		||||
        'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \
 | 
			
		||||
        'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\
 | 
			
		||||
        'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\
 | 
			
		||||
        'routing: is loose?':'is_loose', 'path bandwidth':'path_bandwidth'}
 | 
			
		||||
    try:
 | 
			
		||||
        service_fieldnames = [authorized_fieldnames[e] for e in header]
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        msg = f'Malformed header on Service sheet: {header} field not in {authorized_fieldnames}'
 | 
			
		||||
        logger.critical(msg)
 | 
			
		||||
        raise ValueError(msg)
 | 
			
		||||
    for row in all_rows(service_sheet, start=5):
 | 
			
		||||
        yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames))
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										386
									
								
								gnpy/core/spectrum_assignment.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								gnpy/core/spectrum_assignment.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,386 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
gnpy.core.spectrum_assignment
 | 
			
		||||
=============================
 | 
			
		||||
 | 
			
		||||
This module contains the Oms and Bitmap classes and the different method to
 | 
			
		||||
select and assign spectrum. Spectrum_selection function identifies the free
 | 
			
		||||
slots and select_candidate selects the candidate spectrum according to
 | 
			
		||||
strategy: for example first fit
 | 
			
		||||
oms records its elements, and elements are updated with an oms to have
 | 
			
		||||
element/oms correspondace
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from collections import namedtuple
 | 
			
		||||
from logging import getLogger
 | 
			
		||||
from math import ceil
 | 
			
		||||
from gnpy.core.elements import Roadm, Transceiver
 | 
			
		||||
from gnpy.core.exceptions import SpectrumError
 | 
			
		||||
 | 
			
		||||
LOGGER = getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
class Bitmap:
 | 
			
		||||
    """ records the spectrum occupation
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, f_min, f_max, grid, guardband=0.15e12, bitmap=None):
 | 
			
		||||
        # n is the min index including guardband. Guardband is require to be sure
 | 
			
		||||
        # that a channel can be assigned  with center frequency fmin (means that its
 | 
			
		||||
        # slot occupation goes below freq_index_min
 | 
			
		||||
        n_min = frequency_to_n(f_min-guardband, grid)
 | 
			
		||||
        n_max = frequency_to_n(f_max+guardband, grid) - 1
 | 
			
		||||
        self.n_min = n_min
 | 
			
		||||
        self.n_max = n_max
 | 
			
		||||
        self.freq_index_min = frequency_to_n(f_min)
 | 
			
		||||
        self.freq_index_max = frequency_to_n(f_max)
 | 
			
		||||
        self.freq_index = list(range(n_min, n_max+1))
 | 
			
		||||
        if bitmap is None:
 | 
			
		||||
            self.bitmap = [1] * (n_max-n_min+1)
 | 
			
		||||
        elif len(bitmap) == len(self.freq_index):
 | 
			
		||||
            self.bitmap = bitmap
 | 
			
		||||
        else:
 | 
			
		||||
            raise SpectrumError(f'bitmap is not consistant with f_min{f_min} - n: {n_min} and f_max{f_max}- n :{n_max}')
 | 
			
		||||
 | 
			
		||||
    def getn(self, i):
 | 
			
		||||
        """ converts the n (itu grid) into a local index
 | 
			
		||||
        """
 | 
			
		||||
        return self.freq_index[i]
 | 
			
		||||
    def geti(self, nvalue):
 | 
			
		||||
        """ converts the local index into n (itu grid)
 | 
			
		||||
        """
 | 
			
		||||
        return self.freq_index.index(nvalue)
 | 
			
		||||
    def insert_left(self, newbitmap):
 | 
			
		||||
        """ insert bitmap on the left to align oms bitmaps if their start frequencies are different
 | 
			
		||||
        """
 | 
			
		||||
        self.bitmap = newbitmap + self.bitmap
 | 
			
		||||
        temp = list(range(self.n_min-len(newbitmap), self.n_min))
 | 
			
		||||
        self.freq_index = temp + self.freq_index
 | 
			
		||||
        self.n_min = self.freq_index[0]
 | 
			
		||||
    def insert_right(self, newbitmap):
 | 
			
		||||
        """ insert bitmap on the right to align oms bitmaps if their stop frequencies are different
 | 
			
		||||
        """
 | 
			
		||||
        self.bitmap = self.bitmap + newbitmap
 | 
			
		||||
        self.freq_index = self.freq_index + list(range(self.n_max, self.n_max+len(newbitmap)))
 | 
			
		||||
        self.n_max = self.freq_index[-1]
 | 
			
		||||
 | 
			
		||||
#    +'grid available_slots f_min f_max services_list')
 | 
			
		||||
OMSParams = namedtuple('OMSParams', 'oms_id el_id_list el_list')
 | 
			
		||||
 | 
			
		||||
class OMS:
 | 
			
		||||
    """ OMS class is the logical container that represent a link between two adjacent ROADMs and
 | 
			
		||||
        records the crossed elements and the occupied spectrum
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, *args, **params):
 | 
			
		||||
        params = OMSParams(**params)
 | 
			
		||||
        self.oms_id = params.oms_id
 | 
			
		||||
        self.el_id_list = params.el_id_list
 | 
			
		||||
        self.el_list = params.el_list
 | 
			
		||||
        self.spectrum_bitmap = []
 | 
			
		||||
        self.nb_channels = 0
 | 
			
		||||
        self.service_list = []
 | 
			
		||||
    # TODO
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return '\n\t'.join([f'{type(self).__name__} {self.oms_id}',
 | 
			
		||||
                               f'{self.el_id_list[0]} - {self.el_id_list[-1]}'])
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return '\n\t'.join([f'{type(self).__name__} {self.oms_id}',
 | 
			
		||||
                               f'{self.el_id_list[0]} - {self.el_id_list[-1]}', '\n'])
 | 
			
		||||
 | 
			
		||||
    def add_element(self, elem):
 | 
			
		||||
        """ records oms elements
 | 
			
		||||
        """
 | 
			
		||||
        self.el_id_list.append(elem.uid)
 | 
			
		||||
        self.el_list.append(elem)
 | 
			
		||||
 | 
			
		||||
    def update_spectrum(self, f_min, f_max, guardband=0.15e12, existing_spectrum=None,
 | 
			
		||||
                        grid=0.00625e12):
 | 
			
		||||
        """ frequencies expressed in Hz
 | 
			
		||||
        """
 | 
			
		||||
        if existing_spectrum is None:
 | 
			
		||||
            # add some 150 GHz margin to enable a center channel on f_min
 | 
			
		||||
            # use ITU-T G694.1
 | 
			
		||||
            # Flexible DWDM grid definition
 | 
			
		||||
            # For the flexible DWDM grid, the allowed frequency slots have a nominal
 | 
			
		||||
            # central frequency (in THz) defined by:
 | 
			
		||||
            # 193.1 + n × 0.00625 where n is a positive or negative integer including 0
 | 
			
		||||
            # and 0.00625 is the nominal central frequency granularity in THz
 | 
			
		||||
            # and a slot width defined by:
 | 
			
		||||
            # 12.5 × m where m is a positive integer and 12.5 is the slot width granularity in
 | 
			
		||||
            # GHz.
 | 
			
		||||
            # Any combination of frequency slots is allowed as long as no two frequency
 | 
			
		||||
            # slots overlap.
 | 
			
		||||
 | 
			
		||||
            # TODO : add explaination on that / parametrize ....
 | 
			
		||||
            self.spectrum_bitmap = Bitmap(f_min, f_max, grid, guardband)
 | 
			
		||||
            # print(len(self.spectrum_bitmap.bitmap))
 | 
			
		||||
 | 
			
		||||
    def assign_spectrum(self, nvalue, mvalue):
 | 
			
		||||
        """ change oms spectrum to mark spectrum assigned
 | 
			
		||||
        """
 | 
			
		||||
        if (nvalue is None or mvalue is None or isinstance(nvalue, float)
 | 
			
		||||
                or isinstance(mvalue, float) or mvalue == 0):
 | 
			
		||||
            raise SpectrumError('could not assign None values')
 | 
			
		||||
        startn, stopn = mvalue_to_slots(nvalue, mvalue)
 | 
			
		||||
        # print(f'startn stop n {startn} , {stopn}')
 | 
			
		||||
        # assumes that guardbands are sufficient to ensure that assigning a center channel
 | 
			
		||||
        # at fmin or fmax is OK is startn > self.spectrum_bitmap.n_min
 | 
			
		||||
        if (nvalue <= self.spectrum_bitmap.freq_index_max and
 | 
			
		||||
                nvalue >= self.spectrum_bitmap.freq_index_min and
 | 
			
		||||
                stopn <= self.spectrum_bitmap.n_max and
 | 
			
		||||
                startn > self.spectrum_bitmap.n_min):
 | 
			
		||||
            # verification that both length are identical
 | 
			
		||||
            self.spectrum_bitmap.bitmap[self.spectrum_bitmap.geti(startn):self.spectrum_bitmap.geti(stopn)+1] = [0] * (stopn-startn+1)
 | 
			
		||||
            return True
 | 
			
		||||
        else:
 | 
			
		||||
            msg = f'Could not assign n {nvalue}, m {mvalue} values:' +\
 | 
			
		||||
                  f' one or several slots are not available'
 | 
			
		||||
            LOGGER.info(msg)
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
    def add_service(self, service_id, nb_wl):
 | 
			
		||||
        """ record service and mark spectrum as occupied
 | 
			
		||||
        """
 | 
			
		||||
        self.service_list.append(service_id)
 | 
			
		||||
        self.nb_channels += nb_wl
 | 
			
		||||
 | 
			
		||||
def frequency_to_n(freq, grid=0.00625e12):
 | 
			
		||||
    """ converts frequency into the n value (ITU grid)
 | 
			
		||||
    """
 | 
			
		||||
    return (int)((freq-193.1e12)/grid)
 | 
			
		||||
 | 
			
		||||
def nvalue_to_frequency(nvalue, grid=0.00625e12):
 | 
			
		||||
    """ converts n value into a frequency
 | 
			
		||||
    """
 | 
			
		||||
    return 193.1e12 + nvalue * grid
 | 
			
		||||
 | 
			
		||||
def mvalue_to_slots(nvalue, mvalue):
 | 
			
		||||
    """ convert center n an m into start and stop n
 | 
			
		||||
    """
 | 
			
		||||
    startn = nvalue - mvalue
 | 
			
		||||
    stopn = nvalue + mvalue -1
 | 
			
		||||
    return startn, stopn
 | 
			
		||||
 | 
			
		||||
def slots_to_m(startn, stopn):
 | 
			
		||||
    """ converts the start and stop n values to the center n and m value
 | 
			
		||||
    """
 | 
			
		||||
    nvalue = (int)((startn+stopn+1)/2)
 | 
			
		||||
    mvalue = (int)((stopn-startn+1)/2)
 | 
			
		||||
    return nvalue, mvalue
 | 
			
		||||
 | 
			
		||||
def m_to_freq(nvalue, mvalue, grid=0.00625e12):
 | 
			
		||||
    """ converts m into frequency range
 | 
			
		||||
    """
 | 
			
		||||
    startn, stopn = mvalue_to_slots(nvalue, mvalue)
 | 
			
		||||
    fstart = nvalue_to_frequency(startn, grid)
 | 
			
		||||
    fstop = nvalue_to_frequency(stopn+1, grid)
 | 
			
		||||
    return fstart, fstop
 | 
			
		||||
 | 
			
		||||
def align_grids(oms_list):
 | 
			
		||||
    """ used to apply same grid to all oms : same starting n, stop n and slot size
 | 
			
		||||
        out of grid slots are set to 0
 | 
			
		||||
    """
 | 
			
		||||
    n_min = min([o.spectrum_bitmap.n_min for o in oms_list])
 | 
			
		||||
    n_max = max([o.spectrum_bitmap.n_max for o in oms_list])
 | 
			
		||||
    for this_o in oms_list:
 | 
			
		||||
        if (this_o.spectrum_bitmap.n_min - n_min) > 0:
 | 
			
		||||
            this_o.spectrum_bitmap.insert_left([0] * (this_o.spectrum_bitmap.n_min - n_min))
 | 
			
		||||
        if (n_max - this_o.spectrum_bitmap.n_max) > 0:
 | 
			
		||||
            this_o.spectrum_bitmap.insert_right([0] * (n_max - this_o.spectrum_bitmap.n_max))
 | 
			
		||||
    return oms_list
 | 
			
		||||
 | 
			
		||||
def build_oms_list(network, equipment):
 | 
			
		||||
    """ initialization of OMS list in the network
 | 
			
		||||
        an oms is build reading all intermediate nodes between two adjacent ROADMs
 | 
			
		||||
        each element within the list is being added an oms and oms_id to record the
 | 
			
		||||
        oms it belongs to.
 | 
			
		||||
        the function supports different spectrum width and supposes that the whole network
 | 
			
		||||
        works with the min range among OMSs
 | 
			
		||||
    """
 | 
			
		||||
    oms_id = 0
 | 
			
		||||
    oms_list = []
 | 
			
		||||
    for node in [n for n in network.nodes() if isinstance(n, Roadm)]:
 | 
			
		||||
        for edge in network.edges([node]):
 | 
			
		||||
            if not isinstance(edge[1], Transceiver):
 | 
			
		||||
                nd_in = edge[0] # nd_in is a Roadm
 | 
			
		||||
                try:
 | 
			
		||||
                    nd_in.oms_list.append(oms_id)
 | 
			
		||||
                except AttributeError:
 | 
			
		||||
                    nd_in.oms_list = []
 | 
			
		||||
                    nd_in.oms_list.append(oms_id)
 | 
			
		||||
                nd_out = edge[1]
 | 
			
		||||
 | 
			
		||||
                params = {}
 | 
			
		||||
                params['oms_id'] = oms_id
 | 
			
		||||
                params['el_id_list'] = []
 | 
			
		||||
                params['el_list'] = []
 | 
			
		||||
                oms = OMS(**params)
 | 
			
		||||
                oms.add_element(nd_in)
 | 
			
		||||
                while not isinstance(nd_out, Roadm):
 | 
			
		||||
                    oms.add_element(nd_out)
 | 
			
		||||
                    # add an oms_id in the element
 | 
			
		||||
                    nd_out.oms_id = oms_id
 | 
			
		||||
                    nd_out.oms = oms
 | 
			
		||||
                    n_temp = nd_out
 | 
			
		||||
                    nd_out = next(n[1] for n in network.edges([n_temp]) if n[1].uid != nd_in.uid)
 | 
			
		||||
                    nd_in = n_temp
 | 
			
		||||
 | 
			
		||||
                oms.add_element(nd_out)
 | 
			
		||||
                # nd_out is a Roadm
 | 
			
		||||
                try:
 | 
			
		||||
                    nd_out.oms_list.append(oms_id)
 | 
			
		||||
                except AttributeError:
 | 
			
		||||
                    nd_out.oms_list = []
 | 
			
		||||
                    nd_out.oms_list.append(oms_id)
 | 
			
		||||
 | 
			
		||||
                oms.update_spectrum(equipment['SI']['default'].f_min,
 | 
			
		||||
                                    equipment['SI']['default'].f_max, grid=0.00625e12)
 | 
			
		||||
                # oms.assign_spectrum(13,7) gives back (193137500000000.0, 193225000000000.0)
 | 
			
		||||
                # as in the example in the standard
 | 
			
		||||
                # oms.assign_spectrum(13,7)
 | 
			
		||||
 | 
			
		||||
                oms_list.append(oms)
 | 
			
		||||
                oms_id += 1
 | 
			
		||||
    oms_list = align_grids(oms_list)
 | 
			
		||||
    reversed_oms(oms_list)
 | 
			
		||||
    return oms_list
 | 
			
		||||
 | 
			
		||||
def reversed_oms(oms_list):
 | 
			
		||||
    """ identifies reversed OMS
 | 
			
		||||
        only applicable for non parallel OMS
 | 
			
		||||
    """
 | 
			
		||||
    for oms in oms_list:
 | 
			
		||||
        has_reversed = False
 | 
			
		||||
        for this_o in oms_list:
 | 
			
		||||
            if (oms.el_id_list[0] == this_o.el_id_list[-1] and
 | 
			
		||||
                    oms.el_id_list[-1] == this_o.el_id_list[0]):
 | 
			
		||||
                oms.reversed_oms = this_o
 | 
			
		||||
                has_reversed = True
 | 
			
		||||
                break
 | 
			
		||||
        if not has_reversed:
 | 
			
		||||
            oms.reversed_oms = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def bitmap_sum(band1, band2):
 | 
			
		||||
    """ a functions that marks occupied bitmap by 0 if the slot is occupied in band1 or in band2
 | 
			
		||||
    """
 | 
			
		||||
    res = []
 | 
			
		||||
    for i, elem in enumerate(band1):
 | 
			
		||||
        if band2[i] * elem == 0:
 | 
			
		||||
            res.append(0)
 | 
			
		||||
        else:
 | 
			
		||||
            res.append(1)
 | 
			
		||||
    return res
 | 
			
		||||
 | 
			
		||||
def spectrum_selection(pth, oms_list, requested_m, requested_n=None):
 | 
			
		||||
    """ collects spectrum availability and call the select_candidate function
 | 
			
		||||
    # step 1 collects pth spectrum availability
 | 
			
		||||
    # step 2 if n is not None try to assign the spectrum
 | 
			
		||||
    #            if the spectrum is not available then sends back an "error"
 | 
			
		||||
    #        if n is None selects candidate spectrum
 | 
			
		||||
    #            select spectrum that fits the policy ( first fit, random, ABP...)
 | 
			
		||||
    # step3 returns the selection
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # use indexes instead of ITU-T n values
 | 
			
		||||
    path_oms = []
 | 
			
		||||
    for elem in pth:
 | 
			
		||||
        if not isinstance(elem, Roadm) and not isinstance(elem, Transceiver):
 | 
			
		||||
            # only edfa, fused and fibers have oms_id attribute
 | 
			
		||||
            path_oms.append(elem.oms_id)
 | 
			
		||||
    # remove duplicate oms_id, order is not important
 | 
			
		||||
    path_oms = list(set(path_oms))
 | 
			
		||||
    # assuming all oms have same freq index
 | 
			
		||||
    if not path_oms:
 | 
			
		||||
        candidate = (None, None, None)
 | 
			
		||||
        return candidate, path_oms
 | 
			
		||||
    freq_index = oms_list[path_oms[0]].spectrum_bitmap.freq_index
 | 
			
		||||
    freq_index_min = oms_list[path_oms[0]].spectrum_bitmap.freq_index_min
 | 
			
		||||
    freq_index_max = oms_list[path_oms[0]].spectrum_bitmap.freq_index_max
 | 
			
		||||
 | 
			
		||||
    freq_availability = oms_list[path_oms[0]].spectrum_bitmap.bitmap
 | 
			
		||||
    for oms in path_oms[1:]:
 | 
			
		||||
        freq_availability = bitmap_sum(oms_list[oms].spectrum_bitmap.bitmap, freq_availability)
 | 
			
		||||
    if requested_n is None:
 | 
			
		||||
        # avoid slots reserved on the edge 0.15e-12 on both sides -> 24
 | 
			
		||||
        candidates = [(freq_index[i]+requested_m, freq_index[i], freq_index[i]+2*requested_m-1)
 | 
			
		||||
                      for i in range(len(freq_availability))
 | 
			
		||||
                      if freq_availability[i:i+2*requested_m] == [1] * (2*requested_m)
 | 
			
		||||
                      and freq_index[i] >= freq_index_min
 | 
			
		||||
                      and freq_index[i+2*requested_m-1] <= freq_index_max]
 | 
			
		||||
 | 
			
		||||
        candidate = select_candidate(candidates, policy='first_fit')
 | 
			
		||||
    else:
 | 
			
		||||
        i = oms_list[path_oms[0]].spectrum_bitmap.geti(requested_n)
 | 
			
		||||
        # print(f'N {requested_n} i {i}')
 | 
			
		||||
        # print(freq_availability[i-m:i+m] )
 | 
			
		||||
        # print(freq_index[i-m:i+m])
 | 
			
		||||
        if (freq_availability[i-requested_m:i+requested_m] == [1] * (2*requested_m) and
 | 
			
		||||
                freq_index[i-requested_m] >= freq_index_min
 | 
			
		||||
                      and freq_index[i+requested_m-1] <= freq_index_max):
 | 
			
		||||
            # candidate is the triplet center_n, startn and stopn
 | 
			
		||||
            candidate = (requested_n, requested_n-requested_m, requested_n+requested_m-1)
 | 
			
		||||
        else:
 | 
			
		||||
            candidate = (None, None, None)
 | 
			
		||||
        # print("coucou11")
 | 
			
		||||
        # print(candidate)
 | 
			
		||||
    # print(freq_availability[321:321+2*m])
 | 
			
		||||
    # a = [i+321 for i in range(2*m)]
 | 
			
		||||
    # print(a)
 | 
			
		||||
    # print(candidate)
 | 
			
		||||
    return candidate, path_oms
 | 
			
		||||
 | 
			
		||||
def select_candidate(candidates, policy):
 | 
			
		||||
    """ selects a candidate among all available spectrum
 | 
			
		||||
    """
 | 
			
		||||
    if policy == 'first_fit':
 | 
			
		||||
        if candidates:
 | 
			
		||||
            return candidates[0]
 | 
			
		||||
        else:
 | 
			
		||||
            return (None, None, None)
 | 
			
		||||
    else:
 | 
			
		||||
        raise ServiceError('Only first_fit spectrum assignment policy is implemented.')
 | 
			
		||||
 | 
			
		||||
def pth_assign_spectrum(pths, rqs, oms_list, rpths):
 | 
			
		||||
    """ basic first fit assignment
 | 
			
		||||
        if reversed path are provided, means that occupation is bidir
 | 
			
		||||
    """
 | 
			
		||||
    for i, pth in enumerate(pths):
 | 
			
		||||
        # computes the number of channels required
 | 
			
		||||
        try:
 | 
			
		||||
            if rqs[i].blocking_reason:
 | 
			
		||||
                rqs[i].blocked = True
 | 
			
		||||
                rqs[i].N = 0
 | 
			
		||||
                rqs[i].M = 0
 | 
			
		||||
        except AttributeError:
 | 
			
		||||
            nb_wl = ceil(rqs[i].path_bandwidth / rqs[i].bit_rate)
 | 
			
		||||
            # computes the total nb of slots according to requested spacing
 | 
			
		||||
            # TODO : express superchannels
 | 
			
		||||
            # assumes that all channels must be grouped
 | 
			
		||||
            # TODO : enables non contiguous reservation in case of blocking
 | 
			
		||||
            requested_m = ceil(rqs[i].spacing / 0.0125e12) * nb_wl
 | 
			
		||||
            # concatenate all path and reversed path elements to derive slots availability
 | 
			
		||||
            (center_n, startn, stopn), path_oms = spectrum_selection(pth + rpths[i], oms_list, requested_m,
 | 
			
		||||
                                                                     requested_n=None)
 | 
			
		||||
            # checks that requested_m is fitting startm and stopm
 | 
			
		||||
            # if not None, center_n and start, stop frequencies are applicable to all oms of pth
 | 
			
		||||
            # checks that spectrum is not None else indicate blocking reason
 | 
			
		||||
            if center_n is not None:
 | 
			
		||||
                # checks that requested_m is fitting startm and stopm
 | 
			
		||||
                if 2 * requested_m > (stopn - startn + 1):
 | 
			
		||||
                    msg = f'candidate: {(center_n, startn, stopn)} is not consistant ' +\
 | 
			
		||||
                          f'with {requested_m}'
 | 
			
		||||
                    LOGGER.critical(msg)
 | 
			
		||||
                    raise ValueError(msg)
 | 
			
		||||
 | 
			
		||||
                for oms_elem in path_oms:
 | 
			
		||||
                    oms_list[oms_elem].assign_spectrum(center_n, requested_m)
 | 
			
		||||
                    oms_list[oms_elem].add_service(rqs[i].request_id, nb_wl)
 | 
			
		||||
                rqs[i].blocked = False
 | 
			
		||||
                rqs[i].N = center_n
 | 
			
		||||
                rqs[i].M = requested_m
 | 
			
		||||
            else:
 | 
			
		||||
                rqs[i].blocked = True
 | 
			
		||||
                rqs[i].N = 0
 | 
			
		||||
                rqs[i].M = 0
 | 
			
		||||
                rqs[i].blocking_reason = 'NO_SPECTRUM'
 | 
			
		||||
@@ -11,8 +11,8 @@ This module contains utility functions that are used with gnpy.
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
import numpy as np
 | 
			
		||||
from csv import writer
 | 
			
		||||
import numpy as np
 | 
			
		||||
from numpy import pi, cos, sqrt, log10
 | 
			
		||||
from scipy import constants
 | 
			
		||||
 | 
			
		||||
@@ -73,21 +73,19 @@ def c():
 | 
			
		||||
    return constants.c
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def itufs(spacing, startf=191.35, stopf=196.10):
 | 
			
		||||
    """Creates an array of frequencies whose default range is
 | 
			
		||||
    191.35-196.10 THz
 | 
			
		||||
def arrange_frequencies(length, start, stop):
 | 
			
		||||
    """Create an array of frequencies
 | 
			
		||||
 | 
			
		||||
    :param spacing: Frequency spacing in THz
 | 
			
		||||
    :param starf: Start frequency in THz
 | 
			
		||||
    :param stopf: Stop frequency in THz
 | 
			
		||||
    :type spacing: float
 | 
			
		||||
    :type startf: float
 | 
			
		||||
    :type stopf: float
 | 
			
		||||
    :return an array of frequnecies determined by the spacing parameter
 | 
			
		||||
    :param length: number of elements
 | 
			
		||||
    :param star: Start frequency in THz
 | 
			
		||||
    :param stop: Stop frequency in THz
 | 
			
		||||
    :type length: integer
 | 
			
		||||
    :type start: float
 | 
			
		||||
    :type stop: float
 | 
			
		||||
    :return an array of frequencies determined by the spacing parameter
 | 
			
		||||
    :rtype: numpy.ndarray
 | 
			
		||||
    """
 | 
			
		||||
    return np.arange(startf, stopf + spacing / 2, spacing)
 | 
			
		||||
 | 
			
		||||
    return np.linspace(start, stop, length)
 | 
			
		||||
 | 
			
		||||
def h():
 | 
			
		||||
    """
 | 
			
		||||
@@ -185,3 +183,43 @@ def rrc(ffs, baud_rate, alpha):
 | 
			
		||||
    p_inds = np.where(np.logical_and(np.abs(ffs) > 0, np.abs(ffs) < l_lim))
 | 
			
		||||
    hf[p_inds] = 1
 | 
			
		||||
    return sqrt(hf)
 | 
			
		||||
 | 
			
		||||
def merge_amplifier_restrictions(dict1, dict2):
 | 
			
		||||
    """Updates contents of dicts recursively
 | 
			
		||||
 | 
			
		||||
    >>> d1 = {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}}
 | 
			
		||||
    >>> d2 = {'params': {'target_pch_out_db': -20}}
 | 
			
		||||
    >>> merge_amplifier_restrictions(d1, d2)
 | 
			
		||||
    {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}, 'target_pch_out_db': -20}}
 | 
			
		||||
 | 
			
		||||
    >>> d3 = {'params': {'restrictions': {'preamp_variety_list': ['foo'], 'booster_variety_list': ['bar']}}}
 | 
			
		||||
    >>> merge_amplifier_restrictions(d1, d3)
 | 
			
		||||
    {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}}
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    copy_dict1 = dict1.copy()
 | 
			
		||||
    for key in dict2:
 | 
			
		||||
        if key in dict1:
 | 
			
		||||
            if isinstance(dict1[key], dict):
 | 
			
		||||
                copy_dict1[key] = merge_amplifier_restrictions(copy_dict1[key], dict2[key])
 | 
			
		||||
        else:
 | 
			
		||||
            copy_dict1[key] = dict2[key]
 | 
			
		||||
    return copy_dict1
 | 
			
		||||
 | 
			
		||||
def silent_remove(this_list, elem):
 | 
			
		||||
    """Remove matching elements from a list without raising ValueError
 | 
			
		||||
 | 
			
		||||
    >>> li = [0, 1]
 | 
			
		||||
    >>> li = silent_remove(li, 1)
 | 
			
		||||
    >>> li
 | 
			
		||||
    [0]
 | 
			
		||||
    >>> li = silent_remove(li, 1)
 | 
			
		||||
    >>> li
 | 
			
		||||
    [0]
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    try:
 | 
			
		||||
        this_list.remove(elem)
 | 
			
		||||
    except ValueError:
 | 
			
		||||
        pass
 | 
			
		||||
    return this_list
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,7 @@ type’][‘subtype’]=object**
 | 
			
		||||
Every equipment type is defined in JSON root with according name and
 | 
			
		||||
array of parameters as value.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: none
 | 
			
		||||
 | 
			
		||||
    {"Edfa": [...],
 | 
			
		||||
    "Fiber": [...]
 | 
			
		||||
@@ -40,7 +40,7 @@ object contains **"type_variety":”type name”** name:value combination,
 | 
			
		||||
if only one subtype exists **"type_variety"** name is not mandatory and
 | 
			
		||||
it will be marked with **”default”** value.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
    {"Edfa": [{
 | 
			
		||||
                "type_variety": "std_medium_gain",
 | 
			
		||||
@@ -88,7 +88,7 @@ location is in **transmission_main_example.py** folder:
 | 
			
		||||
   gain/noise figure ripple. **"advanced_config_from_json"** value
 | 
			
		||||
   contains filename.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
    "Edfa":[{
 | 
			
		||||
            "type_variety": "high_detail_model_example",
 | 
			
		||||
@@ -104,7 +104,7 @@ location is in **transmission_main_example.py** folder:
 | 
			
		||||
-  Variable gain – with JSON file describing gain figure tilt and gain/noise
 | 
			
		||||
   figure ripple. **”default_edfa_config.json”** as source file.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
    "Edfa":[{
 | 
			
		||||
            "type_variety": "std_medium_gain",
 | 
			
		||||
@@ -122,7 +122,7 @@ location is in **transmission_main_example.py** folder:
 | 
			
		||||
-  Fixed gain – with JSON file describing gain figure tilt and gain/noise
 | 
			
		||||
   figure ripple. **”default_edfa_config.json”** as source file.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
    "Edfa":[{
 | 
			
		||||
            "type_variety": "std_fixed_gain",
 | 
			
		||||
@@ -138,7 +138,7 @@ location is in **transmission_main_example.py** folder:
 | 
			
		||||
- openroadm – with JSON file describing gain figure tilt and gain/noise
 | 
			
		||||
   figure ripple. **”default_edfa_config.json”** as source file. 
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
    "Edfa":[{
 | 
			
		||||
            "type_variety": "low_noise",
 | 
			
		||||
@@ -156,7 +156,7 @@ location is in **transmission_main_example.py** folder:
 | 
			
		||||
 | 
			
		||||
Fiber element with its parameters:
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
    "Fiber":[{
 | 
			
		||||
            "type_variety": "SSMF",
 | 
			
		||||
@@ -165,12 +165,58 @@ Fiber element with its parameters:
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
RamanFiber element
 | 
			
		||||
******************
 | 
			
		||||
 | 
			
		||||
A special variant of the regular ``Fiber`` where the simulation engine accounts for the Raman effect.
 | 
			
		||||
The newly added parameters are nested in the ``raman_efficiency`` dictionary.
 | 
			
		||||
Its shape corresponds to typical properties of silica.
 | 
			
		||||
More details are available from :cite:`curri_merit_2016`.
 | 
			
		||||
 | 
			
		||||
The ``cr`` property is the normailzed Raman efficiency, so it is is (almost) independent of the fiber type, while the coefficient actually giving Raman gain is g_R=C_R/Aeff.
 | 
			
		||||
 | 
			
		||||
The ``frequency_offset`` represents the spectral difference between the pumping photon and the one receiving energy.
 | 
			
		||||
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
    "RamanFiber":[{
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "dispersion": 1.67e-05,
 | 
			
		||||
      "gamma": 0.00127,
 | 
			
		||||
      "raman_efficiency": {
 | 
			
		||||
        "cr":[
 | 
			
		||||
            0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119,
 | 
			
		||||
            0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469,
 | 
			
		||||
            0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826,
 | 
			
		||||
            0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05,
 | 
			
		||||
            7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05,
 | 
			
		||||
            2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05,
 | 
			
		||||
            2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05,
 | 
			
		||||
            1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05,
 | 
			
		||||
            1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07,
 | 
			
		||||
            2E-07, 1E-07
 | 
			
		||||
        ],
 | 
			
		||||
        "frequency_offset":[
 | 
			
		||||
          0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12,
 | 
			
		||||
          7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12,
 | 
			
		||||
          13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12,
 | 
			
		||||
          17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12,
 | 
			
		||||
          22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12,
 | 
			
		||||
          28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12,
 | 
			
		||||
          35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12,
 | 
			
		||||
          41.5e12, 42e12
 | 
			
		||||
        ]
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
1.2.3 Roadm element
 | 
			
		||||
*******************
 | 
			
		||||
 | 
			
		||||
Roadm element with its parameters:
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
      "Roadms":[{
 | 
			
		||||
            "gain_mode_default_loss": 20,
 | 
			
		||||
@@ -184,7 +230,7 @@ Roadm element with its parameters:
 | 
			
		||||
 | 
			
		||||
Spans element with its parameters:
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
    "Spans":[{
 | 
			
		||||
            "power_mode":true,
 | 
			
		||||
@@ -205,7 +251,7 @@ Spans element with its parameters:
 | 
			
		||||
 | 
			
		||||
Spectral information with its parameters:
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
    "SI":[{
 | 
			
		||||
            "f_min": 191.3e12,
 | 
			
		||||
@@ -227,7 +273,9 @@ Spectral information with its parameters:
 | 
			
		||||
Transceiver element with its parameters. **”mode”** can contain multiple
 | 
			
		||||
Transceiver operation formats.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
Note that ``OSNR`` parameter refers to the receiver's minimal OSNR threshold for a given mode.
 | 
			
		||||
 | 
			
		||||
.. code-block:: json-object
 | 
			
		||||
 | 
			
		||||
    "Transceiver":[{
 | 
			
		||||
                    "frequency":{
 | 
			
		||||
@@ -288,7 +336,7 @@ Network description JSON file root consist of three unordered parts:
 | 
			
		||||
 | 
			
		||||
-  connections – contains array of unidirectional connection objects
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: none
 | 
			
		||||
 | 
			
		||||
    {"network_name": "Example Network",
 | 
			
		||||
    "elements": [{...},
 | 
			
		||||
@@ -317,10 +365,10 @@ obligatory.
 | 
			
		||||
 | 
			
		||||
Transceiver element with its parameters.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
    {"uid": "trx Site_A",
 | 
			
		||||
    “metadata": {
 | 
			
		||||
    "metadata": {
 | 
			
		||||
                "location": {
 | 
			
		||||
                            "city": "Site_A",
 | 
			
		||||
                            "region": "",
 | 
			
		||||
@@ -339,7 +387,7 @@ Transceiver element with its parameters.
 | 
			
		||||
ROADM element with its parameters. **“params”** is optional, if not used
 | 
			
		||||
default loss value of 20dB is used.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
    {"uid": "roadm Site_A",
 | 
			
		||||
    "metadata": {
 | 
			
		||||
@@ -363,12 +411,12 @@ default loss value of 20dB is used.
 | 
			
		||||
Fused element with its parameters. **“params”** is optional, if not used
 | 
			
		||||
default loss value of 1dB is used.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
    {"uid": "ingress fused spans in Site_B",
 | 
			
		||||
    "metadata": {
 | 
			
		||||
                "location": {
 | 
			
		||||
                            “city": "Site_B",
 | 
			
		||||
                            "city": "Site_B",
 | 
			
		||||
                            "region": "",
 | 
			
		||||
                            "latitude": 0,
 | 
			
		||||
                            "longitude": 0
 | 
			
		||||
@@ -386,7 +434,7 @@ default loss value of 1dB is used.
 | 
			
		||||
 | 
			
		||||
Fiber element with its parameters.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
    {"uid": "fiber (Site_A \\u2192 Site_B)",
 | 
			
		||||
    "metadata": {
 | 
			
		||||
@@ -406,13 +454,56 @@ Fiber element with its parameters.
 | 
			
		||||
                }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
2.2.5. RamanFiber element
 | 
			
		||||
*************************
 | 
			
		||||
 | 
			
		||||
2.2.5. EDFA element
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "Span1",
 | 
			
		||||
      "type": "RamanFiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "temperature": 283,
 | 
			
		||||
        "raman_pumps": [
 | 
			
		||||
          {
 | 
			
		||||
            "power": 200e-3,
 | 
			
		||||
            "frequency": 205e12,
 | 
			
		||||
            "propagation_direction": "counterprop"
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "power": 206e-3,
 | 
			
		||||
            "frequency": 201e12,
 | 
			
		||||
            "propagation_direction": "counterprop"
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },
 | 
			
		||||
      "params": {
 | 
			
		||||
        "type_variety": "SSMF",
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "att_in": 0,
 | 
			
		||||
        "con_in": 0.5,
 | 
			
		||||
        "con_out": 0.5
 | 
			
		||||
      },
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1,
 | 
			
		||||
          "longitude": 0,
 | 
			
		||||
          "city": null,
 | 
			
		||||
          "region": ""
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
2.2.6. EDFA element
 | 
			
		||||
********************
 | 
			
		||||
 | 
			
		||||
EDFA element with its parameters.
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
    {"uid": "Edfa1",
 | 
			
		||||
    "type": "Edfa",
 | 
			
		||||
@@ -438,8 +529,31 @@ Each unidirectional connection object in connections array consist of
 | 
			
		||||
two unordered **”from_node”** and **”to_node”** name pair with values
 | 
			
		||||
corresponding to element **”uid”**
 | 
			
		||||
 | 
			
		||||
.. code-block::
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
    {"from_node": "roadm Site_C",
 | 
			
		||||
    "to_node": "trx Site_C"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
************************
 | 
			
		||||
3. Simulation Parameters
 | 
			
		||||
************************
 | 
			
		||||
 | 
			
		||||
Additional details of the simulation are controlled via ``sim_params.json``:
 | 
			
		||||
 | 
			
		||||
.. code-block:: json
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    "raman_computed_channels": [1, 18, 37, 56, 75],
 | 
			
		||||
    "raman_parameters": {
 | 
			
		||||
      "flag_raman": true,
 | 
			
		||||
      "space_resolution": 10e3,
 | 
			
		||||
      "tolerance": 1e-8
 | 
			
		||||
    },
 | 
			
		||||
    "nli_parameters": {
 | 
			
		||||
      "nli_method_name": "ggn_spectrally_separated",
 | 
			
		||||
      "wdm_grid_size": 50e9,
 | 
			
		||||
      "dispersion_tolerance": 1,
 | 
			
		||||
      "phase_shift_tollerance": 0.1
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,40 +1,62 @@
 | 
			
		||||
{
 | 
			
		||||
  "paths": [
 | 
			
		||||
  "response": [
 | 
			
		||||
    {
 | 
			
		||||
      "path": {
 | 
			
		||||
        "path-id": null,
 | 
			
		||||
        "path-properties": {
 | 
			
		||||
          "path-metric": [
 | 
			
		||||
            {
 | 
			
		||||
              "metric-type": null,
 | 
			
		||||
              "accumulative-value": null
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "path-srlgs": {
 | 
			
		||||
            "usage": "not used yet",
 | 
			
		||||
            "values": ["not used yet"]
 | 
			
		||||
      "response-id": null,
 | 
			
		||||
      "path-properties": {
 | 
			
		||||
        "path-metric": [
 | 
			
		||||
          {
 | 
			
		||||
            "metric-type": "SNR@bandwidth",
 | 
			
		||||
            "accumulative-value": null
 | 
			
		||||
          },
 | 
			
		||||
          "path-route-objects": [
 | 
			
		||||
            {
 | 
			
		||||
              "path-route-object": {
 | 
			
		||||
                "index": null,
 | 
			
		||||
                "unnumbered-hop": {
 | 
			
		||||
                  "node-id": null,
 | 
			
		||||
                  "link-tp-id": null,
 | 
			
		||||
                  "hop-type": null,
 | 
			
		||||
                  "direction": "not used"
 | 
			
		||||
                },
 | 
			
		||||
                "label-hop": {
 | 
			
		||||
                  "te-label": {
 | 
			
		||||
                    "generic": "not used yet",
 | 
			
		||||
                    "direction": "not used yet"
 | 
			
		||||
                  }
 | 
			
		||||
                }
 | 
			
		||||
          {
 | 
			
		||||
            "metric-type": "SNR@0.1nm",
 | 
			
		||||
            "accumulative-value": null
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "metric-type": "OSNR@bandwidth",
 | 
			
		||||
            "accumulative-value": null
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "metric-type": "OSNR@0.1nm",
 | 
			
		||||
            "accumulative-value": null
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "metric-type": "reference_power",
 | 
			
		||||
            "accumulative-value": null
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "metric-type": "path_bandwidth",
 | 
			
		||||
            "accumulative-value": null
 | 
			
		||||
          }
 | 
			
		||||
        ],
 | 
			
		||||
        "path-route-objects": [
 | 
			
		||||
          {
 | 
			
		||||
            "path-route-object": {
 | 
			
		||||
              "index": 0,
 | 
			
		||||
              "num-unnum-hop": {
 | 
			
		||||
                "node-id": null,
 | 
			
		||||
                "link-tp-id": null
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
        }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "path-route-object": {
 | 
			
		||||
              "index": 1,
 | 
			
		||||
              "transponder": {
 | 
			
		||||
                "transponder-type": null,
 | 
			
		||||
                "transponder-mode": null
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "path-route-object": {
 | 
			
		||||
              "index": 2,
 | 
			
		||||
              "num-unnum-hop": {
 | 
			
		||||
                "node-id": null,
 | 
			
		||||
                "link-tp-id": null
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
    },      
 | 
			
		||||
@@ -1,2 +1,2 @@
 | 
			
		||||
[pytest]
 | 
			
		||||
addopts = -p no:warnings
 | 
			
		||||
addopts = --doctest-modules
 | 
			
		||||
 
 | 
			
		||||
@@ -1,44 +1,14 @@
 | 
			
		||||
alabaster==0.7.12
 | 
			
		||||
appdirs==1.4.3
 | 
			
		||||
atomicwrites==1.2.1
 | 
			
		||||
attrs==18.2.0
 | 
			
		||||
Babel==2.6.0
 | 
			
		||||
black==18.9b0
 | 
			
		||||
certifi==2018.10.15
 | 
			
		||||
chardet==3.0.4
 | 
			
		||||
Click==7.0
 | 
			
		||||
cycler==0.10.0
 | 
			
		||||
decorator==4.3.0
 | 
			
		||||
docutils==0.14
 | 
			
		||||
idna==2.7
 | 
			
		||||
imagesize==1.1.0
 | 
			
		||||
Jinja2==2.10
 | 
			
		||||
kiwisolver==1.0.1
 | 
			
		||||
latexcodec==1.0.5
 | 
			
		||||
MarkupSafe==1.0
 | 
			
		||||
matplotlib==3.0.0
 | 
			
		||||
more-itertools==4.3.0
 | 
			
		||||
networkx==2.2
 | 
			
		||||
numpy==1.15.2
 | 
			
		||||
oset==0.1.3
 | 
			
		||||
packaging==18.0
 | 
			
		||||
pluggy==0.7.1
 | 
			
		||||
py==1.7.0
 | 
			
		||||
pybtex==0.21
 | 
			
		||||
pybtex-docutils==0.2.1
 | 
			
		||||
Pygments==2.2.0
 | 
			
		||||
pyparsing==2.2.2
 | 
			
		||||
pytest==3.8.2
 | 
			
		||||
python-dateutil==2.7.3
 | 
			
		||||
pytz==2018.5
 | 
			
		||||
PyYAML==3.13
 | 
			
		||||
requests==2.19.1
 | 
			
		||||
scipy==1.1.0
 | 
			
		||||
six==1.11.0
 | 
			
		||||
snowballstemmer==1.2.1
 | 
			
		||||
Sphinx==1.8.1
 | 
			
		||||
sphinxcontrib-bibtex==0.4.0
 | 
			
		||||
sphinxcontrib-websupport==1.1.0
 | 
			
		||||
toml==0.10.0
 | 
			
		||||
urllib3==1.23
 | 
			
		||||
xlrd==1.1.0
 | 
			
		||||
alabaster>=0.7.12,<1
 | 
			
		||||
docutils==0.15.2
 | 
			
		||||
flask==1.0.2
 | 
			
		||||
flask-restful==0.3.7
 | 
			
		||||
matplotlib>=3.1.0,<4
 | 
			
		||||
networkx>=2.3,<3
 | 
			
		||||
numpy>=1.16.1,<2
 | 
			
		||||
pandas==0.24.2
 | 
			
		||||
Pygments>=2.4.2,<3
 | 
			
		||||
pytest>=4.0.0,<5
 | 
			
		||||
scipy>=1.3.0,<2
 | 
			
		||||
Sphinx>=2.1.1,<3
 | 
			
		||||
sphinxcontrib-bibtex>=0.4.2,<1
 | 
			
		||||
xlrd>=1.2.0,<2
 | 
			
		||||
 
 | 
			
		||||
@@ -6,55 +6,72 @@
 | 
			
		||||
      "destination": null,
 | 
			
		||||
      "src-tp-id": null,
 | 
			
		||||
      "dst-tp-id": null,
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": null,
 | 
			
		||||
            "index": null,
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": null,
 | 
			
		||||
              "link-tp-id": null,
 | 
			
		||||
              "hop-type": null
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": null,
 | 
			
		||||
            "index": null,
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "N": null,
 | 
			
		||||
              "M": null
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": null,
 | 
			
		||||
            "index": null,
 | 
			
		||||
            "transponder": {
 | 
			
		||||
              "transponder-type": null,
 | 
			
		||||
              "transponder-mode": null
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": null,
 | 
			
		||||
            "index": null,
 | 
			
		||||
            "regenerator": {
 | 
			
		||||
              "regenerator-id": null,
 | 
			
		||||
              "transponder-type": null,
 | 
			
		||||
              "transponder-mode": null
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      },      
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": null,
 | 
			
		||||
          "trx_mode": null,
 | 
			
		||||
          "trx_type": "name of the tsp type_variety as listed in the library",
 | 
			
		||||
          "trx_mode": "optional, name of the mode as listed in the tsp type_variety",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": null,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": null
 | 
			
		||||
          }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": {
 | 
			
		||||
          "route-object-include-object": [
 | 
			
		||||
          {
 | 
			
		||||
            "index": null,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
              "node-id": null,
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": null,
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          ]
 | 
			
		||||
          "spacing": mandatory decimal Hz,
 | 
			
		||||
          "max-nb-of-channel": optional integer,
 | 
			
		||||
          "output-power": optional decimal W,
 | 
			
		||||
          "path_bandwidth": optional bit/s
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }],
 | 
			
		||||
  "synchronization": [
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "synchronization": [   list of disjunctions, optional
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": null,
 | 
			
		||||
      "synchronization-id": "3",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "True",
 | 
			
		||||
        "link-diverse": "False",
 | 
			
		||||
        "node-diverse": "False",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
         null ]
 | 
			
		||||
         null, null ]
 | 
			
		||||
        },
 | 
			
		||||
    }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
  ]  
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								setup.py
									
									
									
									
									
								
							@@ -11,13 +11,13 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
 | 
			
		||||
 | 
			
		||||
setup(
 | 
			
		||||
    name='gnpy',
 | 
			
		||||
    version='1.2.0',
 | 
			
		||||
    version='2.1',
 | 
			
		||||
    description='route planning and optimization tool for mesh optical networks',
 | 
			
		||||
    long_description=long_description,
 | 
			
		||||
    long_description_content_type='text/x-rst; charset=UTF-8',
 | 
			
		||||
    url='https://github.com/Telecominfraproject/gnpy',
 | 
			
		||||
    author='Telecom Infra Project',
 | 
			
		||||
    author_email='james.powell@telecominfraproject.com',
 | 
			
		||||
    author_email='jan.kundrat@telecominfraproject.com',
 | 
			
		||||
    classifiers=[
 | 
			
		||||
        'Development Status :: 3 - Alpha',
 | 
			
		||||
        'Intended Audience :: Developers',
 | 
			
		||||
 
 | 
			
		||||
@@ -205,6 +205,36 @@
 | 
			
		||||
                    "longitude": 0
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
        "uid": "Att_B",
 | 
			
		||||
        "type": "Fused",
 | 
			
		||||
        "params":{
 | 
			
		||||
            "loss":16
 | 
			
		||||
        },
 | 
			
		||||
        "metadata": {
 | 
			
		||||
            "location": {
 | 
			
		||||
              "latitude": 2.0,
 | 
			
		||||
              "longitude": 1.0,
 | 
			
		||||
              "city": "Corlay",
 | 
			
		||||
              "region": "RLD"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
        "uid": "Att_F",
 | 
			
		||||
        "type": "Fused",
 | 
			
		||||
        "params":{
 | 
			
		||||
            "loss":16
 | 
			
		||||
        },
 | 
			
		||||
        "metadata": {
 | 
			
		||||
            "location": {
 | 
			
		||||
              "latitude": 2.0,
 | 
			
		||||
              "longitude": 1.0,
 | 
			
		||||
              "city": "Corlay",
 | 
			
		||||
              "region": "RLD"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
  ],
 | 
			
		||||
@@ -247,6 +277,10 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Edfa5",
 | 
			
		||||
      "to_node": "Att_F"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Att_F",
 | 
			
		||||
      "to_node": "trx F"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
@@ -255,6 +289,10 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Edfa1",
 | 
			
		||||
      "to_node": "Att_B"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "Att_B",
 | 
			
		||||
      "to_node": "trx B"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
 
 | 
			
		||||
@@ -77,8 +77,11 @@ def compare_networks(expected, actual):
 | 
			
		||||
def compare_services(expected, actual):
 | 
			
		||||
    requests = compare(expected['path-request'], actual['path-request'],
 | 
			
		||||
                       key=lambda el: el['request-id'])
 | 
			
		||||
    synchronizations = compare(expected['synchronization'], actual['synchronization'],
 | 
			
		||||
                               key=lambda el: el['synchronization-id'])
 | 
			
		||||
    synchronizations = compare(expected['path-request'], expected['path-request'],
 | 
			
		||||
                               key=lambda el: el['request-id'])
 | 
			
		||||
    if 'synchronization' in expected.keys():
 | 
			
		||||
        synchronizations = compare(expected['synchronization'], actual['synchronization'],
 | 
			
		||||
                                   key=lambda el: el['synchronization-id'])
 | 
			
		||||
    return ServicesResults(requests, synchronizations)
 | 
			
		||||
 | 
			
		||||
def compare_paths(expected_output, actual_output):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										196470
									
								
								tests/data/CORONET_Global_Topology_auto_design_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196470
									
								
								tests/data/CORONET_Global_Topology_auto_design_expected.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,9 +1,11 @@
 | 
			
		||||
{     "Edfa":[{
 | 
			
		||||
            "type_variety": "CienaDB_medium_gain",
 | 
			
		||||
            "type_def": "advanced_model",
 | 
			
		||||
            "gain_flatmax": 25,
 | 
			
		||||
            "gain_min": 15,
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "advanced_config_from_json": "std_medium_gain_advanced_config.json",
 | 
			
		||||
            "out_voa_auto": false,
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@@ -14,6 +16,7 @@
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf_min": 6,
 | 
			
		||||
            "nf_max": 10,
 | 
			
		||||
            "out_voa_auto": false,
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@@ -24,6 +27,7 @@
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf_min": 7,
 | 
			
		||||
            "nf_max": 11,
 | 
			
		||||
            "out_voa_auto": false,
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@@ -34,6 +38,7 @@
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf_min": 5.8,
 | 
			
		||||
            "nf_max": 10,
 | 
			
		||||
            "out_voa_auto": false,
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@@ -44,7 +49,16 @@
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf0": 5,
 | 
			
		||||
            "allowed_for_design": true
 | 
			
		||||
            }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
            "type_variety": "std_booster",
 | 
			
		||||
            "type_def": "fixed_gain",
 | 
			
		||||
            "gain_flatmax": 21,
 | 
			
		||||
            "gain_min": 20,
 | 
			
		||||
            "p_max": 21,
 | 
			
		||||
            "nf0": 5,
 | 
			
		||||
            "allowed_for_design": false
 | 
			
		||||
            }            
 | 
			
		||||
      ],
 | 
			
		||||
      "Fiber":[{
 | 
			
		||||
            "type_variety": "SSMF",
 | 
			
		||||
@@ -52,9 +66,11 @@
 | 
			
		||||
            "gamma": 0.00127
 | 
			
		||||
            }
 | 
			
		||||
      ],
 | 
			
		||||
      "Spans":[{
 | 
			
		||||
            "power_mode": true,
 | 
			
		||||
            "delta_power_range_db": [0,0,1],
 | 
			
		||||
      "Span":[{
 | 
			
		||||
            "power_mode":true,
 | 
			
		||||
            "delta_power_range_db": [0,0,0.5],
 | 
			
		||||
            "max_fiber_lineic_loss_for_raman": 0.25,
 | 
			
		||||
            "target_extended_gain": 2.5,
 | 
			
		||||
            "max_length": 150,
 | 
			
		||||
            "length_units": "km",
 | 
			
		||||
            "max_loss": 28,
 | 
			
		||||
@@ -64,10 +80,13 @@
 | 
			
		||||
            "con_out": 0
 | 
			
		||||
            }
 | 
			
		||||
      ],
 | 
			
		||||
      "Roadms":[{
 | 
			
		||||
            "gain_mode_default_loss": 20,
 | 
			
		||||
            "power_mode_pout_target": -20,
 | 
			
		||||
            "add_drop_osnr": 100
 | 
			
		||||
      "Roadm":[{
 | 
			
		||||
            "target_pch_out_db": -20,
 | 
			
		||||
            "add_drop_osnr": 38,
 | 
			
		||||
            "restrictions": {
 | 
			
		||||
                            "preamp_variety_list":[],
 | 
			
		||||
                            "booster_variety_list":[]
 | 
			
		||||
                            }    
 | 
			
		||||
            }],
 | 
			
		||||
      "SI":[{
 | 
			
		||||
            "f_min": 191.3e12,
 | 
			
		||||
@@ -75,7 +94,7 @@
 | 
			
		||||
            "baud_rate": 32e9,
 | 
			
		||||
            "spacing": 50e9,
 | 
			
		||||
            "power_dbm": 0,
 | 
			
		||||
            "power_range_db": [0,0.5,0.5],
 | 
			
		||||
            "power_range_db": [0,0,0.5],
 | 
			
		||||
            "roll_off": 0.15,
 | 
			
		||||
            "tx_osnr": 100,
 | 
			
		||||
            "sys_margins": 0            
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -1,698 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "elements": [
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Lannion_CAS",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Vannes_KBE",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Vannes_KBE",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Rennes_STA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Rennes_STA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Brest_KLA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Brest_KLA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Lannion_CAS",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Vannes_KBE",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Vannes_KBE",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Rennes_STA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Rennes_STA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Brest_KLA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Brest_KLA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west fused spans in Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Corlay",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west fused spans in Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Loudeac",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east fused spans in Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Corlay",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east fused spans in Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Loudeac",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lannion_CAS → Corlay)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 20.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Corlay → Loudeac)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 50.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Loudeac → Lorient_KMA)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lorient_KMA → Vannes_KBE)-F01",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 10.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lannion_CAS → Stbrieuc)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Stbrieuc → Rennes_STA)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 65.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lannion_CAS → Morlaix)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 40.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Morlaix → Brest_KLA)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 35.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Corlay → Lannion_CAS)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 20.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Loudeac → Corlay)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 50.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lorient_KMA → Loudeac)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Vannes_KBE → Lorient_KMA)-F01",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 10.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Stbrieuc → Lannion_CAS)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Rennes_STA → Stbrieuc)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 65.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Morlaix → Lannion_CAS)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 40.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Brest_KLA → Morlaix)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 35.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lannion_CAS to Morlaix",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "test",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lannion_CAS to Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "test",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lannion_CAS to Stbrieuc",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "test",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Corlay to Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Corlay",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "test",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "connections": [
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "east edfa in Lannion_CAS to Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lannion_CAS to Corlay",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Corlay)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Corlay → Lannion_CAS)-",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "east edfa in Lannion_CAS to Stbrieuc"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lannion_CAS to Stbrieuc",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Stbrieuc)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Stbrieuc → Lannion_CAS)-",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "east edfa in Lannion_CAS to Morlaix"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lannion_CAS to Morlaix",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Morlaix)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Morlaix → Lannion_CAS)-",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Corlay)-",
 | 
			
		||||
      "to_node": "west fused spans in Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west fused spans in Corlay",
 | 
			
		||||
      "to_node": "fiber (Corlay → Loudeac)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Loudeac → Corlay)-",
 | 
			
		||||
      "to_node": "east fused spans in Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east fused spans in Corlay",
 | 
			
		||||
      "to_node": "fiber (Corlay → Lannion_CAS)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Corlay → Loudeac)-",
 | 
			
		||||
      "to_node": "west fused spans in Loudeac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west fused spans in Loudeac",
 | 
			
		||||
      "to_node": "fiber (Loudeac → Lorient_KMA)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lorient_KMA → Loudeac)-",
 | 
			
		||||
      "to_node": "east fused spans in Loudeac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east fused spans in Loudeac",
 | 
			
		||||
      "to_node": "fiber (Loudeac → Corlay)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "fiber (Lorient_KMA → Loudeac)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Loudeac → Lorient_KMA)-",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F01"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F01",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Vannes_KBE",
 | 
			
		||||
      "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F01"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F01",
 | 
			
		||||
      "to_node": "roadm Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Stbrieuc)-",
 | 
			
		||||
      "to_node": "fiber (Stbrieuc → Rennes_STA)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Rennes_STA → Stbrieuc)-",
 | 
			
		||||
      "to_node": "fiber (Stbrieuc → Lannion_CAS)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Rennes_STA",
 | 
			
		||||
      "to_node": "fiber (Rennes_STA → Stbrieuc)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Stbrieuc → Rennes_STA)-",
 | 
			
		||||
      "to_node": "roadm Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Morlaix)-",
 | 
			
		||||
      "to_node": "fiber (Morlaix → Brest_KLA)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Brest_KLA → Morlaix)-",
 | 
			
		||||
      "to_node": "fiber (Morlaix → Lannion_CAS)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Brest_KLA",
 | 
			
		||||
      "to_node": "fiber (Brest_KLA → Morlaix)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Morlaix → Brest_KLA)-",
 | 
			
		||||
      "to_node": "roadm Brest_KLA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Lannion_CAS",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "trx Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Lorient_KMA",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "trx Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Vannes_KBE",
 | 
			
		||||
      "to_node": "roadm Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Vannes_KBE",
 | 
			
		||||
      "to_node": "trx Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Rennes_STA",
 | 
			
		||||
      "to_node": "roadm Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Rennes_STA",
 | 
			
		||||
      "to_node": "trx Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Brest_KLA",
 | 
			
		||||
      "to_node": "roadm Brest_KLA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Brest_KLA",
 | 
			
		||||
      "to_node": "trx Brest_KLA"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,84 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "path-request": [
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "0",
 | 
			
		||||
      "source": "BREST_KLA",
 | 
			
		||||
      "destination": "Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx BREST_KLA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager_16QAM",
 | 
			
		||||
          "trx_mode": "16QAM",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "1",
 | 
			
		||||
      "source": "Lorient_KMA",
 | 
			
		||||
      "destination": "Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager_16QAM",
 | 
			
		||||
          "trx_mode": "16QAM",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "synchronization": [
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "0",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "0",
 | 
			
		||||
          "1"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "1",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "1",
 | 
			
		||||
          "0"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										97
									
								
								tests/data/expected_results_science_utils.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								tests/data/expected_results_science_utils.csv
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
,signal,ase,nli
 | 
			
		||||
0,0.0002869472910750076,3.829243751386179e-08,2.157043502374111e-07
 | 
			
		||||
1,0.000284426444181902,3.8108068606265256e-08,2.1799950841472648e-07
 | 
			
		||||
2,0.0002819286625240274,3.7925434667811625e-08,2.2023841125044652e-07
 | 
			
		||||
3,0.0002794537215642205,3.774451238936698e-08,2.224218994135113e-07
 | 
			
		||||
4,0.0002756243295734432,3.739256063612741e-08,2.2343448272114653e-07
 | 
			
		||||
5,0.0002718482755003954,3.7044477620123535e-08,2.2437826192962217e-07
 | 
			
		||||
6,0.0002681247979313455,3.6700201831013766e-08,2.2525495466695055e-07
 | 
			
		||||
7,0.0002644507001383656,3.635953568122817e-08,2.2606415187870565e-07
 | 
			
		||||
8,0.0002608253488031495,3.602242321653821e-08,2.268074852150968e-07
 | 
			
		||||
9,0.00025690468888571607,3.564391587795796e-08,2.2718285844824803e-07
 | 
			
		||||
10,0.0002530414048173237,3.5269661038482016e-08,2.2749429758476786e-07
 | 
			
		||||
11,0.0002492279873568786,3.4899736994459975e-08,2.277374766526846e-07
 | 
			
		||||
12,0.0002454639458992114,3.4534068616323406e-08,2.2791414400784552e-07
 | 
			
		||||
13,0.00024174879168999762,3.417258192135115e-08,2.280260208417629e-07
 | 
			
		||||
14,0.00023798746912556782,3.3802278288721e-08,2.2798420759779948e-07
 | 
			
		||||
15,0.00023427697848575827,3.3436265380528345e-08,2.2788101592690985e-07
 | 
			
		||||
16,0.00023061678363205047,3.30744682841412e-08,2.2771816297652923e-07
 | 
			
		||||
17,0.00022700656967542085,3.271682680678683e-08,2.2749755602884014e-07
 | 
			
		||||
18,0.0002234457948096593,3.236326805537296e-08,2.236182244259085e-07
 | 
			
		||||
19,0.0002195336193536736,3.195819496314336e-08,2.193976173454328e-07
 | 
			
		||||
20,0.00021568313139087874,3.155821230359698e-08,2.1524945887103656e-07
 | 
			
		||||
21,0.00021189361260563733,3.116322489050993e-08,2.1117277567390236e-07
 | 
			
		||||
22,0.00020816423698459606,3.0773141693336075e-08,2.0716649124094935e-07
 | 
			
		||||
23,0.0002044941867087381,3.038787321635763e-08,2.032295417993187e-07
 | 
			
		||||
24,0.00020116081520673765,3.00440338127331e-08,1.9963693210324778e-07
 | 
			
		||||
25,0.00019787569461895006,2.9704199888387147e-08,1.9610141536963145e-07
 | 
			
		||||
26,0.00019463824873065924,2.9368302916351224e-08,1.9262221997372471e-07
 | 
			
		||||
27,0.0001914486066928752,2.903632427420397e-08,1.8919927457565086e-07
 | 
			
		||||
28,0.00018830616497930887,2.870819640079397e-08,1.858317840670677e-07
 | 
			
		||||
29,0.00018521032563368435,2.838385281897912e-08,1.8251896218718178e-07
 | 
			
		||||
30,0.00018216049720979434,2.8063228018898468e-08,1.7926003240909075e-07
 | 
			
		||||
31,0.0001791561867005718,2.7746255438682553e-08,1.76054318231933e-07
 | 
			
		||||
32,0.00017619680881744213,2.7432871709278503e-08,1.7290105534292413e-07
 | 
			
		||||
33,0.00017328178390236163,2.7123014438128492e-08,1.6979948820364567e-07
 | 
			
		||||
34,0.00017049664136784971,2.6828118382010868e-08,1.668331233176527e-07
 | 
			
		||||
35,0.0001677518922618999,2.6536524600591003e-08,1.639139770351797e-07
 | 
			
		||||
36,0.00016504703499520338,2.6248178236430935e-08,1.6104139135571758e-07
 | 
			
		||||
37,0.0001623826677977635,2.596311344676757e-08,1.579538179464147e-07
 | 
			
		||||
38,0.0001597582427278653,2.5681275450827438e-08,1.549209871570718e-07
 | 
			
		||||
39,0.0001571732182028194,2.5402610321183817e-08,1.5194201541886346e-07
 | 
			
		||||
40,0.00015462705891566638,2.512706495768609e-08,1.490160317195833e-07
 | 
			
		||||
41,0.00015212101646392648,2.4854546722771583e-08,1.4614388817377845e-07
 | 
			
		||||
42,0.00014965447757986727,2.4585006051161647e-08,1.4332463586636234e-07
 | 
			
		||||
43,0.00014722683809507942,2.4318394065447274e-08,1.4055734193947907e-07
 | 
			
		||||
44,0.0001447164668892396,2.4034548127308286e-08,1.3772590008270512e-07
 | 
			
		||||
45,0.00014224784112375704,2.3753926686114635e-08,1.3494914625939818e-07
 | 
			
		||||
46,0.00013982028367499942,2.3476475779461364e-08,1.3222606385780792e-07
 | 
			
		||||
47,0.00013743418748445304,2.3202244204140228e-08,1.2955665313419502e-07
 | 
			
		||||
48,0.00013508884015386575,2.2931178307200807e-08,1.269398709602497e-07
 | 
			
		||||
49,0.00013278354172499636,2.2663225269637508e-08,1.243746944213211e-07
 | 
			
		||||
50,0.0001305176041972383,2.2398333101097452e-08,1.2186012017916144e-07
 | 
			
		||||
51,0.00012829168984639723,2.2136419884279648e-08,1.1939640981690787e-07
 | 
			
		||||
52,0.00012610506317956035,2.1877436733290284e-08,1.169825203056231e-07
 | 
			
		||||
53,0.000123957002859191,2.1621335420785434e-08,1.1461743054419468e-07
 | 
			
		||||
54,0.00012180241033649304,2.1360152817604167e-08,1.1225922783038433e-07
 | 
			
		||||
55,0.00011968650905779935,2.1101906890578305e-08,1.0994951537259513e-07
 | 
			
		||||
56,0.000117608577762061,2.0846548870078847e-08,1.0757395097864581e-07
 | 
			
		||||
57,0.00011556891128259058,2.0594151467353748e-08,1.0524972555992308e-07
 | 
			
		||||
58,0.00011356676177301841,2.0344667169015006e-08,1.0297570549831857e-07
 | 
			
		||||
59,0.00011160139690545192,2.00980493433389e-08,1.0075078305548045e-07
 | 
			
		||||
60,0.00010967209909252646,1.985425227516509e-08,9.857387536569511e-08
 | 
			
		||||
61,0.00010777915187087522,1.9613208260272527e-08,9.644480679616336e-08
 | 
			
		||||
62,0.00010592181397175155,1.937487453011716e-08,9.436248424611683e-08
 | 
			
		||||
63,0.00010409936038610526,1.913920913597429e-08,9.23258408012148e-08
 | 
			
		||||
64,0.00010246447558375888,1.8936226281729442e-08,9.046927135291653e-08
 | 
			
		||||
65,0.00010085803630104006,1.87354387522902e-08,8.865067925960373e-08
 | 
			
		||||
66,9.927950010553608e-05,1.853681852284204e-08,8.686925127146881e-08
 | 
			
		||||
67,9.772837346090978e-05,1.834034443508121e-08,8.512422533827548e-08
 | 
			
		||||
68,9.620413430112097e-05,1.8145990199784238e-08,8.341482250639003e-08
 | 
			
		||||
69,9.470627135913274e-05,1.795373041706864e-08,8.174028142913882e-08
 | 
			
		||||
70,9.323428359797426e-05,1.776354066998682e-08,8.009985766376296e-08
 | 
			
		||||
71,9.178813743816942e-05,1.7575386852678668e-08,7.849321446941785e-08
 | 
			
		||||
72,9.03673300948529e-05,1.7389247191220127e-08,7.691961625609547e-08
 | 
			
		||||
73,8.897136946427622e-05,1.7205101122769978e-08,7.537834446342857e-08
 | 
			
		||||
74,8.760740745800998e-05,1.7025337039390582e-08,7.387513417420477e-08
 | 
			
		||||
75,8.626710469266086e-05,1.684760610568072e-08,7.274492099363918e-08
 | 
			
		||||
76,8.495000573672162e-05,1.6671894857242002e-08,7.163427447510873e-08
 | 
			
		||||
77,8.365569697520994e-05,1.649819993412593e-08,7.054284583689279e-08
 | 
			
		||||
78,8.238374036674246e-05,1.6326513144182658e-08,6.947026569965565e-08
 | 
			
		||||
79,8.113370706498376e-05,1.6156829499842502e-08,6.841617243780552e-08
 | 
			
		||||
80,7.990517700269747e-05,1.5989147949913657e-08,6.738021182874466e-08
 | 
			
		||||
81,7.86978423091888e-05,1.5823469853370494e-08,6.636212425984957e-08
 | 
			
		||||
82,7.751129541079691e-05,1.5659805288834794e-08,6.536156604375694e-08
 | 
			
		||||
83,7.634513730458643e-05,1.549817228640182e-08,6.4378200720386e-08
 | 
			
		||||
84,7.530262080974352e-05,1.5364274253504764e-08,6.349909645089537e-08
 | 
			
		||||
85,7.427675504203847e-05,1.523236211656126e-08,6.263403294276386e-08
 | 
			
		||||
86,7.326723873728748e-05,1.5102509684796054e-08,6.17827561543225e-08
 | 
			
		||||
87,7.227232864621635e-05,1.497407531211962e-08,6.094379608688325e-08
 | 
			
		||||
88,7.129179755315639e-05,1.4847053209180731e-08,6.011696114034632e-08
 | 
			
		||||
89,7.032542203609286e-05,1.4721438007057792e-08,5.930206291361871e-08
 | 
			
		||||
90,6.937298231674387e-05,1.4597224779058979e-08,5.8498916078193026e-08
 | 
			
		||||
91,6.843339696762452e-05,1.4474430063551042e-08,5.7706608718023995e-08
 | 
			
		||||
92,6.750649045006184e-05,1.435304906112738e-08,5.692499280974924e-08
 | 
			
		||||
93,6.659208967850971e-05,1.4233077472549144e-08,5.615392239861094e-08
 | 
			
		||||
94,6.554258932109723e-05,1.4075047005202515e-08,5.5268928972034715e-08
 | 
			
		||||
95,6.450957734109015e-05,1.3918652473373596e-08,5.439783940505763e-08
 | 
			
		||||
		
		
			
  | 
										
											Binary file not shown.
										
									
								
							@@ -1,806 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "elements": [
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Lannion_CAS",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Vannes_KBE",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Vannes_KBE",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 4.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Rennes_STA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Rennes_STA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Brest_KLA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Brest_KLA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 4.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx toto",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "toto",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 6.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx tata",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "tata",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 7.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Lannion_CAS",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Vannes_KBE",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Vannes_KBE",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 4.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Rennes_STA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Rennes_STA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Brest_KLA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Brest_KLA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 4.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm toto",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "toto",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 6.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm tata",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "tata",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 7.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west fused spans in Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Corlay",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west fused spans in Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Loudeac",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west fused spans in Morlaix",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Morlaix",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 3.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east fused spans in Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Corlay",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east fused spans in Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Loudeac",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east fused spans in Morlaix",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Morlaix",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 3.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lannion_CAS → Corlay)-F061",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 20.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Corlay → Loudeac)-F010",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 50.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Loudeac → Lorient_KMA)-F054",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lorient_KMA → Vannes_KBE)-F055",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 10.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lannion_CAS → Stbrieuc)-F056",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Stbrieuc → Rennes_STA)-F057",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 65.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lannion_CAS → Morlaix)-F059",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 40.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Morlaix → Brest_KLA)-F060",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 3.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 35.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (toto → tata)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 6.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Corlay → Lannion_CAS)-F061",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 20.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Loudeac → Corlay)-F010",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 50.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lorient_KMA → Loudeac)-F054",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Vannes_KBE → Lorient_KMA)-F055",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 10.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Stbrieuc → Lannion_CAS)-F056",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Rennes_STA → Stbrieuc)-F057",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 65.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Morlaix → Lannion_CAS)-F059",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 40.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Brest_KLA → Morlaix)-F060",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 3.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 35.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (tata → toto)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 6.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lannion_CAS to Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "test",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east edfa in Lorient_KMA to Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Edfa",
 | 
			
		||||
      "type_variety": "test",
 | 
			
		||||
      "operational": {
 | 
			
		||||
        "gain_target": 0,
 | 
			
		||||
        "tilt_target": 0,
 | 
			
		||||
        "out_voa": 0
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "connections": [
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "east edfa in Lannion_CAS to Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lannion_CAS to Corlay",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Corlay)-F061"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Corlay → Lannion_CAS)-F061",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Stbrieuc → Lannion_CAS)-F056",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Morlaix)-F059"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Morlaix → Lannion_CAS)-F059",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Corlay)-F061",
 | 
			
		||||
      "to_node": "west fused spans in Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west fused spans in Corlay",
 | 
			
		||||
      "to_node": "fiber (Corlay → Loudeac)-F010"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Loudeac → Corlay)-F010",
 | 
			
		||||
      "to_node": "east fused spans in Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east fused spans in Corlay",
 | 
			
		||||
      "to_node": "fiber (Corlay → Lannion_CAS)-F061"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Corlay → Loudeac)-F010",
 | 
			
		||||
      "to_node": "west fused spans in Loudeac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west fused spans in Loudeac",
 | 
			
		||||
      "to_node": "fiber (Loudeac → Lorient_KMA)-F054"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lorient_KMA → Loudeac)-F054",
 | 
			
		||||
      "to_node": "east fused spans in Loudeac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east fused spans in Loudeac",
 | 
			
		||||
      "to_node": "fiber (Loudeac → Corlay)-F010"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "east edfa in Lorient_KMA to Loudeac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east edfa in Lorient_KMA to Loudeac",
 | 
			
		||||
      "to_node": "fiber (Lorient_KMA → Loudeac)-F054"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Loudeac → Lorient_KMA)-F054",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Vannes_KBE",
 | 
			
		||||
      "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055",
 | 
			
		||||
      "to_node": "roadm Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056",
 | 
			
		||||
      "to_node": "fiber (Stbrieuc → Rennes_STA)-F057"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Rennes_STA → Stbrieuc)-F057",
 | 
			
		||||
      "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Rennes_STA",
 | 
			
		||||
      "to_node": "fiber (Rennes_STA → Stbrieuc)-F057"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Stbrieuc → Rennes_STA)-F057",
 | 
			
		||||
      "to_node": "roadm Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Morlaix)-F059",
 | 
			
		||||
      "to_node": "west fused spans in Morlaix"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west fused spans in Morlaix",
 | 
			
		||||
      "to_node": "fiber (Morlaix → Brest_KLA)-F060"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Brest_KLA → Morlaix)-F060",
 | 
			
		||||
      "to_node": "east fused spans in Morlaix"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east fused spans in Morlaix",
 | 
			
		||||
      "to_node": "fiber (Morlaix → Lannion_CAS)-F059"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Brest_KLA",
 | 
			
		||||
      "to_node": "fiber (Brest_KLA → Morlaix)-F060"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Morlaix → Brest_KLA)-F060",
 | 
			
		||||
      "to_node": "roadm Brest_KLA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm toto",
 | 
			
		||||
      "to_node": "fiber (toto → tata)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (tata → toto)-",
 | 
			
		||||
      "to_node": "roadm toto"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm tata",
 | 
			
		||||
      "to_node": "fiber (tata → toto)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (toto → tata)-",
 | 
			
		||||
      "to_node": "roadm tata"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Lannion_CAS",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "trx Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Lorient_KMA",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "trx Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Vannes_KBE",
 | 
			
		||||
      "to_node": "roadm Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Vannes_KBE",
 | 
			
		||||
      "to_node": "trx Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Rennes_STA",
 | 
			
		||||
      "to_node": "roadm Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Rennes_STA",
 | 
			
		||||
      "to_node": "trx Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Brest_KLA",
 | 
			
		||||
      "to_node": "roadm Brest_KLA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Brest_KLA",
 | 
			
		||||
      "to_node": "trx Brest_KLA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx toto",
 | 
			
		||||
      "to_node": "roadm toto"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm toto",
 | 
			
		||||
      "to_node": "trx toto"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx tata",
 | 
			
		||||
      "to_node": "roadm tata"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm tata",
 | 
			
		||||
      "to_node": "trx tata"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,224 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "path-request": [
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "0",
 | 
			
		||||
      "source": "Lorient_KMA",
 | 
			
		||||
      "destination": "Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager_16QAM",
 | 
			
		||||
          "trx_mode": "16QAM",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "1",
 | 
			
		||||
      "source": "Brest_KLA",
 | 
			
		||||
      "destination": "Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Brest_KLA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager_16QAM",
 | 
			
		||||
          "trx_mode": "16QAM",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
          {
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
              "node-id": "Lannion_CAS",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "index": 1,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
              "node-id": "Lorient_KMA",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "3",
 | 
			
		||||
      "source": "Lannion_CAS",
 | 
			
		||||
      "destination": "Rennes_STA",
 | 
			
		||||
      "src-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "dst-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "vendorA_trx-type1",
 | 
			
		||||
          "trx_mode": "PS_SP64_1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "4",
 | 
			
		||||
      "source": "Rennes_STA",
 | 
			
		||||
      "destination": "Lannion_CAS",
 | 
			
		||||
      "src-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "dst-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "vendorA_trx-type1",
 | 
			
		||||
          "trx_mode": "PS_SP64_2",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 75000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 64,
 | 
			
		||||
          "output-power": 0.0019952623149688794,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "5",
 | 
			
		||||
      "source": "Lorient_KMA",
 | 
			
		||||
      "destination": "Lannion_CAS",
 | 
			
		||||
      "src-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "dst-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager_16QAM",
 | 
			
		||||
          "trx_mode": "16QAM",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
          {
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
              "node-id": "toto",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "synchronization": [
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "0",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "0",
 | 
			
		||||
          "0"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "3",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "3",
 | 
			
		||||
          "4"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "5",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "5",
 | 
			
		||||
          "0"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,762 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "elements": [
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Lannion_CAS",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Vannes_KBE",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Vannes_KBE",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 4.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Rennes_STA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Rennes_STA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx Brest_KLA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Brest_KLA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 4.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx toto",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "toto",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 6.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx tata",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "tata",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 7.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Lannion_CAS",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lannion_CAS",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Lorient_KMA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Lorient_KMA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Vannes_KBE",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Vannes_KBE",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 4.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Rennes_STA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Rennes_STA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm Brest_KLA",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Brest_KLA",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 4.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm toto",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "toto",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 6.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm tata",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "tata",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 7.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west fused spans in Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Corlay",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west fused spans in Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Loudeac",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "west fused spans in Morlaix",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Morlaix",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 3.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east fused spans in Corlay",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Corlay",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east fused spans in Loudeac",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Loudeac",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "east fused spans in Morlaix",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "Morlaix",
 | 
			
		||||
          "region": "RLD",
 | 
			
		||||
          "latitude": 3.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fused"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lannion_CAS → Corlay)-F061",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 20.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Corlay → Loudeac)-F010",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 50.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Loudeac → Lorient_KMA)-F054",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lorient_KMA → Vannes_KBE)-F055",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 10.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lannion_CAS → Stbrieuc)-F056",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Stbrieuc → Rennes_STA)-F057",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 65.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lannion_CAS → Morlaix)-F059",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 40.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Morlaix → Brest_KLA)-F060",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 3.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 35.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (toto → tata)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 6.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Corlay → Lannion_CAS)-F061",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 0.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 20.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Loudeac → Corlay)-F010",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 1.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 50.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Lorient_KMA → Loudeac)-F054",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 2.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Vannes_KBE → Lorient_KMA)-F055",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.0,
 | 
			
		||||
          "longitude": 3.5
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 10.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Stbrieuc → Lannion_CAS)-F056",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 1.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Rennes_STA → Stbrieuc)-F057",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 65.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Morlaix → Lannion_CAS)-F059",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 2.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 40.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (Brest_KLA → Morlaix)-F060",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 3.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 35.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (tata → toto)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 6.5,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "connections": [
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Corlay)-F061"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Corlay → Lannion_CAS)-F061",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Stbrieuc → Lannion_CAS)-F056",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "fiber (Lannion_CAS → Morlaix)-F059"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Morlaix → Lannion_CAS)-F059",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Corlay)-F061",
 | 
			
		||||
      "to_node": "west fused spans in Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west fused spans in Corlay",
 | 
			
		||||
      "to_node": "fiber (Corlay → Loudeac)-F010"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Loudeac → Corlay)-F010",
 | 
			
		||||
      "to_node": "east fused spans in Corlay"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east fused spans in Corlay",
 | 
			
		||||
      "to_node": "fiber (Corlay → Lannion_CAS)-F061"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Corlay → Loudeac)-F010",
 | 
			
		||||
      "to_node": "west fused spans in Loudeac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west fused spans in Loudeac",
 | 
			
		||||
      "to_node": "fiber (Loudeac → Lorient_KMA)-F054"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lorient_KMA → Loudeac)-F054",
 | 
			
		||||
      "to_node": "east fused spans in Loudeac"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east fused spans in Loudeac",
 | 
			
		||||
      "to_node": "fiber (Loudeac → Corlay)-F010"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "fiber (Lorient_KMA → Loudeac)-F054"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Loudeac → Lorient_KMA)-F054",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Vannes_KBE",
 | 
			
		||||
      "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055",
 | 
			
		||||
      "to_node": "roadm Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056",
 | 
			
		||||
      "to_node": "fiber (Stbrieuc → Rennes_STA)-F057"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Rennes_STA → Stbrieuc)-F057",
 | 
			
		||||
      "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Rennes_STA",
 | 
			
		||||
      "to_node": "fiber (Rennes_STA → Stbrieuc)-F057"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Stbrieuc → Rennes_STA)-F057",
 | 
			
		||||
      "to_node": "roadm Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Lannion_CAS → Morlaix)-F059",
 | 
			
		||||
      "to_node": "west fused spans in Morlaix"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "west fused spans in Morlaix",
 | 
			
		||||
      "to_node": "fiber (Morlaix → Brest_KLA)-F060"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Brest_KLA → Morlaix)-F060",
 | 
			
		||||
      "to_node": "east fused spans in Morlaix"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "east fused spans in Morlaix",
 | 
			
		||||
      "to_node": "fiber (Morlaix → Lannion_CAS)-F059"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Brest_KLA",
 | 
			
		||||
      "to_node": "fiber (Brest_KLA → Morlaix)-F060"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (Morlaix → Brest_KLA)-F060",
 | 
			
		||||
      "to_node": "roadm Brest_KLA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm toto",
 | 
			
		||||
      "to_node": "fiber (toto → tata)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (tata → toto)-",
 | 
			
		||||
      "to_node": "roadm toto"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm tata",
 | 
			
		||||
      "to_node": "fiber (tata → toto)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (toto → tata)-",
 | 
			
		||||
      "to_node": "roadm tata"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Lannion_CAS",
 | 
			
		||||
      "to_node": "roadm Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lannion_CAS",
 | 
			
		||||
      "to_node": "trx Lannion_CAS"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Lorient_KMA",
 | 
			
		||||
      "to_node": "roadm Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Lorient_KMA",
 | 
			
		||||
      "to_node": "trx Lorient_KMA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Vannes_KBE",
 | 
			
		||||
      "to_node": "roadm Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Vannes_KBE",
 | 
			
		||||
      "to_node": "trx Vannes_KBE"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Rennes_STA",
 | 
			
		||||
      "to_node": "roadm Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Rennes_STA",
 | 
			
		||||
      "to_node": "trx Rennes_STA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx Brest_KLA",
 | 
			
		||||
      "to_node": "roadm Brest_KLA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm Brest_KLA",
 | 
			
		||||
      "to_node": "trx Brest_KLA"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx toto",
 | 
			
		||||
      "to_node": "roadm toto"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm toto",
 | 
			
		||||
      "to_node": "trx toto"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx tata",
 | 
			
		||||
      "to_node": "roadm tata"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm tata",
 | 
			
		||||
      "to_node": "trx tata"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,224 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "path-request": [
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "0",
 | 
			
		||||
      "source": "Lorient_KMA",
 | 
			
		||||
      "destination": "Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager_16QAM",
 | 
			
		||||
          "trx_mode": "16QAM",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "1",
 | 
			
		||||
      "source": "Brest_KLA",
 | 
			
		||||
      "destination": "Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Brest_KLA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager_16QAM",
 | 
			
		||||
          "trx_mode": "16QAM",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
          {
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
              "node-id": "Lannion_CAS",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "index": 1,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
              "node-id": "Lorient_KMA",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "3",
 | 
			
		||||
      "source": "Lannion_CAS",
 | 
			
		||||
      "destination": "Rennes_STA",
 | 
			
		||||
      "src-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "dst-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "vendorA_trx-type1",
 | 
			
		||||
          "trx_mode": "PS_SP64_1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "4",
 | 
			
		||||
      "source": "Rennes_STA",
 | 
			
		||||
      "destination": "Lannion_CAS",
 | 
			
		||||
      "src-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "dst-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "vendorA_trx-type1",
 | 
			
		||||
          "trx_mode": "PS_SP64_2",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 75000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 64,
 | 
			
		||||
          "output-power": 0.0019952623149688794,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "5",
 | 
			
		||||
      "source": "Lorient_KMA",
 | 
			
		||||
      "destination": "Lannion_CAS",
 | 
			
		||||
      "src-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "dst-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager_16QAM",
 | 
			
		||||
          "trx_mode": "16QAM",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "n": "null",
 | 
			
		||||
              "m": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
          {
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
              "node-id": "toto",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "synchronization": [
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "0",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "0",
 | 
			
		||||
          "0"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "3",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "3",
 | 
			
		||||
          "4"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "5",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "5",
 | 
			
		||||
          "0"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -1,730 +0,0 @@
 | 
			
		||||
{
 | 
			
		||||
  "elements": [
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx a",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "a",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx b",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "b",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx c",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "c",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx d",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "d",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx e",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "e",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx f",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "f",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx g",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "g",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "trx h",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "h",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Transceiver"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm a",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "a",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm b",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "b",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm c",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "c",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm d",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "d",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm e",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "e",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm f",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "f",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm g",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "g",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "roadm h",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "city": "h",
 | 
			
		||||
          "region": "",
 | 
			
		||||
          "latitude": 0,
 | 
			
		||||
          "longitude": 0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Roadm"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (a \u2192 b)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 30.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (a \u2192 c)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 30.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (c \u2192 d)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 50.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (c \u2192 f)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (b \u2192 f)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 70.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (e \u2192 d)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (e \u2192 g)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 90.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (f \u2192 h)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 100.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (h \u2192 g)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 110.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (b \u2192 a)-F061",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 30.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (c \u2192 a)-F010",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 30.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (d \u2192 c)-F054",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 50.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (f \u2192 c)-F055",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 60.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (f \u2192 b)-F056",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 70.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (d \u2192 e)-F057",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 80.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (g \u2192 e)-F059",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 90.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (h \u2192 f)-F060",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 100.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "uid": "fiber (g \u2192 h)-",
 | 
			
		||||
      "metadata": {
 | 
			
		||||
        "location": {
 | 
			
		||||
          "latitude": 0.0,
 | 
			
		||||
          "longitude": 0.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "type": "Fiber",
 | 
			
		||||
      "type_variety": "SSMF",
 | 
			
		||||
      "params": {
 | 
			
		||||
        "length": 110.0,
 | 
			
		||||
        "length_units": "km",
 | 
			
		||||
        "loss_coef": 0.2,
 | 
			
		||||
        "con_in": null,
 | 
			
		||||
        "con_out": null
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "connections": [
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm a",
 | 
			
		||||
      "to_node": "fiber (a \u2192 b)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (b \u2192 a)-F061",
 | 
			
		||||
      "to_node": "roadm a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm a",
 | 
			
		||||
      "to_node": "fiber (a \u2192 c)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (c \u2192 a)-F010",
 | 
			
		||||
      "to_node": "roadm a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm b",
 | 
			
		||||
      "to_node": "fiber (b \u2192 a)-F061"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (a \u2192 b)-",
 | 
			
		||||
      "to_node": "roadm b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm b",
 | 
			
		||||
      "to_node": "fiber (b \u2192 f)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (f \u2192 b)-F056",
 | 
			
		||||
      "to_node": "roadm b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm c",
 | 
			
		||||
      "to_node": "fiber (c \u2192 a)-F010"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (a \u2192 c)-",
 | 
			
		||||
      "to_node": "roadm c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm c",
 | 
			
		||||
      "to_node": "fiber (c \u2192 d)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (d \u2192 c)-F054",
 | 
			
		||||
      "to_node": "roadm c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm c",
 | 
			
		||||
      "to_node": "fiber (c \u2192 f)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (f \u2192 c)-F055",
 | 
			
		||||
      "to_node": "roadm c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm d",
 | 
			
		||||
      "to_node": "fiber (d \u2192 c)-F054"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (c \u2192 d)-",
 | 
			
		||||
      "to_node": "roadm d"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm d",
 | 
			
		||||
      "to_node": "fiber (d \u2192 e)-F057"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (e \u2192 d)-",
 | 
			
		||||
      "to_node": "roadm d"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm e",
 | 
			
		||||
      "to_node": "fiber (e \u2192 d)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (d \u2192 e)-F057",
 | 
			
		||||
      "to_node": "roadm e"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm e",
 | 
			
		||||
      "to_node": "fiber (e \u2192 g)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (g \u2192 e)-F059",
 | 
			
		||||
      "to_node": "roadm e"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm f",
 | 
			
		||||
      "to_node": "fiber (f \u2192 c)-F055"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (c \u2192 f)-",
 | 
			
		||||
      "to_node": "roadm f"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm f",
 | 
			
		||||
      "to_node": "fiber (f \u2192 b)-F056"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (b \u2192 f)-",
 | 
			
		||||
      "to_node": "roadm f"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm f",
 | 
			
		||||
      "to_node": "fiber (f \u2192 h)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (h \u2192 f)-F060",
 | 
			
		||||
      "to_node": "roadm f"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm g",
 | 
			
		||||
      "to_node": "fiber (g \u2192 e)-F059"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (e \u2192 g)-",
 | 
			
		||||
      "to_node": "roadm g"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm g",
 | 
			
		||||
      "to_node": "fiber (g \u2192 h)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (h \u2192 g)-",
 | 
			
		||||
      "to_node": "roadm g"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm h",
 | 
			
		||||
      "to_node": "fiber (h \u2192 f)-F060"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (f \u2192 h)-",
 | 
			
		||||
      "to_node": "roadm h"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm h",
 | 
			
		||||
      "to_node": "fiber (h \u2192 g)-"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "fiber (g \u2192 h)-",
 | 
			
		||||
      "to_node": "roadm h"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx a",
 | 
			
		||||
      "to_node": "roadm a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm a",
 | 
			
		||||
      "to_node": "trx a"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx b",
 | 
			
		||||
      "to_node": "roadm b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm b",
 | 
			
		||||
      "to_node": "trx b"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx c",
 | 
			
		||||
      "to_node": "roadm c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm c",
 | 
			
		||||
      "to_node": "trx c"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx d",
 | 
			
		||||
      "to_node": "roadm d"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm d",
 | 
			
		||||
      "to_node": "trx d"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx e",
 | 
			
		||||
      "to_node": "roadm e"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm e",
 | 
			
		||||
      "to_node": "trx e"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx f",
 | 
			
		||||
      "to_node": "roadm f"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm f",
 | 
			
		||||
      "to_node": "trx f"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx g",
 | 
			
		||||
      "to_node": "roadm g"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm g",
 | 
			
		||||
      "to_node": "trx g"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "trx h",
 | 
			
		||||
      "to_node": "roadm h"
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "from_node": "roadm h",
 | 
			
		||||
      "to_node": "trx h"
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										223
									
								
								tests/data/raman_fiber_config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								tests/data/raman_fiber_config.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,223 @@
 | 
			
		||||
{
 | 
			
		||||
  "uid": "Span1",
 | 
			
		||||
  "params": {
 | 
			
		||||
    "length": 80,
 | 
			
		||||
    "loss_coef": 0.2,
 | 
			
		||||
    "length_units": "km",
 | 
			
		||||
    "att_in": 0,
 | 
			
		||||
    "con_in": 0.5,
 | 
			
		||||
    "con_out": 0.5,
 | 
			
		||||
    "type_variety": "SSMF",
 | 
			
		||||
    "dispersion": 0.0000167,
 | 
			
		||||
    "gamma": 0.00127,
 | 
			
		||||
    "raman_efficiency": {
 | 
			
		||||
      "cr": [
 | 
			
		||||
        0,
 | 
			
		||||
        0.0000094,
 | 
			
		||||
        0.0000292,
 | 
			
		||||
        0.0000488,
 | 
			
		||||
        0.0000682,
 | 
			
		||||
        0.0000831,
 | 
			
		||||
        0.000094,
 | 
			
		||||
        0.0001014,
 | 
			
		||||
        0.0001069,
 | 
			
		||||
        0.0001119,
 | 
			
		||||
        0.0001217,
 | 
			
		||||
        0.0001268,
 | 
			
		||||
        0.0001365,
 | 
			
		||||
        0.000149,
 | 
			
		||||
        0.000165,
 | 
			
		||||
        0.000181,
 | 
			
		||||
        0.0001977,
 | 
			
		||||
        0.0002192,
 | 
			
		||||
        0.0002469,
 | 
			
		||||
        0.0002749,
 | 
			
		||||
        0.0002999,
 | 
			
		||||
        0.0003206,
 | 
			
		||||
        0.0003405,
 | 
			
		||||
        0.0003592,
 | 
			
		||||
        0.000374,
 | 
			
		||||
        0.0003826,
 | 
			
		||||
        0.0003841,
 | 
			
		||||
        0.0003826,
 | 
			
		||||
        0.0003802,
 | 
			
		||||
        0.0003756,
 | 
			
		||||
        0.0003549,
 | 
			
		||||
        0.0003795,
 | 
			
		||||
        0.000344,
 | 
			
		||||
        0.0002933,
 | 
			
		||||
        0.0002024,
 | 
			
		||||
        0.0001158,
 | 
			
		||||
        0.0000846,
 | 
			
		||||
        0.0000714,
 | 
			
		||||
        0.0000686,
 | 
			
		||||
        0.000085,
 | 
			
		||||
        0.0000893,
 | 
			
		||||
        0.0000901,
 | 
			
		||||
        0.0000815,
 | 
			
		||||
        0.0000667,
 | 
			
		||||
        0.0000437,
 | 
			
		||||
        0.0000328,
 | 
			
		||||
        0.0000296,
 | 
			
		||||
        0.0000265,
 | 
			
		||||
        0.0000257,
 | 
			
		||||
        0.0000281,
 | 
			
		||||
        0.0000308,
 | 
			
		||||
        0.0000367,
 | 
			
		||||
        0.0000585,
 | 
			
		||||
        0.0000663,
 | 
			
		||||
        0.0000636,
 | 
			
		||||
        0.000055,
 | 
			
		||||
        0.0000406,
 | 
			
		||||
        0.0000277,
 | 
			
		||||
        0.0000242,
 | 
			
		||||
        0.0000187,
 | 
			
		||||
        0.000016,
 | 
			
		||||
        0.000014,
 | 
			
		||||
        0.0000113,
 | 
			
		||||
        0.0000105,
 | 
			
		||||
        0.0000098,
 | 
			
		||||
        0.0000098,
 | 
			
		||||
        0.0000113,
 | 
			
		||||
        0.0000164,
 | 
			
		||||
        0.0000195,
 | 
			
		||||
        0.0000238,
 | 
			
		||||
        0.0000226,
 | 
			
		||||
        0.0000203,
 | 
			
		||||
        0.0000148,
 | 
			
		||||
        0.0000109,
 | 
			
		||||
        0.0000098,
 | 
			
		||||
        0.0000105,
 | 
			
		||||
        0.0000117,
 | 
			
		||||
        0.0000125,
 | 
			
		||||
        0.0000121,
 | 
			
		||||
        0.0000109,
 | 
			
		||||
        0.0000098,
 | 
			
		||||
        0.0000082,
 | 
			
		||||
        0.0000066,
 | 
			
		||||
        0.0000047,
 | 
			
		||||
        0.0000027,
 | 
			
		||||
        0.0000019,
 | 
			
		||||
        0.0000012,
 | 
			
		||||
        4e-7,
 | 
			
		||||
        2e-7,
 | 
			
		||||
        1e-7
 | 
			
		||||
      ],
 | 
			
		||||
      "frequency_offset": [
 | 
			
		||||
        0,
 | 
			
		||||
        500000000000,
 | 
			
		||||
        1000000000000,
 | 
			
		||||
        1500000000000,
 | 
			
		||||
        2000000000000,
 | 
			
		||||
        2500000000000,
 | 
			
		||||
        3000000000000,
 | 
			
		||||
        3500000000000,
 | 
			
		||||
        4000000000000,
 | 
			
		||||
        4500000000000,
 | 
			
		||||
        5000000000000,
 | 
			
		||||
        5500000000000,
 | 
			
		||||
        6000000000000,
 | 
			
		||||
        6500000000000,
 | 
			
		||||
        7000000000000,
 | 
			
		||||
        7500000000000,
 | 
			
		||||
        8000000000000,
 | 
			
		||||
        8500000000000,
 | 
			
		||||
        9000000000000,
 | 
			
		||||
        9500000000000,
 | 
			
		||||
        10000000000000,
 | 
			
		||||
        10500000000000,
 | 
			
		||||
        11000000000000,
 | 
			
		||||
        11500000000000,
 | 
			
		||||
        12000000000000,
 | 
			
		||||
        12500000000000,
 | 
			
		||||
        12750000000000,
 | 
			
		||||
        13000000000000,
 | 
			
		||||
        13250000000000,
 | 
			
		||||
        13500000000000,
 | 
			
		||||
        14000000000000,
 | 
			
		||||
        14500000000000,
 | 
			
		||||
        14750000000000,
 | 
			
		||||
        15000000000000,
 | 
			
		||||
        15500000000000,
 | 
			
		||||
        16000000000000,
 | 
			
		||||
        16500000000000,
 | 
			
		||||
        17000000000000,
 | 
			
		||||
        17500000000000,
 | 
			
		||||
        18000000000000,
 | 
			
		||||
        18250000000000,
 | 
			
		||||
        18500000000000,
 | 
			
		||||
        18750000000000,
 | 
			
		||||
        19000000000000,
 | 
			
		||||
        19500000000000,
 | 
			
		||||
        20000000000000,
 | 
			
		||||
        20500000000000,
 | 
			
		||||
        21000000000000,
 | 
			
		||||
        21500000000000,
 | 
			
		||||
        22000000000000,
 | 
			
		||||
        22500000000000,
 | 
			
		||||
        23000000000000,
 | 
			
		||||
        23500000000000,
 | 
			
		||||
        24000000000000,
 | 
			
		||||
        24500000000000,
 | 
			
		||||
        25000000000000,
 | 
			
		||||
        25500000000000,
 | 
			
		||||
        26000000000000,
 | 
			
		||||
        26500000000000,
 | 
			
		||||
        27000000000000,
 | 
			
		||||
        27500000000000,
 | 
			
		||||
        28000000000000,
 | 
			
		||||
        28500000000000,
 | 
			
		||||
        29000000000000,
 | 
			
		||||
        29500000000000,
 | 
			
		||||
        30000000000000,
 | 
			
		||||
        30500000000000,
 | 
			
		||||
        31000000000000,
 | 
			
		||||
        31500000000000,
 | 
			
		||||
        32000000000000,
 | 
			
		||||
        32500000000000,
 | 
			
		||||
        33000000000000,
 | 
			
		||||
        33500000000000,
 | 
			
		||||
        34000000000000,
 | 
			
		||||
        34500000000000,
 | 
			
		||||
        35000000000000,
 | 
			
		||||
        35500000000000,
 | 
			
		||||
        36000000000000,
 | 
			
		||||
        36500000000000,
 | 
			
		||||
        37000000000000,
 | 
			
		||||
        37500000000000,
 | 
			
		||||
        38000000000000,
 | 
			
		||||
        38500000000000,
 | 
			
		||||
        39000000000000,
 | 
			
		||||
        39500000000000,
 | 
			
		||||
        40000000000000,
 | 
			
		||||
        40500000000000,
 | 
			
		||||
        41000000000000,
 | 
			
		||||
        41500000000000,
 | 
			
		||||
        42000000000000
 | 
			
		||||
      ]
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "operational": {
 | 
			
		||||
    "temperature": 283,
 | 
			
		||||
    "raman_pumps": [
 | 
			
		||||
      {
 | 
			
		||||
        "power": 0.2,
 | 
			
		||||
        "frequency": 205000000000000,
 | 
			
		||||
        "propagation_direction": "counterprop"
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        "power": 0.206,
 | 
			
		||||
        "frequency": 201000000000000,
 | 
			
		||||
        "propagation_direction": "counterprop"
 | 
			
		||||
      }
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "metadata": {
 | 
			
		||||
    "location": {
 | 
			
		||||
      "latitude": 1,
 | 
			
		||||
      "longitude": 0,
 | 
			
		||||
      "city": null,
 | 
			
		||||
      "region": ""
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								tests/data/sim_params.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/data/sim_params.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
{
 | 
			
		||||
  "raman_computed_channels": [1, 18, 37, 56, 75],
 | 
			
		||||
  "raman_parameters": {
 | 
			
		||||
    "flag_raman": true,
 | 
			
		||||
    "space_resolution": 10e3,
 | 
			
		||||
    "tolerance": 1e-8
 | 
			
		||||
  },
 | 
			
		||||
  "nli_parameters": {
 | 
			
		||||
  	"nli_method_name": "ggn_spectrally_separated",
 | 
			
		||||
  	"wdm_grid_size": 50e9,
 | 
			
		||||
  	"dispersion_tolerance": 1,
 | 
			
		||||
  	"phase_shift_tollerance": 0.1
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								tests/data/testService.xls
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/data/testService.xls
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										79
									
								
								tests/data/testService_services_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								tests/data/testService_services_expected.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
{
 | 
			
		||||
  "path-request": [
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "0",
 | 
			
		||||
      "source": "trx Lorient_KMA",
 | 
			
		||||
      "destination": "trx Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager",
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 100000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "1",
 | 
			
		||||
      "source": "trx Brest_KLA",
 | 
			
		||||
      "destination": "trx Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Brest_KLA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager",
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 10000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "3",
 | 
			
		||||
      "source": "trx Lannion_CAS",
 | 
			
		||||
      "destination": "trx Rennes_STA",
 | 
			
		||||
      "src-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "dst-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "vendorA_trx-type1",
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 60000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								tests/data/testTopology.xls
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/data/testTopology.xls
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										3357
									
								
								tests/data/testTopology_auto_design_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3357
									
								
								tests/data/testTopology_auto_design_expected.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1781
									
								
								tests/data/testTopology_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1781
									
								
								tests/data/testTopology_expected.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1471
									
								
								tests/data/testTopology_response.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1471
									
								
								tests/data/testTopology_response.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										8
									
								
								tests/data/testTopology_response_expected.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/data/testTopology_response_expected.csv
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,OSNR-0.1nm,SNR-0.1nm,SNR-bandwidth,baud rate (Gbaud),input power (dBm),path,"spectrum (N,M)",reversed path OSNR-0.1nm,reversed path SNR-0.1nm,reversed path SNR-bandwidth
 | 
			
		||||
0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,30.84,30.84,26.75,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE,"-284, 4"
 | 
			
		||||
1,trx Brest_KLA,trx Vannes_KBE,10.0,True,1,1,Voyager,mode 1,22.65,22.11,18.03,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | Edfa0_roadm Brest_KLA | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | Edfa0_fiber (Loudeac → Lorient_KMA)-F054 | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE,"-276, 4"
 | 
			
		||||
3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,28.29,25.85,21.77,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | Edfa0_fiber (Stbrieuc → Rennes_STA)-F057 | roadm Rennes_STA | trx Rennes_STA,"-284, 4"
 | 
			
		||||
4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,22.27,22.15,15.05,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | Edfa1_roadm Rennes_STA | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | Edfa0_fiber (Ploermel → Vannes_KBE)- | roadm Vannes_KBE | Edfa0_roadm Vannes_KBE | fiber (Vannes_KBE → Lorient_KMA)-F055 | Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055 | roadm Lorient_KMA | Edfa0_roadm Lorient_KMA | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS,"-266, 6"
 | 
			
		||||
5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,30.79,28.77,21.68,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | Edfa0_roadm Rennes_STA | fiber (Rennes_STA → Stbrieuc)-F057 | Edfa0_fiber (Rennes_STA → Stbrieuc)-F057 | fiber (Stbrieuc → Lannion_CAS)-F056 | Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056 | roadm Lannion_CAS | trx Lannion_CAS,"-274, 6"
 | 
			
		||||
6,,,,NO_PATH,,,,,,,,,,,
 | 
			
		||||
 | 
			
		||||
| 
		
		
			 Can't render this file because it has a wrong number of fields in line 2. 
		
	 | 
							
								
								
									
										218
									
								
								tests/data/testTopology_services_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								tests/data/testTopology_services_expected.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,218 @@
 | 
			
		||||
{
 | 
			
		||||
  "path-request": [
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "0",
 | 
			
		||||
      "source": "trx Lorient_KMA",
 | 
			
		||||
      "destination": "trx Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Lorient_KMA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager",
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 100000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "1",
 | 
			
		||||
      "source": "trx Brest_KLA",
 | 
			
		||||
      "destination": "trx Vannes_KBE",
 | 
			
		||||
      "src-tp-id": "trx Brest_KLA",
 | 
			
		||||
      "dst-tp-id": "trx Vannes_KBE",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "Voyager",
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 10000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm Brest_KLA",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 1,
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm Lannion_CAS",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 2,
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm Lorient_KMA",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 3,
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm Vannes_KBE",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "3",
 | 
			
		||||
      "source": "trx Lannion_CAS",
 | 
			
		||||
      "destination": "trx Rennes_STA",
 | 
			
		||||
      "src-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "dst-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "vendorA_trx-type1",
 | 
			
		||||
          "trx_mode": "mode 1",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 50000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 80,
 | 
			
		||||
          "output-power": 0.0012589254117941673,
 | 
			
		||||
          "path_bandwidth": 60000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "4",
 | 
			
		||||
      "source": "trx Rennes_STA",
 | 
			
		||||
      "destination": "trx Lannion_CAS",
 | 
			
		||||
      "src-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "dst-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "vendorA_trx-type1",
 | 
			
		||||
          "trx_mode": "mode 2",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 75000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 150000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "5",
 | 
			
		||||
      "source": "trx Rennes_STA",
 | 
			
		||||
      "destination": "trx Lannion_CAS",
 | 
			
		||||
      "src-tp-id": "trx Rennes_STA",
 | 
			
		||||
      "dst-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "vendorA_trx-type1",
 | 
			
		||||
          "trx_mode": "mode 2",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 75000000000.0,
 | 
			
		||||
          "max-nb-of-channel": 63,
 | 
			
		||||
          "output-power": 0.0019952623149688794,
 | 
			
		||||
          "path_bandwidth": 20000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "6",
 | 
			
		||||
      "source": "trx Lannion_CAS",
 | 
			
		||||
      "destination": "trx a",
 | 
			
		||||
      "src-tp-id": "trx Lannion_CAS",
 | 
			
		||||
      "dst-tp-id": "trx a",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
          "trx_type": "vendorA_trx-type1",
 | 
			
		||||
          "trx_mode": "mode 2",
 | 
			
		||||
          "effective-freq-slot": [
 | 
			
		||||
            {
 | 
			
		||||
              "N": "null",
 | 
			
		||||
              "M": "null"
 | 
			
		||||
            }
 | 
			
		||||
          ],
 | 
			
		||||
          "spacing": 75000000000.0,
 | 
			
		||||
          "max-nb-of-channel": null,
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 100000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
  "synchronization": [
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "3",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "false",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "3",
 | 
			
		||||
          "1"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "synchronization-id": "4",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "false",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "4",
 | 
			
		||||
          "5"
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
@@ -2,10 +2,11 @@
 | 
			
		||||
  "path-request": [
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "1",
 | 
			
		||||
      "source": "a",
 | 
			
		||||
      "destination": "g",
 | 
			
		||||
      "source": "trx a",
 | 
			
		||||
      "destination": "trx g",
 | 
			
		||||
      "src-tp-id": "trx a",
 | 
			
		||||
      "dst-tp-id": "trx g",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -22,17 +23,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "2a",
 | 
			
		||||
      "source": "a",
 | 
			
		||||
      "destination": "h",
 | 
			
		||||
      "source": "trx a",
 | 
			
		||||
      "destination": "trx h",
 | 
			
		||||
      "src-tp-id": "trx a",
 | 
			
		||||
      "dst-tp-id": "trx h",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -49,17 +48,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "3",
 | 
			
		||||
      "source": "f",
 | 
			
		||||
      "destination": "b",
 | 
			
		||||
      "source": "trx f",
 | 
			
		||||
      "destination": "trx b",
 | 
			
		||||
      "src-tp-id": "trx f",
 | 
			
		||||
      "dst-tp-id": "trx b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -76,17 +73,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "ee",
 | 
			
		||||
      "source": "c",
 | 
			
		||||
      "destination": "f",
 | 
			
		||||
      "source": "trx c",
 | 
			
		||||
      "destination": "trx f",
 | 
			
		||||
      "src-tp-id": "trx c",
 | 
			
		||||
      "dst-tp-id": "trx f",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -104,36 +99,23 @@
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm e",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 1,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm g",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
@@ -141,10 +123,11 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "ff",
 | 
			
		||||
      "source": "c",
 | 
			
		||||
      "destination": "f",
 | 
			
		||||
      "source": "trx c",
 | 
			
		||||
      "destination": "trx f",
 | 
			
		||||
      "src-tp-id": "trx c",
 | 
			
		||||
      "dst-tp-id": "trx f",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -161,17 +144,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "10",
 | 
			
		||||
      "source": "a",
 | 
			
		||||
      "destination": "g",
 | 
			
		||||
      "source": "trx a",
 | 
			
		||||
      "destination": "trx g",
 | 
			
		||||
      "src-tp-id": "trx a",
 | 
			
		||||
      "dst-tp-id": "trx g",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -188,17 +169,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "11",
 | 
			
		||||
      "source": "a",
 | 
			
		||||
      "destination": "h",
 | 
			
		||||
      "source": "trx a",
 | 
			
		||||
      "destination": "trx h",
 | 
			
		||||
      "src-tp-id": "trx a",
 | 
			
		||||
      "dst-tp-id": "trx h",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -216,21 +195,15 @@
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "bb",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
@@ -238,10 +211,11 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "12",
 | 
			
		||||
      "source": "f",
 | 
			
		||||
      "destination": "b",
 | 
			
		||||
      "source": "trx f",
 | 
			
		||||
      "destination": "trx b",
 | 
			
		||||
      "src-tp-id": "trx f",
 | 
			
		||||
      "dst-tp-id": "trx b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -259,21 +233,15 @@
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "trx b",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
@@ -281,10 +249,11 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "13",
 | 
			
		||||
      "source": "c",
 | 
			
		||||
      "destination": "f",
 | 
			
		||||
      "source": "trx c",
 | 
			
		||||
      "destination": "trx f",
 | 
			
		||||
      "src-tp-id": "trx c",
 | 
			
		||||
      "dst-tp-id": "trx f",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -301,17 +270,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "14",
 | 
			
		||||
      "source": "c",
 | 
			
		||||
      "destination": "f",
 | 
			
		||||
      "source": "trx c",
 | 
			
		||||
      "destination": "trx f",
 | 
			
		||||
      "src-tp-id": "trx c",
 | 
			
		||||
      "dst-tp-id": "trx f",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -329,36 +296,23 @@
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm e",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 1,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm g",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
@@ -366,10 +320,11 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "e:1# /",
 | 
			
		||||
      "source": "a",
 | 
			
		||||
      "destination": "g",
 | 
			
		||||
      "source": "trx a",
 | 
			
		||||
      "destination": "trx g",
 | 
			
		||||
      "src-tp-id": "trx a",
 | 
			
		||||
      "dst-tp-id": "trx g",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -386,17 +341,15 @@
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "b-2a",
 | 
			
		||||
      "source": "a",
 | 
			
		||||
      "destination": "h",
 | 
			
		||||
      "source": "trx a",
 | 
			
		||||
      "destination": "trx h",
 | 
			
		||||
      "src-tp-id": "trx a",
 | 
			
		||||
      "dst-tp-id": "trx h",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -413,17 +366,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "3a;?",
 | 
			
		||||
      "source": "f",
 | 
			
		||||
      "destination": "b",
 | 
			
		||||
      "source": "trx f",
 | 
			
		||||
      "destination": "trx b",
 | 
			
		||||
      "src-tp-id": "trx f",
 | 
			
		||||
      "dst-tp-id": "trx b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -440,17 +391,15 @@
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "ee-s",
 | 
			
		||||
      "source": "c",
 | 
			
		||||
      "destination": "f",
 | 
			
		||||
      "source": "trx c",
 | 
			
		||||
      "destination": "trx f",
 | 
			
		||||
      "src-tp-id": "trx c",
 | 
			
		||||
      "dst-tp-id": "trx f",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -468,36 +417,23 @@
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": [
 | 
			
		||||
      "explicit-route-objects": {
 | 
			
		||||
        "route-object-include-exclude": [
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 0,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm e",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "explicit-route-usage": "route-include-ero",
 | 
			
		||||
            "index": 1,
 | 
			
		||||
            "unnumbered-hop": {
 | 
			
		||||
            "num-unnum-hop": {
 | 
			
		||||
              "node-id": "roadm g",
 | 
			
		||||
              "link-tp-id": "link-tp-id is not used",
 | 
			
		||||
              "hop-type": "loose",
 | 
			
		||||
              "direction": "direction is not used"
 | 
			
		||||
            },
 | 
			
		||||
            "label-hop": {
 | 
			
		||||
              "te-label": {
 | 
			
		||||
                "generic": "generic is not used",
 | 
			
		||||
                "direction": "direction is not used"
 | 
			
		||||
              }
 | 
			
		||||
              "hop-type": "LOOSE"
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
@@ -505,10 +441,11 @@
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "ff-b",
 | 
			
		||||
      "source": "c",
 | 
			
		||||
      "destination": "f",
 | 
			
		||||
      "source": "trx c",
 | 
			
		||||
      "destination": "trx f",
 | 
			
		||||
      "src-tp-id": "trx c",
 | 
			
		||||
      "dst-tp-id": "trx f",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -525,17 +462,15 @@
 | 
			
		||||
          "output-power": 0.001,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "10-z",
 | 
			
		||||
      "source": "a",
 | 
			
		||||
      "destination": "g",
 | 
			
		||||
      "source": "trx a",
 | 
			
		||||
      "destination": "trx g",
 | 
			
		||||
      "src-tp-id": "trx a",
 | 
			
		||||
      "dst-tp-id": "trx g",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -552,17 +487,15 @@
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "11 g",
 | 
			
		||||
      "source": "a",
 | 
			
		||||
      "destination": "h",
 | 
			
		||||
      "source": "trx a",
 | 
			
		||||
      "destination": "trx h",
 | 
			
		||||
      "src-tp-id": "trx a",
 | 
			
		||||
      "dst-tp-id": "trx h",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -579,17 +512,15 @@
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": 300000000000.0
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "12<",
 | 
			
		||||
      "source": "f",
 | 
			
		||||
      "destination": "b",
 | 
			
		||||
      "source": "trx f",
 | 
			
		||||
      "destination": "trx b",
 | 
			
		||||
      "src-tp-id": "trx f",
 | 
			
		||||
      "dst-tp-id": "trx b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -606,17 +537,15 @@
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": null
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      "request-id": "12>",
 | 
			
		||||
      "source": "f",
 | 
			
		||||
      "destination": "b",
 | 
			
		||||
      "source": "trx f",
 | 
			
		||||
      "destination": "trx b",
 | 
			
		||||
      "src-tp-id": "trx f",
 | 
			
		||||
      "dst-tp-id": "trx b",
 | 
			
		||||
      "bidirectional": false,
 | 
			
		||||
      "path-constraints": {
 | 
			
		||||
        "te-bandwidth": {
 | 
			
		||||
          "technology": "flexi-grid",
 | 
			
		||||
@@ -633,9 +562,6 @@
 | 
			
		||||
          "output-power": null,
 | 
			
		||||
          "path_bandwidth": null
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "optimizations": {
 | 
			
		||||
        "explicit-route-include-objects": []
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ],
 | 
			
		||||
@@ -644,8 +570,7 @@
 | 
			
		||||
      "synchronization-id": "1",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "1",
 | 
			
		||||
          "2a"
 | 
			
		||||
@@ -656,8 +581,7 @@
 | 
			
		||||
      "synchronization-id": "3",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "3",
 | 
			
		||||
          "1"
 | 
			
		||||
@@ -668,8 +592,7 @@
 | 
			
		||||
      "synchronization-id": "ff",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "ff",
 | 
			
		||||
          "13"
 | 
			
		||||
@@ -680,8 +603,7 @@
 | 
			
		||||
      "synchronization-id": "13",
 | 
			
		||||
      "svec": {
 | 
			
		||||
        "relaxable": "False",
 | 
			
		||||
        "link-diverse": "True",
 | 
			
		||||
        "node-diverse": "True",
 | 
			
		||||
        "disjointness": "node link",
 | 
			
		||||
        "request-id-number": [
 | 
			
		||||
          "13",
 | 
			
		||||
          "14"
 | 
			
		||||
@@ -77,7 +77,22 @@
 | 
			
		||||
                    "longitude": 0
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },   
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
        "uid": "Att_B",
 | 
			
		||||
        "type": "Fused",
 | 
			
		||||
        "params":{
 | 
			
		||||
            "loss":16
 | 
			
		||||
        }, 
 | 
			
		||||
        "metadata": {
 | 
			
		||||
            "location": {
 | 
			
		||||
              "latitude": 2.0,
 | 
			
		||||
              "longitude": 1.0,
 | 
			
		||||
              "city": "Corlay",
 | 
			
		||||
              "region": "RLD"
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "uid": "Site_B",
 | 
			
		||||
            "type": "Transceiver",
 | 
			
		||||
@@ -110,6 +125,10 @@
 | 
			
		||||
        },        
 | 
			
		||||
        {
 | 
			
		||||
            "from_node": "Edfa2",
 | 
			
		||||
            "to_node": "Att_B"
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "from_node": "Att_B",
 | 
			
		||||
            "to_node": "Site_B"
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,8 +9,8 @@ from json import load
 | 
			
		||||
from gnpy.core.elements import Transceiver, Fiber, Edfa
 | 
			
		||||
from gnpy.core.utils import lin2db, db2lin
 | 
			
		||||
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
 | 
			
		||||
from gnpy.core.equipment import load_equipment, automatic_fmax
 | 
			
		||||
from gnpy.core.network import build_network, load_network, set_roadm_loss
 | 
			
		||||
from gnpy.core.equipment import load_equipment, automatic_fmax, automatic_nch
 | 
			
		||||
from gnpy.core.network import build_network, load_network
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
@@ -79,7 +79,7 @@ def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si):
 | 
			
		||||
    pin = pin/db2lin(gain)
 | 
			
		||||
    baud_rates = array([c.baud_rate for c in si.carriers])
 | 
			
		||||
    edfa.operational.gain_target = gain
 | 
			
		||||
    pref = Pref(0, -gain)
 | 
			
		||||
    pref = Pref(0, -gain, lin2db(len(frequencies)))
 | 
			
		||||
    edfa.interpol_params(frequencies, pin, baud_rates, pref)
 | 
			
		||||
    result = edfa.nf
 | 
			
		||||
    assert pytest.approx(nf_expected, abs=0.01) == result[0]
 | 
			
		||||
@@ -93,7 +93,7 @@ def test_fixed_gain_nf(gain, nf_expected, setup_edfa_fixed_gain, si):
 | 
			
		||||
    pin = pin/db2lin(gain)
 | 
			
		||||
    baud_rates = array([c.baud_rate for c in si.carriers])
 | 
			
		||||
    edfa.operational.gain_target = gain
 | 
			
		||||
    pref = Pref(0, -gain)
 | 
			
		||||
    pref = Pref(0, -gain, lin2db(len(frequencies)))
 | 
			
		||||
    edfa.interpol_params(frequencies, pin, baud_rates, pref)
 | 
			
		||||
 | 
			
		||||
    assert pytest.approx(nf_expected, abs=0.01) == edfa.nf[0]
 | 
			
		||||
@@ -118,7 +118,7 @@ def test_compare_nf_models(gain, setup_edfa_variable_gain, si):
 | 
			
		||||
    pin = pin/db2lin(gain)
 | 
			
		||||
    baud_rates = array([c.baud_rate for c in si.carriers])
 | 
			
		||||
    edfa.operational.gain_target = gain
 | 
			
		||||
    pref = Pref(0, -gain)
 | 
			
		||||
    pref = Pref(0, -gain, lin2db(len(frequencies)))
 | 
			
		||||
    edfa.interpol_params(frequencies, pin, baud_rates, pref)
 | 
			
		||||
    nf_model = edfa.nf[0]
 | 
			
		||||
    edfa.interpol_params(frequencies, pin, baud_rates, pref)
 | 
			
		||||
@@ -137,7 +137,7 @@ def test_ase_noise(gain, si, setup_edfa_variable_gain, setup_trx, bw):
 | 
			
		||||
    pin = array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
 | 
			
		||||
    baud_rates = array([c.baud_rate for c in si.carriers])
 | 
			
		||||
    edfa.operational.gain_target = gain
 | 
			
		||||
    pref = Pref(0, 0)
 | 
			
		||||
    pref = Pref(0, 0, lin2db(len(frequencies)))
 | 
			
		||||
    edfa.interpol_params(frequencies, pin, baud_rates, pref)
 | 
			
		||||
    nf = edfa.nf
 | 
			
		||||
    pin = lin2db(pin[0]*1e3)
 | 
			
		||||
 
 | 
			
		||||
@@ -18,16 +18,14 @@ from pathlib import Path
 | 
			
		||||
import pytest
 | 
			
		||||
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch
 | 
			
		||||
from gnpy.core.network import load_network, build_network
 | 
			
		||||
from examples.path_requests_run import (requests_from_json , correct_route_list ,
 | 
			
		||||
                                        load_requests , disjunctions_from_json)
 | 
			
		||||
from gnpy.core.request import (compute_path_dsjctn, isdisjoint , find_reversed_path,
 | 
			
		||||
                               propagate,propagate_and_optimize_mode) 
 | 
			
		||||
from examples.path_requests_run import requests_from_json, correct_route_list, load_requests
 | 
			
		||||
from gnpy.core.request import compute_path_dsjctn, propagate, propagate_and_optimize_mode
 | 
			
		||||
from gnpy.core.utils import db2lin, lin2db
 | 
			
		||||
from gnpy.core.elements import Roadm
 | 
			
		||||
 | 
			
		||||
network_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy.json'
 | 
			
		||||
service_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_services.json'
 | 
			
		||||
result_file_name  = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_results.json'
 | 
			
		||||
network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json'
 | 
			
		||||
service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json'
 | 
			
		||||
result_file_name  = Path(__file__).parent.parent / 'tests/data/testTopology_testresults.json'
 | 
			
		||||
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize("net",[network_file_name])
 | 
			
		||||
@@ -35,7 +33,7 @@ eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
 | 
			
		||||
@pytest.mark.parametrize("serv",[service_file_name])
 | 
			
		||||
@pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 2', 'mode 1', 'mode 2', 'nok']])
 | 
			
		||||
def test_automaticmodefeature(net,eqpt,serv,expected_mode):
 | 
			
		||||
    data = load_requests(serv,eqpt)
 | 
			
		||||
    data = load_requests(serv, eqpt, bidir=False)
 | 
			
		||||
    equipment = load_equipment(eqpt)
 | 
			
		||||
    network = load_network(net,equipment)
 | 
			
		||||
 | 
			
		||||
@@ -72,7 +70,7 @@ def test_automaticmodefeature(net,eqpt,serv,expected_mode):
 | 
			
		||||
        if pathreq.baud_rate is not None:
 | 
			
		||||
            print(pathreq.format)
 | 
			
		||||
            path_res_list.append(pathreq.format)
 | 
			
		||||
            total_path = propagate(total_path,pathreq,equipment, show=False)
 | 
			
		||||
            total_path = propagate(total_path,pathreq,equipment)
 | 
			
		||||
        else:
 | 
			
		||||
            total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment)
 | 
			
		||||
            # if no baudrate satisfies spacing, no mode is returned and an empty path is returned
 | 
			
		||||
 
 | 
			
		||||
@@ -19,20 +19,20 @@ from examples.path_requests_run import (requests_from_json , correct_route_list
 | 
			
		||||
from gnpy.core.request import compute_path_dsjctn, isdisjoint , find_reversed_path
 | 
			
		||||
from gnpy.core.utils import db2lin, lin2db
 | 
			
		||||
from gnpy.core.elements import Roadm
 | 
			
		||||
from gnpy.core.spectrum_assignment import build_oms_list
 | 
			
		||||
 | 
			
		||||
network_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy.json'
 | 
			
		||||
service_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_services.json'
 | 
			
		||||
result_file_name  = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_results.json'
 | 
			
		||||
network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json'
 | 
			
		||||
service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json'
 | 
			
		||||
result_file_name  = Path(__file__).parent.parent / 'tests/data/testTopology_testresults.json'
 | 
			
		||||
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize("net",[network_file_name])
 | 
			
		||||
@pytest.mark.parametrize("eqpt", [eqpt_library_name])
 | 
			
		||||
@pytest.mark.parametrize("serv",[service_file_name])
 | 
			
		||||
def test_disjunction(net,eqpt,serv):
 | 
			
		||||
    data = load_requests(serv,eqpt)
 | 
			
		||||
    data = load_requests(serv, eqpt, bidir=False)
 | 
			
		||||
    equipment = load_equipment(eqpt)
 | 
			
		||||
    network = load_network(net,equipment)
 | 
			
		||||
 | 
			
		||||
    # Build the network once using the default power defined in SI in eqpt config
 | 
			
		||||
    # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
 | 
			
		||||
    # spacing, f_min and f_max 
 | 
			
		||||
@@ -41,6 +41,7 @@ def test_disjunction(net,eqpt,serv):
 | 
			
		||||
    p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
 | 
			
		||||
        equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
 | 
			
		||||
    build_network(network, equipment, p_db, p_total_db)
 | 
			
		||||
    build_oms_list(network, equipment)
 | 
			
		||||
 | 
			
		||||
    rqs = requests_from_json(data, equipment)
 | 
			
		||||
    rqs = correct_route_list(network, rqs)
 | 
			
		||||
@@ -56,7 +57,7 @@ def test_disjunction(net,eqpt,serv):
 | 
			
		||||
        rqs_id_list = [r.request_id for r in rqs]
 | 
			
		||||
        p1 = pths[rqs_id_list.index(e[0])][1:-1]
 | 
			
		||||
        p2 = pths[rqs_id_list.index(e[1])][1:-1]
 | 
			
		||||
        if isdisjoint(p1,p2) + isdisjoint(p1,find_reversed_path(p2, network)) > 0:
 | 
			
		||||
        if isdisjoint(p1, p2) + isdisjoint(p1, find_reversed_path(p2)) > 0:
 | 
			
		||||
            test = False
 | 
			
		||||
            print(f'Computed path (roadms):{[e.uid for e in p1  if isinstance(e, Roadm)]}\n')
 | 
			
		||||
            print(f'Computed path (roadms):{[e.uid for e in p2  if isinstance(e, Roadm)]}\n')
 | 
			
		||||
@@ -68,7 +69,7 @@ def test_disjunction(net,eqpt,serv):
 | 
			
		||||
@pytest.mark.parametrize("eqpt", [eqpt_library_name])
 | 
			
		||||
@pytest.mark.parametrize("serv",[service_file_name])
 | 
			
		||||
def test_does_not_loop_back(net,eqpt,serv):
 | 
			
		||||
    data = load_requests(serv,eqpt)
 | 
			
		||||
    data = load_requests(serv, eqpt, bidir=False)
 | 
			
		||||
    equipment = load_equipment(eqpt)
 | 
			
		||||
    network = load_network(net,equipment)
 | 
			
		||||
 | 
			
		||||
@@ -80,6 +81,7 @@ def test_does_not_loop_back(net,eqpt,serv):
 | 
			
		||||
    p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
 | 
			
		||||
        equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
 | 
			
		||||
    build_network(network, equipment, p_db, p_total_db)
 | 
			
		||||
    build_oms_list(network, equipment)
 | 
			
		||||
 | 
			
		||||
    rqs = requests_from_json(data, equipment)
 | 
			
		||||
    rqs = correct_route_list(network, rqs)
 | 
			
		||||
@@ -104,4 +106,4 @@ def test_does_not_loop_back(net,eqpt,serv):
 | 
			
		||||
 | 
			
		||||
    # check that the total agregated bandwidth is the same after aggregation
 | 
			
		||||
 | 
			
		||||
    # 
 | 
			
		||||
    #
 | 
			
		||||
 
 | 
			
		||||
@@ -3,43 +3,57 @@
 | 
			
		||||
# @Author: Esther Le Rouzic
 | 
			
		||||
# @Date:   2018-06-15
 | 
			
		||||
 | 
			
		||||
from gnpy.core.elements import Edfa
 | 
			
		||||
import numpy as np
 | 
			
		||||
""" Adding tests to check the parser non regression
 | 
			
		||||
    convention of naming of test files:
 | 
			
		||||
    - ..._expected.json for the reference output
 | 
			
		||||
    tests:
 | 
			
		||||
    - generation of topology json
 | 
			
		||||
    - reading of Eqpt sheet w and W/ power mode
 | 
			
		||||
    - consistency of autodesign
 | 
			
		||||
    - generation of service list based on service sheet
 | 
			
		||||
    - writing of results in csv
 | 
			
		||||
    - writing of results in json (same keys)
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from json import load
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from os import unlink
 | 
			
		||||
from pandas import read_csv
 | 
			
		||||
import pytest
 | 
			
		||||
from gnpy.core import network_from_json
 | 
			
		||||
from gnpy.core.elements import Transceiver, Fiber, Edfa
 | 
			
		||||
from gnpy.core.utils import lin2db, db2lin
 | 
			
		||||
from gnpy.core.info import SpectralInformation, Channel, Power
 | 
			
		||||
from tests.compare import compare_networks, compare_services
 | 
			
		||||
from copy import deepcopy
 | 
			
		||||
from gnpy.core.utils import lin2db
 | 
			
		||||
from gnpy.core.network import save_network, build_network
 | 
			
		||||
from gnpy.core.convert import convert_file
 | 
			
		||||
from gnpy.core.service_sheet import convert_service_sheet
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import filecmp
 | 
			
		||||
from os import unlink
 | 
			
		||||
from gnpy.core.equipment import load_equipment, automatic_nch
 | 
			
		||||
from gnpy.core.network import load_network
 | 
			
		||||
from gnpy.core.request import (jsontocsv, requests_aggregation,
 | 
			
		||||
                               compute_path_dsjctn, Result_element)
 | 
			
		||||
from gnpy.core.spectrum_assignment import build_oms_list, pth_assign_spectrum
 | 
			
		||||
from gnpy.core.exceptions import ServiceError
 | 
			
		||||
from examples.path_requests_run import (requests_from_json, disjunctions_from_json,
 | 
			
		||||
                                        correct_route_list, correct_disjn,
 | 
			
		||||
                                        compute_path_with_disjunction)
 | 
			
		||||
 | 
			
		||||
TEST_DIR = Path(__file__).parent
 | 
			
		||||
DATA_DIR = TEST_DIR / 'data'
 | 
			
		||||
eqpt_filename = DATA_DIR / 'eqpt_config.json'
 | 
			
		||||
 | 
			
		||||
# adding tests to check the parser non regression
 | 
			
		||||
# convention of naming of test files:
 | 
			
		||||
#
 | 
			
		||||
#    - ..._expected.json for the reference output
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('xls_input,expected_json_output', {
 | 
			
		||||
    DATA_DIR / 'excelTestFile.xls':             DATA_DIR / 'excelTestFile_expected.json',
 | 
			
		||||
    DATA_DIR / 'CORONET_Global_Topology.xls':   DATA_DIR / 'CORONET_Global_Topology_expected.json',
 | 
			
		||||
    DATA_DIR / 'meshTopologyExampleV2.xls':     DATA_DIR / 'meshTopologyExampleV2_expected.json',
 | 
			
		||||
    DATA_DIR / 'meshTopologyExampleV2Eqpt.xls': DATA_DIR / 'meshTopologyExampleV2Eqpt_expected.json',
 | 
			
		||||
 }.items())
 | 
			
		||||
    DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_expected.json',
 | 
			
		||||
    }.items())
 | 
			
		||||
def test_excel_json_generation(xls_input, expected_json_output):
 | 
			
		||||
    """ tests generation of topology json
 | 
			
		||||
    """
 | 
			
		||||
    convert_file(xls_input)
 | 
			
		||||
 | 
			
		||||
    actual_json_output = xls_input.with_suffix('.json')
 | 
			
		||||
    with open(actual_json_output, encoding='utf-8') as f:
 | 
			
		||||
        actual = load(f)
 | 
			
		||||
    #unlink(actual_json_output)
 | 
			
		||||
    unlink(actual_json_output)
 | 
			
		||||
 | 
			
		||||
    with open(expected_json_output, encoding='utf-8') as f:
 | 
			
		||||
        expected = load(f)
 | 
			
		||||
@@ -52,16 +66,97 @@ def test_excel_json_generation(xls_input, expected_json_output):
 | 
			
		||||
    assert not results.connections.extra
 | 
			
		||||
    assert not results.connections.different
 | 
			
		||||
 | 
			
		||||
# assume json entries
 | 
			
		||||
# test that the build network gives correct results
 | 
			
		||||
# TODO !!
 | 
			
		||||
# assume xls entries
 | 
			
		||||
# test that the build network gives correct results in gain mode
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('xls_input,expected_json_output',
 | 
			
		||||
                         {DATA_DIR / 'CORONET_Global_Topology.xls':\
 | 
			
		||||
                          DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json',
 | 
			
		||||
                          DATA_DIR / 'testTopology.xls':\
 | 
			
		||||
                          DATA_DIR / 'testTopology_auto_design_expected.json',
 | 
			
		||||
                         }.items())
 | 
			
		||||
def test_auto_design_generation_fromxlsgainmode(xls_input, expected_json_output):
 | 
			
		||||
    """ tests generation of topology json
 | 
			
		||||
        test that the build network gives correct results in gain mode
 | 
			
		||||
    """
 | 
			
		||||
    equipment = load_equipment(eqpt_filename)
 | 
			
		||||
    network = load_network(xls_input, equipment)
 | 
			
		||||
    # in order to test the Eqpt sheet and load gain target,
 | 
			
		||||
    # change the power-mode to False (to be in gain mode)
 | 
			
		||||
    equipment['Span']['default'].power_mode = False
 | 
			
		||||
    # Build the network once using the default power defined in SI in eqpt config
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    save_network(xls_input, network)
 | 
			
		||||
 | 
			
		||||
    actual_json_output = f'{str(xls_input)[0:len(str(xls_input))-4]}_auto_design.json'
 | 
			
		||||
 | 
			
		||||
    with open(actual_json_output, encoding='utf-8') as f:
 | 
			
		||||
        actual = load(f)
 | 
			
		||||
    unlink(actual_json_output)
 | 
			
		||||
 | 
			
		||||
    with open(expected_json_output, encoding='utf-8') as f:
 | 
			
		||||
        expected = load(f)
 | 
			
		||||
 | 
			
		||||
    results = compare_networks(expected, actual)
 | 
			
		||||
    assert not results.elements.missing
 | 
			
		||||
    assert not results.elements.extra
 | 
			
		||||
    assert not results.elements.different
 | 
			
		||||
    assert not results.connections.missing
 | 
			
		||||
    assert not results.connections.extra
 | 
			
		||||
    assert not results.connections.different
 | 
			
		||||
 | 
			
		||||
#test that autodesign creates same file as an input file already autodesigned
 | 
			
		||||
@pytest.mark.parametrize('json_input,expected_json_output',
 | 
			
		||||
                         {DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json':\
 | 
			
		||||
                          DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json',
 | 
			
		||||
                          DATA_DIR / 'testTopology_auto_design_expected.json':\
 | 
			
		||||
                          DATA_DIR / 'testTopology_auto_design_expected.json',
 | 
			
		||||
                         }.items())
 | 
			
		||||
def test_auto_design_generation_fromjson(json_input, expected_json_output):
 | 
			
		||||
    """test that autodesign creates same file as an input file already autodesigned
 | 
			
		||||
    """
 | 
			
		||||
    equipment = load_equipment(eqpt_filename)
 | 
			
		||||
    network = load_network(json_input, equipment)
 | 
			
		||||
    # in order to test the Eqpt sheet and load gain target,
 | 
			
		||||
    # change the power-mode to False (to be in gain mode)
 | 
			
		||||
    equipment['Span']['default'].power_mode = False
 | 
			
		||||
    # Build the network once using the default power defined in SI in eqpt config
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
    save_network(json_input, network)
 | 
			
		||||
 | 
			
		||||
    actual_json_output = f'{str(json_input)[0:len(str(json_input))-5]}_auto_design.json'
 | 
			
		||||
 | 
			
		||||
    with open(actual_json_output, encoding='utf-8') as f:
 | 
			
		||||
        actual = load(f)
 | 
			
		||||
    unlink(actual_json_output)
 | 
			
		||||
 | 
			
		||||
    with open(expected_json_output, encoding='utf-8') as f:
 | 
			
		||||
        expected = load(f)
 | 
			
		||||
 | 
			
		||||
    results = compare_networks(expected, actual)
 | 
			
		||||
    assert not results.elements.missing
 | 
			
		||||
    assert not results.elements.extra
 | 
			
		||||
    assert not results.elements.different
 | 
			
		||||
    assert not results.connections.missing
 | 
			
		||||
    assert not results.connections.extra
 | 
			
		||||
    assert not results.connections.different
 | 
			
		||||
 | 
			
		||||
# test services creation
 | 
			
		||||
@pytest.mark.parametrize('xls_input,expected_json_output', {
 | 
			
		||||
    DATA_DIR / 'excelTestFile.xls':             DATA_DIR / 'excelTestFile_services_expected.json',
 | 
			
		||||
    DATA_DIR / 'meshTopologyExampleV2.xls':     DATA_DIR / 'meshTopologyExampleV2_services_expected.json',
 | 
			
		||||
    DATA_DIR / 'meshTopologyExampleV2Eqpt.xls': DATA_DIR / 'meshTopologyExampleV2Eqpt_services_expected.json',
 | 
			
		||||
}.items())
 | 
			
		||||
    DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_services_expected.json',
 | 
			
		||||
    DATA_DIR / 'testService.xls':     DATA_DIR / 'testService_services_expected.json'
 | 
			
		||||
    }.items())
 | 
			
		||||
def test_excel_service_json_generation(xls_input, expected_json_output):
 | 
			
		||||
    """ test services creation
 | 
			
		||||
    """
 | 
			
		||||
    convert_service_sheet(xls_input, eqpt_filename)
 | 
			
		||||
 | 
			
		||||
    actual_json_output = f'{str(xls_input)[:-4]}_services.json'
 | 
			
		||||
@@ -79,3 +174,173 @@ def test_excel_service_json_generation(xls_input, expected_json_output):
 | 
			
		||||
    assert not results.synchronizations.missing
 | 
			
		||||
    assert not results.synchronizations.extra
 | 
			
		||||
    assert not results.synchronizations.different
 | 
			
		||||
 | 
			
		||||
    # TODO verify that requested bandwidth is not zero !
 | 
			
		||||
 | 
			
		||||
# test xls answers creation
 | 
			
		||||
@pytest.mark.parametrize('json_input, csv_output', {
 | 
			
		||||
    DATA_DIR / 'testTopology_response.json':     DATA_DIR / 'testTopology_response',
 | 
			
		||||
}.items())
 | 
			
		||||
def test_csv_response_generation(json_input, csv_output):
 | 
			
		||||
    """ tests if generated csv is consistant with expected generation
 | 
			
		||||
        same columns (order not important)
 | 
			
		||||
    """
 | 
			
		||||
    with open(json_input) as jsonfile:
 | 
			
		||||
        json_data = load(jsonfile)
 | 
			
		||||
    equipment = load_equipment(eqpt_filename)
 | 
			
		||||
    csv_filename = str(csv_output)+'.csv'
 | 
			
		||||
    with open(csv_filename, 'w', encoding='utf-8') as fcsv:
 | 
			
		||||
        jsontocsv(json_data, equipment, fcsv)
 | 
			
		||||
 | 
			
		||||
    expected_csv_filename = str(csv_output)+'_expected.csv'
 | 
			
		||||
 | 
			
		||||
    # expected header
 | 
			
		||||
    # csv_header = \
 | 
			
		||||
    # [
 | 
			
		||||
    #  'response-id',
 | 
			
		||||
    #  'source',
 | 
			
		||||
    #  'destination',
 | 
			
		||||
    #  'path_bandwidth',
 | 
			
		||||
    #  'Pass?',
 | 
			
		||||
    #  'nb of tsp pairs',
 | 
			
		||||
    #  'total cost',
 | 
			
		||||
    #  'transponder-type',
 | 
			
		||||
    #  'transponder-mode',
 | 
			
		||||
    #  'OSNR-0.1nm',
 | 
			
		||||
    #  'SNR-0.1nm',
 | 
			
		||||
    #  'SNR-bandwidth',
 | 
			
		||||
    #  'baud rate (Gbaud)',
 | 
			
		||||
    #  'input power (dBm)',
 | 
			
		||||
    #  'path',
 | 
			
		||||
    #  'spectrum (N,M)',
 | 
			
		||||
    #  'reversed path OSNR-0.1nm',
 | 
			
		||||
    #  'reversed path SNR-0.1nm',
 | 
			
		||||
    #  'reversed path SNR-bandwidth'
 | 
			
		||||
    # ]
 | 
			
		||||
 | 
			
		||||
    resp = read_csv(csv_filename)
 | 
			
		||||
    print(resp)
 | 
			
		||||
    unlink(csv_filename)
 | 
			
		||||
    expected_resp = read_csv(expected_csv_filename)
 | 
			
		||||
    print(expected_resp)
 | 
			
		||||
    resp_header = list(resp.head(0))
 | 
			
		||||
    expected_resp_header = list(expected_resp.head(0))
 | 
			
		||||
    # check that headers are the same
 | 
			
		||||
    resp_header.sort()
 | 
			
		||||
    expected_resp_header.sort()
 | 
			
		||||
    print('headers are differents')
 | 
			
		||||
    print(resp_header)
 | 
			
		||||
    print(expected_resp_header)
 | 
			
		||||
    assert resp_header == expected_resp_header
 | 
			
		||||
 | 
			
		||||
    # for each header checks that the output are as expected
 | 
			
		||||
    resp.sort_values(by=['response-id'])
 | 
			
		||||
    expected_resp.sort_values(by=['response-id'])
 | 
			
		||||
 | 
			
		||||
    for column in expected_resp:
 | 
			
		||||
        assert list(resp[column].fillna('')) == list(expected_resp[column].fillna(''))
 | 
			
		||||
        print('results are different')
 | 
			
		||||
        print(list(resp[column]))
 | 
			
		||||
        print(list(expected_resp[column]))
 | 
			
		||||
        print(type(list(resp[column])[-1]))
 | 
			
		||||
 | 
			
		||||
def compare_response(exp_resp, act_resp):
 | 
			
		||||
    """ False if the keys are different in the nested dicts as well
 | 
			
		||||
    """
 | 
			
		||||
    print(exp_resp)
 | 
			
		||||
    print(act_resp)
 | 
			
		||||
    test = True
 | 
			
		||||
    for key in act_resp.keys():
 | 
			
		||||
        if not key in exp_resp.keys():
 | 
			
		||||
            print(f'{key} is not expected')
 | 
			
		||||
            return False
 | 
			
		||||
        if isinstance(act_resp[key], dict):
 | 
			
		||||
            test = compare_response(exp_resp[key], act_resp[key])
 | 
			
		||||
    if test:
 | 
			
		||||
        for key in exp_resp.keys():
 | 
			
		||||
            if not key in act_resp.keys():
 | 
			
		||||
                print(f'{key} is expected')
 | 
			
		||||
                return False
 | 
			
		||||
            if isinstance(exp_resp[key], dict):
 | 
			
		||||
                test = compare_response(exp_resp[key], act_resp[key])
 | 
			
		||||
 | 
			
		||||
    # at this point exp_resp and act_resp have the same keys. Check if their values are the same
 | 
			
		||||
    for key in act_resp.keys():
 | 
			
		||||
        if not isinstance(act_resp[key], dict):
 | 
			
		||||
            if exp_resp[key] != act_resp[key]:
 | 
			
		||||
                print(f'expected value :{exp_resp[key]}\n actual value: {act_resp[key]}')
 | 
			
		||||
                return False
 | 
			
		||||
    return test
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# test json answers creation
 | 
			
		||||
@pytest.mark.parametrize('xls_input, expected_response_file', {
 | 
			
		||||
    DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_response.json',
 | 
			
		||||
}.items())
 | 
			
		||||
def test_json_response_generation(xls_input, expected_response_file):
 | 
			
		||||
    """ tests if json response is correctly generated for all combinations of requests
 | 
			
		||||
    """
 | 
			
		||||
    data = convert_service_sheet(xls_input, eqpt_filename)
 | 
			
		||||
    # change one of the request with bidir option to cover bidir case as well
 | 
			
		||||
    data['path-request'][2]['bidirectional'] = True
 | 
			
		||||
 | 
			
		||||
    equipment = load_equipment(eqpt_filename)
 | 
			
		||||
    network = load_network(xls_input, equipment)
 | 
			
		||||
    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)
 | 
			
		||||
    oms_list = build_oms_list(network, equipment)
 | 
			
		||||
    rqs = requests_from_json(data, equipment)
 | 
			
		||||
    rqs = correct_route_list(network, rqs)
 | 
			
		||||
    dsjn = disjunctions_from_json(data)
 | 
			
		||||
    dsjn = correct_disjn(dsjn)
 | 
			
		||||
    rqs, dsjn = requests_aggregation(rqs, dsjn)
 | 
			
		||||
    pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
 | 
			
		||||
    propagatedpths, reversed_pths, reversed_propagatedpths = \
 | 
			
		||||
        compute_path_with_disjunction(network, equipment, rqs, pths)
 | 
			
		||||
    pth_assign_spectrum(pths, rqs, oms_list, reversed_pths)
 | 
			
		||||
 | 
			
		||||
    result = []
 | 
			
		||||
    for i, pth in enumerate(propagatedpths):
 | 
			
		||||
        # test ServiceError handling : when M is zero at this point, the
 | 
			
		||||
        # json result should not be created if there is no blocking reason
 | 
			
		||||
        if i == 1:
 | 
			
		||||
            my_rq = deepcopy(rqs[i])
 | 
			
		||||
            my_rq.M = 0
 | 
			
		||||
            with pytest.raises(ServiceError):
 | 
			
		||||
                Result_element(my_rq, pth, reversed_propagatedpths[i]).json
 | 
			
		||||
 | 
			
		||||
            my_rq.blocking_reason = 'NO_SPECTRUM'
 | 
			
		||||
            Result_element(my_rq, pth, reversed_propagatedpths[i]).json
 | 
			
		||||
 | 
			
		||||
        result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i]))
 | 
			
		||||
 | 
			
		||||
    temp = {
 | 
			
		||||
        'response': [n.json for n in result]
 | 
			
		||||
    }
 | 
			
		||||
    # load expected result and compare keys and values
 | 
			
		||||
 | 
			
		||||
    with open(expected_response_file) as jsonfile:
 | 
			
		||||
        expected = load(jsonfile)
 | 
			
		||||
        # since we changes bidir attribute of request#2, need to add the corresponding
 | 
			
		||||
        # metric in response
 | 
			
		||||
 | 
			
		||||
    for i, response in enumerate(temp['response']):
 | 
			
		||||
        if i == 2:
 | 
			
		||||
            # compare response must be False because z-a metric is missing
 | 
			
		||||
            # (request with bidir option to cover bidir case)
 | 
			
		||||
            assert not compare_response(expected['response'][i], response)
 | 
			
		||||
            print(f'response {response["response-id"]} should not match')
 | 
			
		||||
            expected['response'][2]['path-properties']['z-a-path-metric'] = [
 | 
			
		||||
                {'metric-type': 'SNR-bandwidth', 'accumulative-value': 22.809999999999999},
 | 
			
		||||
                {'metric-type': 'SNR-0.1nm', 'accumulative-value': 26.890000000000001},
 | 
			
		||||
                {'metric-type': 'OSNR-bandwidth', 'accumulative-value': 26.239999999999998},
 | 
			
		||||
                {'metric-type': 'OSNR-0.1nm', 'accumulative-value': 30.32},
 | 
			
		||||
                {'metric-type': 'reference_power', 'accumulative-value': 0.0012589254117941673},
 | 
			
		||||
                {'metric-type': 'path_bandwidth', 'accumulative-value': 60000000000.0}]
 | 
			
		||||
            # test should be OK now
 | 
			
		||||
        else:
 | 
			
		||||
            assert compare_response(expected['response'][i], response)
 | 
			
		||||
            print(f'response {response["response-id"]} is not correct')
 | 
			
		||||
 
 | 
			
		||||
@@ -18,6 +18,8 @@ from numpy import mean
 | 
			
		||||
 | 
			
		||||
#network_file_name = 'tests/test_network.json'
 | 
			
		||||
network_file_name = Path(__file__).parent.parent / 'tests/LinkforTest.json'
 | 
			
		||||
#TODO: note that this json entries has a weird topology since EDfa1 has a possible branch on a receiver B
 | 
			
		||||
# this might not pass future tests/ code updates
 | 
			
		||||
#network_file_name = Path(__file__).parent.parent / 'examples/edfa_example_network.json'
 | 
			
		||||
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
 | 
			
		||||
 | 
			
		||||
@@ -47,12 +49,8 @@ def propagation(input_power, con_in, con_out,dest):
 | 
			
		||||
 | 
			
		||||
    p = input_power
 | 
			
		||||
    p = db2lin(p) * 1e-3
 | 
			
		||||
    spacing = 0.05 # THz
 | 
			
		||||
    si = SpectralInformation() # SI units: W, Hz
 | 
			
		||||
    si = si.update(carriers=[
 | 
			
		||||
        Channel(f, (191.3 + spacing * f) * 1e12, 32e9, 0.15, Power(p, 0, 0))
 | 
			
		||||
        for f in range(1,80)
 | 
			
		||||
    ])
 | 
			
		||||
    spacing = 50e9 # THz
 | 
			
		||||
    si = create_input_spectral_information(191.3e12, 191.3e12+79*spacing, 0.15, 32e9, p, spacing)
 | 
			
		||||
    source = next(transceivers[uid] for uid in transceivers if uid == 'trx A')
 | 
			
		||||
    sink = next(transceivers[uid] for uid in transceivers if uid == dest)
 | 
			
		||||
    path = dijkstra_path(network, source, sink)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										203
									
								
								tests/test_roadm_restrictions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								tests/test_roadm_restrictions.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,203 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# @Author: Esther Le Rouzic
 | 
			
		||||
# @Date:   2019-05-22
 | 
			
		||||
"""
 | 
			
		||||
@author: esther.lerouzic
 | 
			
		||||
checks that fused placed in amp type is correctly converted to a fused element instead of an edfa
 | 
			
		||||
and that no additional amp is added.
 | 
			
		||||
checks that restrictions in roadms are correctly applied during autodesign
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
import pytest
 | 
			
		||||
from gnpy.core.utils import lin2db, load_json
 | 
			
		||||
from gnpy.core.elements import Fused, Roadm, Edfa
 | 
			
		||||
from gnpy.core.equipment import load_equipment, Amp, automatic_nch
 | 
			
		||||
from gnpy.core.network import network_from_json, build_network
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEST_DIR = Path(__file__).parent
 | 
			
		||||
EQPT_LIBRARY_NAME = TEST_DIR / 'data/eqpt_config.json'
 | 
			
		||||
NETWORK_FILE_NAME = TEST_DIR / 'data/testTopology_expected.json'
 | 
			
		||||
# adding tests to check the roadm restrictions
 | 
			
		||||
 | 
			
		||||
# mark node_uid amps as fused for testing purpose
 | 
			
		||||
@pytest.mark.parametrize("node_uid", ['east edfa in Lannion_CAS to Stbrieuc'])
 | 
			
		||||
def test_no_amp_feature(node_uid):
 | 
			
		||||
    ''' Check that booster is not placed on a roadm if fused is specified
 | 
			
		||||
        test_parser covers partly this behaviour. This test should guaranty that the
 | 
			
		||||
        feature is preserved even if convert is changed
 | 
			
		||||
    '''
 | 
			
		||||
    equipment = load_equipment(EQPT_LIBRARY_NAME)
 | 
			
		||||
    json_network = load_json(NETWORK_FILE_NAME)
 | 
			
		||||
 | 
			
		||||
    for elem in json_network['elements']:
 | 
			
		||||
        if elem['uid'] == node_uid:
 | 
			
		||||
            #replace edfa node by a fused node in the topology
 | 
			
		||||
            elem['type'] = 'Fused'
 | 
			
		||||
            elem.pop('type_variety')
 | 
			
		||||
            elem.pop('operational')
 | 
			
		||||
            elem['params'] = {'loss': 0}
 | 
			
		||||
 | 
			
		||||
            next_node_uid = next(conn['to_node'] for conn in json_network['connections'] \
 | 
			
		||||
                                 if conn['from_node'] == node_uid)
 | 
			
		||||
            previous_node_uid = next(conn['from_node'] for conn in json_network['connections'] \
 | 
			
		||||
                                 if conn['to_node'] == node_uid)
 | 
			
		||||
 | 
			
		||||
    network = network_from_json(json_network, equipment)
 | 
			
		||||
    # Build the network once using the default power defined in SI in eqpt config
 | 
			
		||||
    # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
 | 
			
		||||
    # spacing, f_min and f_max
 | 
			
		||||
    p_db = equipment['SI']['default'].power_dbm
 | 
			
		||||
    p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
 | 
			
		||||
        equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
 | 
			
		||||
 | 
			
		||||
    build_network(network, equipment, p_db, p_total_db)
 | 
			
		||||
 | 
			
		||||
    node = next(nd for nd in network.nodes() if nd.uid == node_uid)
 | 
			
		||||
    next_node = next(network.successors(node))
 | 
			
		||||
    previous_node = next(network.predecessors(node))
 | 
			
		||||
 | 
			
		||||
    if not isinstance(node, Fused):
 | 
			
		||||
        raise AssertionError()
 | 
			
		||||
    if not node.params.loss == 0.0:
 | 
			
		||||
        raise AssertionError()
 | 
			
		||||
    if not next_node_uid == next_node.uid:
 | 
			
		||||
        raise AssertionError()
 | 
			
		||||
    if not previous_node_uid == previous_node.uid:
 | 
			
		||||
        raise AssertionError()
 | 
			
		||||
 | 
			
		||||
@pytest.fixture()
 | 
			
		||||
def equipment():
 | 
			
		||||
    """init transceiver class to access snr and osnr calculations"""
 | 
			
		||||
    equipment = load_equipment(EQPT_LIBRARY_NAME)
 | 
			
		||||
    # define some booster and preamps
 | 
			
		||||
    restrictions_list = [
 | 
			
		||||
        {
 | 
			
		||||
            'type_variety': 'booster_medium_gain',
 | 
			
		||||
            'type_def': 'variable_gain',
 | 
			
		||||
            'gain_flatmax': 25,
 | 
			
		||||
            'gain_min': 15,
 | 
			
		||||
            'p_max': 21,
 | 
			
		||||
            'nf_min': 5.8,
 | 
			
		||||
            'nf_max': 10,
 | 
			
		||||
            'out_voa_auto': False,
 | 
			
		||||
            'allowed_for_design': False
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            'type_variety': 'preamp_medium_gain',
 | 
			
		||||
            'type_def': 'variable_gain',
 | 
			
		||||
            'gain_flatmax': 26,
 | 
			
		||||
            'gain_min': 15,
 | 
			
		||||
            'p_max': 23,
 | 
			
		||||
            'nf_min': 6,
 | 
			
		||||
            'nf_max': 10,
 | 
			
		||||
            'out_voa_auto': False,
 | 
			
		||||
            'allowed_for_design': False
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            'type_variety': 'preamp_high_gain',
 | 
			
		||||
            'type_def': 'variable_gain',
 | 
			
		||||
            'gain_flatmax': 35,
 | 
			
		||||
            'gain_min': 25,
 | 
			
		||||
            'p_max': 21,
 | 
			
		||||
            'nf_min': 5.5,
 | 
			
		||||
            'nf_max': 7,
 | 
			
		||||
            'out_voa_auto': False,
 | 
			
		||||
            'allowed_for_design': False
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            'type_variety': 'preamp_low_gain',
 | 
			
		||||
            'type_def': 'variable_gain',
 | 
			
		||||
            'gain_flatmax': 16,
 | 
			
		||||
            'gain_min': 8,
 | 
			
		||||
            'p_max': 23,
 | 
			
		||||
            'nf_min': 6.5,
 | 
			
		||||
            'nf_max': 11,
 | 
			
		||||
            'out_voa_auto': False,
 | 
			
		||||
            'allowed_for_design': False
 | 
			
		||||
        }]
 | 
			
		||||
    # add them to the library
 | 
			
		||||
    for entry in restrictions_list:
 | 
			
		||||
        equipment['Edfa'][entry['type_variety']] = Amp.from_json(EQPT_LIBRARY_NAME, **entry)
 | 
			
		||||
    return equipment
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize("restrictions", [
 | 
			
		||||
    {
 | 
			
		||||
        'preamp_variety_list':[],
 | 
			
		||||
        'booster_variety_list':[]
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        'preamp_variety_list':[],
 | 
			
		||||
        'booster_variety_list':['booster_medium_gain']
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
        'preamp_variety_list':['preamp_medium_gain', 'preamp_high_gain', 'preamp_low_gain'],
 | 
			
		||||
        'booster_variety_list':[]
 | 
			
		||||
    }])
 | 
			
		||||
def test_restrictions(restrictions, equipment):
 | 
			
		||||
    ''' test that restriction is correctly applied if provided in eqpt_config and if no Edfa type
 | 
			
		||||
    were provided in the network json
 | 
			
		||||
    '''
 | 
			
		||||
    # add restrictions
 | 
			
		||||
    equipment['Roadm']['default'].restrictions = restrictions
 | 
			
		||||
    # build network
 | 
			
		||||
    json_network = load_json(NETWORK_FILE_NAME)
 | 
			
		||||
    network = network_from_json(json_network, equipment)
 | 
			
		||||
 | 
			
		||||
    amp_nodes_nobuild_uid = [nd.uid for nd in network.nodes() \
 | 
			
		||||
        if isinstance(nd, Edfa) and isinstance(next(network.predecessors(nd)), Roadm)]
 | 
			
		||||
    preamp_nodes_nobuild_uid = [nd.uid for nd in network.nodes() \
 | 
			
		||||
        if isinstance(nd, Edfa) and isinstance(next(network.successors(nd)), Roadm)]
 | 
			
		||||
    amp_nodes_nobuild = {nd.uid : nd for nd in network.nodes() \
 | 
			
		||||
        if isinstance(nd, Edfa) and isinstance(next(network.predecessors(nd)), Roadm)}
 | 
			
		||||
    preamp_nodes_nobuild = {nd.uid : nd for nd in network.nodes() \
 | 
			
		||||
        if isinstance(nd, Edfa) and isinstance(next(network.successors(nd)), Roadm)}
 | 
			
		||||
    # roadm dict with restrictions before build
 | 
			
		||||
    roadms = {nd.uid: nd for nd in network.nodes() if isinstance(nd, Roadm)}
 | 
			
		||||
    # Build the network once using the default power defined in SI in eqpt config
 | 
			
		||||
    # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
 | 
			
		||||
    # spacing, f_min and f_max
 | 
			
		||||
    p_db = equipment['SI']['default'].power_dbm
 | 
			
		||||
    p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
 | 
			
		||||
        equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
 | 
			
		||||
 | 
			
		||||
    build_network(network, equipment, p_db, p_total_db)
 | 
			
		||||
 | 
			
		||||
    amp_nodes = [nd for nd in network.nodes() \
 | 
			
		||||
        if isinstance(nd, Edfa) and isinstance(next(network.predecessors(nd)), Roadm)\
 | 
			
		||||
           and next(network.predecessors(nd)).restrictions['booster_variety_list']]
 | 
			
		||||
 | 
			
		||||
    preamp_nodes = [nd for nd in network.nodes() \
 | 
			
		||||
        if isinstance(nd, Edfa) and isinstance(next(network.successors(nd)), Roadm)\
 | 
			
		||||
           and next(network.successors(nd)).restrictions['preamp_variety_list']]
 | 
			
		||||
 | 
			
		||||
    # check that previously existing amp are not changed
 | 
			
		||||
    for amp in amp_nodes:
 | 
			
		||||
        if amp.uid in amp_nodes_nobuild_uid:
 | 
			
		||||
            print(amp.uid, amp.params.type_variety)
 | 
			
		||||
            if not amp.params.type_variety == amp_nodes_nobuild[amp.uid].params.type_variety:
 | 
			
		||||
                raise AssertionError()
 | 
			
		||||
    for amp in preamp_nodes:
 | 
			
		||||
        if amp.uid in preamp_nodes_nobuild_uid:
 | 
			
		||||
            if not amp.params.type_variety == preamp_nodes_nobuild[amp.uid].params.type_variety:
 | 
			
		||||
                raise AssertionError()
 | 
			
		||||
    # check that restrictions are correctly applied
 | 
			
		||||
    for amp in amp_nodes:
 | 
			
		||||
        if amp.uid not in amp_nodes_nobuild_uid:
 | 
			
		||||
            # and if roadm had no restrictions before build:
 | 
			
		||||
            if restrictions['booster_variety_list'] and \
 | 
			
		||||
               not roadms[next(network.predecessors(amp)).uid]\
 | 
			
		||||
                         .restrictions['booster_variety_list']:
 | 
			
		||||
                if not amp.params.type_variety in restrictions['booster_variety_list']:
 | 
			
		||||
 | 
			
		||||
                    raise AssertionError()
 | 
			
		||||
    for amp in preamp_nodes:
 | 
			
		||||
        if amp.uid not in preamp_nodes_nobuild_uid:
 | 
			
		||||
            if restrictions['preamp_variety_list'] and\
 | 
			
		||||
            not roadms[next(network.successors(amp)).uid].restrictions['preamp_variety_list']:
 | 
			
		||||
                if not amp.params.type_variety in restrictions['preamp_variety_list']:
 | 
			
		||||
                    raise AssertionError()
 | 
			
		||||
							
								
								
									
										49
									
								
								tests/test_science_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tests/test_science_utils.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
# -*- coding: utf-8 -*-
 | 
			
		||||
# @Author: Alessio Ferrari
 | 
			
		||||
"""
 | 
			
		||||
checks that RamanFiber propagates properly the spectral information. In this way, also the RamanSolver and the NliSolver
 | 
			
		||||
 are tested.
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
import json
 | 
			
		||||
from pandas import read_csv
 | 
			
		||||
from numpy.testing import assert_allclose
 | 
			
		||||
from gnpy.core.info import create_input_spectral_information
 | 
			
		||||
from gnpy.core.elements import RamanFiber
 | 
			
		||||
from gnpy.core.network import load_sim_params
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
TEST_DIR = Path(__file__).parent
 | 
			
		||||
 | 
			
		||||
def test_raman_fiber():
 | 
			
		||||
    """ Test the accuracy of propagating the RamanFiber.
 | 
			
		||||
    """
 | 
			
		||||
    # spectral information generation
 | 
			
		||||
    power = 1e-3
 | 
			
		||||
    with open(TEST_DIR / 'data' / 'eqpt_config.json', 'r') as file:
 | 
			
		||||
        eqpt_params = json.load(file)
 | 
			
		||||
    spectral_info_params = eqpt_params['SI'][0]
 | 
			
		||||
    spectral_info_params.pop('power_dbm')
 | 
			
		||||
    spectral_info_params.pop('power_range_db')
 | 
			
		||||
    spectral_info_params.pop('tx_osnr')
 | 
			
		||||
    spectral_info_params.pop('sys_margins')
 | 
			
		||||
    spectral_info_input = create_input_spectral_information(power=power, **spectral_info_params)
 | 
			
		||||
 | 
			
		||||
    # RamanFiber
 | 
			
		||||
    with open(TEST_DIR / 'data' / 'raman_fiber_config.json', 'r') as file:
 | 
			
		||||
        raman_fiber_params = json.load(file)
 | 
			
		||||
    sim_params = load_sim_params(TEST_DIR / 'data' / 'sim_params.json')
 | 
			
		||||
    fiber = RamanFiber(**raman_fiber_params)
 | 
			
		||||
    fiber.sim_params = sim_params
 | 
			
		||||
 | 
			
		||||
    # propagation
 | 
			
		||||
    spectral_info_out = fiber(spectral_info_input)
 | 
			
		||||
 | 
			
		||||
    p_signal = [carrier.power.signal for carrier in spectral_info_out.carriers]
 | 
			
		||||
    p_ase = [carrier.power.ase for carrier in spectral_info_out.carriers]
 | 
			
		||||
    p_nli = [carrier.power.nli for carrier in spectral_info_out.carriers]
 | 
			
		||||
 | 
			
		||||
    expected_results = read_csv(TEST_DIR / 'data' / 'expected_results_science_utils.csv')
 | 
			
		||||
    assert_allclose(p_signal, expected_results['signal'], rtol=1e-3)
 | 
			
		||||
    assert_allclose(p_ase, expected_results['ase'], rtol=1e-3)
 | 
			
		||||
    assert_allclose(p_nli, expected_results['nli'], rtol=1e-3)
 | 
			
		||||
		Reference in New Issue
	
	Block a user