mirror of
				https://github.com/Telecominfraproject/oopt-gnpy.git
				synced 2025-10-31 10:07:57 +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 | language: python | ||||||
|  | services: docker | ||||||
| python: | python: | ||||||
|   - "3.6" |   - "3.6" | ||||||
| # command to install dependencies |   - "3.7" | ||||||
| install: | install: skip | ||||||
|   - python setup.py install |  | ||||||
| # command to run tests |  | ||||||
| before_script: |  | ||||||
| script: | 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*) | (*in alphabetical order*) | ||||||
|  |  | ||||||
| - Alessio Ferrari (Politecnico di Torino) <alessio.ferrari@polito.it> | - 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> | - Brian Taylor (Facebook) <briantaylor@fb.com> | ||||||
| - David Boertjes (Ciena) <dboertje@ciena.com> | - David Boertjes (Ciena) <dboertje@ciena.com> | ||||||
|  | - Diego Landa (Facebook) <dlanda@fb.com> | ||||||
| - Esther Le Rouzic (Orange) <esther.lerouzic@orange.com> | - Esther Le Rouzic (Orange) <esther.lerouzic@orange.com> | ||||||
| - Gabriele Galimberti (Cisco) <ggalimbe@cisco.com> | - Gabriele Galimberti (Cisco) <ggalimbe@cisco.com> | ||||||
| - Gert Grammel (Juniper Networks) <ggrammel@juniper.net> | - Gert Grammel (Juniper Networks) <ggrammel@juniper.net> | ||||||
| - Gilad Goldfarb (Facebook) <giladg@fb.com> | - Gilad Goldfarb (Facebook) <giladg@fb.com> | ||||||
| - James Powell (Telecom Infra Project) <james.powell@telecominfraproject.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> | - Jeanluc Augé (Orange) <jeanluc.auge@orange.com> | ||||||
| - Jonas Mårtensson (RISE) <jonas.martensson@ri.se> | - Jonas Mårtensson (RISE) <jonas.martensson@ri.se> | ||||||
| - Mattia Cantono (Politecnico di Torino) <mattia.cantono@polito.it> | - 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> | - 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> | - Vittorio Curri (Politecnico di Torino) <vittorio.curri@polito.it> | ||||||
| - Xufeng Liu (Jabil) <xufeng_liu@jabil.com> | - 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 | ||||||
| ----------- | ----------- | ||||||
|  |  | ||||||
| Nodes sheet contains seven columns. | Nodes sheet contains nine columns. | ||||||
| Each line represents a 'node' (ROADM site or an in line amplifier site ILA):: | Each line represents a 'node' (ROADM site or an in line amplifier site ILA or a Fused):: | ||||||
|  |  | ||||||
|   City (Mandatory) ; State ; Country ; Region ; Latitude ; Longitude ; Type |   City (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. | - *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** | **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.  | - **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 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 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). | - **amp_gain** is not mandatory. It is the value to be set on the amplifier (in dB). | ||||||
|   If not filled, it will be determined with design rules in the convert.py file. |   If 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 | `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 | **`gnpy` is an open-source, community-developed library for building route | ||||||
| planning and optimization tools in real-world mesh optical networks.** | planning and optimization tools in real-world mesh optical networks.** | ||||||
|  |  | ||||||
| `gnpy <http://github.com/telecominfraproject/oopt-gnpy>`__ is: | `gnpy <http://github.com/telecominfraproject/oopt-gnpy>`__ is: | ||||||
|  | -------------------------------------------------------------- | ||||||
|  |  | ||||||
| - a sponsored project of the `OOPT/PSE <https://telecominfraproject.com/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_ | - a sponsored project of the `OOPT/PSE <https://telecominfraproject.com/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_ | ||||||
| - fully community-driven, fully open source library | - 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 | 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 | 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 `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 | 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. |    **Note**: `gnpy` supports Python 3 only. Python 2 is not supported. | ||||||
|    `gnpy` requires Python ≥3.6 |    `gnpy` requires Python ≥3.6 | ||||||
|  |  | ||||||
| @@ -62,10 +77,9 @@ How to Install | |||||||
| It is recommended that you use a "virtual environment" when installing `gnpy`. | It is recommended that you use a "virtual environment" when installing `gnpy`. | ||||||
| Do not install `gnpy` on your system Python. | Do not install `gnpy` on your system Python. | ||||||
|  |  | ||||||
| We recommend the use of the Anaconda Python distribution | We recommend the use of the `Anaconda Python distribution <https://www.anaconda.com/download>`_ which comes with many scientific computing | ||||||
| (https://www.anaconda.com/download) which comes with many scientific computing |  | ||||||
| dependencies pre-installed. Anaconda creates a base "virtual environment" for | 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: | environments" yourself (see: | ||||||
| https://conda.io/docs/user-guide/tasks/manage-environments.html) | 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 |     $ python setup.py install                                    # install | ||||||
|  |  | ||||||
| To test that `gnpy` was successfully installed, you can run this command. If it | 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`. | `gnpy`. | ||||||
|  |  | ||||||
| .. code-block:: shell | .. code-block:: shell | ||||||
|  |  | ||||||
|     $ python -c 'import gnpy' # attempt to import gnpy |     $ python -c 'import gnpy' # attempt to import gnpy | ||||||
|  |  | ||||||
|     $ cd oopt-gnpy |  | ||||||
|     $ pytest                  # run tests |     $ pytest                  # run tests | ||||||
|  |  | ||||||
| Instructions for First Use | Instructions for First Use | ||||||
| @@ -130,20 +143,18 @@ It ships with a number of example programs. Release versions will ship with | |||||||
| fully-functional programs. | fully-functional programs. | ||||||
|  |  | ||||||
|     **Note**: *If you are a network operator or involved in route planning and |     **Note**: *If you are a network operator or involved in route planning and | ||||||
|     optimization for your organization, please contact project maintainer James |     optimization for your organization, please contact project maintainer Jan | ||||||
|     Powell <james.powell@telecominfraproject>. gnpy is looking for users with |     Kundrát <jan.kundrat@telecominfraproject.com>. gnpy is looking for users with | ||||||
|     specific, delineated use cases to drive requirements for future |     specific, delineated use cases to drive requirements for future | ||||||
|     development.* |     development.* | ||||||
|  |  | ||||||
| **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.* | .. image:: https://telecominfraproject.github.io/oopt-gnpy/docs/images/transmission_main_example.svg | ||||||
|  |    :width: 100% | ||||||
| .. code-block:: shell |    :align: left | ||||||
|     $ pwd |    :alt: Running a simple simulation example | ||||||
|     /path/to/oopt-gnpy |    :target: https://asciinema.org/a/252295 | ||||||
|     $ cd examples |  | ||||||
|     $ python transmission_main_example.py |  | ||||||
|  |  | ||||||
| By default, this script operates on a single span network defined in | By default, this script operates on a single span network defined in | ||||||
| `examples/edfa_example_network.json <examples/edfa_example_network.json>`_ | `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 | example, to use the CORONET Global network defined in | ||||||
| `examples/CORONET_Global_Topology.json <examples/CORONET_Global_Topology.json>`_: | `examples/CORONET_Global_Topology.json <examples/CORONET_Global_Topology.json>`_: | ||||||
|  |  | ||||||
| .. code-block:: shell | .. code-block:: shell-session | ||||||
|  |  | ||||||
|     $ cd examples |     $ ./examples/transmission_main_example.py examples/CORONET_Global_Topology.json | ||||||
|     $ python transmission_main_example.py CORONET_Global_Topology.json |  | ||||||
|  |  | ||||||
| It is also possible to use an Excel file input (for example | It is also possible to use an Excel file input (for example | ||||||
| `examples/CORONET_Global_Topology.xls <examples/CORONET_Global_Topology.xls>`_). | `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 | 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: | 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. | 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` | 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. | 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: | For all amplifier models: | ||||||
|  |  | ||||||
| +----------------------+-----------+-----------------------------------------+ | +------------------------+-----------+-----------------------------------------+ | ||||||
| | field                |   type    | description                             | | | field                  |   type    | description                             | | ||||||
| +======================+===========+=========================================+ | +========================+===========+=========================================+ | ||||||
| | `type_variety`       | (string)  | a unique name to ID the amplifier in the| | | ``type_variety``       | (string)  | a unique name to ID the amplifier in the| | ||||||
| |                      |           | JSON/Excel template topology input file | | |                        |           | JSON/Excel template topology input file | | ||||||
| +----------------------+-----------+-----------------------------------------+ | +------------------------+-----------+-----------------------------------------+ | ||||||
| | `out_voa_auto`       | (boolean) | auto_design feature to optimize the     | | | ``out_voa_auto``       | (boolean) | auto_design feature to optimize the     | | ||||||
| |                      |           | amplifier output VOA. If true, output   | | |                        |           | amplifier output VOA. If true, output   | | ||||||
| |                      |           | VOA is present and will be used to push | | |                        |           | VOA is present and will be used to push | | ||||||
| |                      |           | amplifier gain to its maximum, within   | | |                        |           | amplifier gain to its maximum, within   | | ||||||
| |                      |           | EOL power margins.                      | | |                        |           | EOL power margins.                      | | ||||||
| +----------------------+-----------+-----------------------------------------+ | +------------------------+-----------+-----------------------------------------+ | ||||||
| | `allowed_for_design` | (boolean) | If false, the amplifier will not be     | | | ``allowed_for_design`` | (boolean) | If false, the amplifier will not be     | | ||||||
| |                      |           | picked by auto-design but it can still  | | |                        |           | picked by auto-design but it can still  | | ||||||
| |                      |           | be used as a manual input (from JSON or | | |                        |           | be used as a manual input (from JSON or | | ||||||
| |                      |           | Excel template topology files.)         | | |                        |           | 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: | 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                             | | | 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   | | |                      |           | JSON or Excel template topology input   | | ||||||
| |                      |           | file                                    | | |                      |           | 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 | 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 | 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 | 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                             | | | 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     | | |                      |           | the JSON or Excel template topology     | | ||||||
| |                      |           | input file                              | | |                      |           | 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  | | |                      |           | transponder. New modes can be added at  | | ||||||
| |                      |           | will by the user. The modes are specific| | |                      |           | will by the user. The modes are specific| | ||||||
| |                      |           | to each transponder type_variety.       | | |                      |           | to each transponder type_variety.       | | ||||||
| @@ -257,142 +267,142 @@ The modes are defined as follows: | |||||||
| +----------------------+-----------+-----------------------------------------+ | +----------------------+-----------+-----------------------------------------+ | ||||||
| | field                | type      | description                             | | | 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. | Simulation parameters are defined as follows. | ||||||
|  |  | ||||||
| Auto-design automatically creates EDFA amplifier network elements when they are | 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 | 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` | functionality can be manually and locally deactivated by introducing a ``Fused`` | ||||||
| network element after a `Fiber` or a `Roadm` that doesn't need amplification. | 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 | 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 | 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 | For amplifiers defined in the topology JSON input but whose ``gain = 0`` | ||||||
| (placeholder), auto-design will set its gain automatically: see `power_mode` in | (placeholder), auto-design will set its gain automatically: see ``power_mode`` in | ||||||
| the `Spans` library to find out how the gain is calculated. | 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 | 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 | in later releases) and the user can only modify the value of existing | ||||||
| parameters: | parameters: | ||||||
|  |  | ||||||
| +------------------------+-----------+---------------------------------------------+ | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
| | field                  | type      | description                                 | | | field                               | type      | description                                 | | ||||||
| +========================+===========+=============================================+ | +=====================================+===========+=============================================+ | ||||||
| | `power_mode`           | (boolean) | If false, gain mode. Auto-design sets       | | | ``power_mode``                      | (boolean) | If false, gain mode. Auto-design sets       | | ||||||
| |                        |           | amplifier gain = preceding span loss,      | | |                                     |           | amplifier gain = preceding span loss,       | | ||||||
| |                        |           | unless the amplifier exists and its         | | |                                     |           | unless the amplifier exists and its         | | ||||||
| |                        |           | gain > 0 in the topology input json.        | | |                                     |           | gain > 0 in the topology input JSON.        | | ||||||
| |                        |           | If true, power mode (recommended for        | | |                                     |           | If true, power mode (recommended for        | | ||||||
| |                        |           | auto-design and power sweep.)               | | |                                     |           | auto-design and power sweep.)               | | ||||||
| |                        |           | Auto-design sets amplifier power            | | |                                     |           | Auto-design sets amplifier power            | | ||||||
| |                        |           | according to delta_power_range. If the      | | |                                     |           | according to delta_power_range. If the      | | ||||||
| |                        |           | amplifier exists with gain > 0 in the       | | |                                     |           | amplifier exists with gain > 0 in the       | | ||||||
| |                        |           | topology json input, then its gain is       | | |                                     |           | topology JSON input, then its gain is       | | ||||||
| |                        |           | translated into a power target/channel.     | | |                                     |           | translated into a power target/channel.     | | ||||||
| |                        |           | Moreover, when performing a power sweep     | | |                                     |           | Moreover, when performing a power sweep     | | ||||||
| |                        |           | (see power_range_db in the SI               | | |                                     |           | (see ``power_range_db`` in the SI           | | ||||||
| |                        |           | configuration library) the power sweep      | | |                                     |           | configuration library) the power sweep      | | ||||||
| |                        |           | is performed w/r/t this power target,       | | |                                     |           | is performed w/r/t this power target,       | | ||||||
| |                        |           | regardless of preceding amplifiers         | | |                                     |           | regardless of preceding amplifiers          | | ||||||
| |                        |           | power saturation/limitations.               | | |                                     |           | power saturation/limitations.               | | ||||||
| +------------------------+-----------+---------------------------------------------+ | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
| | `delta_power_range_db` | (number)  | Auto-design only, power-mode                | | | ``delta_power_range_db``            | (number)  | Auto-design only, power-mode                | | ||||||
| |                        |           | only. Specifies the [min, max, step]        | | |                                     |           | only. Specifies the [min, max, step]        | | ||||||
| |                        |           | power excursion/span. It is a relative      | | |                                     |           | power excursion/span. It is a relative      | | ||||||
| |                        |           | power excursion w/r/t the                   | | |                                     |           | power excursion w/r/t the                   | | ||||||
| |                        |           | power_dbm + power_range_db                  | | |                                     |           | power_dbm + power_range_db                  | | ||||||
| |                        |           | (power sweep if applicable) defined in      | | |                                     |           | (power sweep if applicable) defined in      | | ||||||
| |                        |           | the SI configuration library. This          | | |                                     |           | the SI configuration library. This          | | ||||||
| |                        |           | relative power excursion is = 1/3 of        | | |                                     |           | relative power excursion is = 1/3 of        | | ||||||
| |                        |           | the span loss difference with the           | | |                                     |           | the span loss difference with the           | | ||||||
| |                        |           | reference 20 dB span. The 1/3 slope is      | | |                                     |           | reference 20 dB span. The 1/3 slope is      | | ||||||
| |                        |           | derived from the GN model equations.        | | |                                     |           | derived from the GN model equations.        | | ||||||
| |                        |           | For example, a 23 dB span loss will be      | | |                                     |           | For example, a 23 dB span loss will be      | | ||||||
| |                        |           | set to 1 dB more power than a 20 dB         | | |                                     |           | set to 1 dB more power than a 20 dB         | | ||||||
| |                        |           | span loss. The 20 dB reference spans        | | |                                     |           | span loss. The 20 dB reference spans        | | ||||||
| |                        |           | will *always* be set to                     | | |                                     |           | will *always* be set to                     | | ||||||
| |                        |           | power = power_dbm + power_range_db.         | | |                                     |           | power = power_dbm + power_range_db.         | | ||||||
| |                        |           | To configure the same power in all          | | |                                     |           | To configure the same power in all          | | ||||||
| |                        |           | spans, use `[0, 0, 0]`. All spans will      | | |                                     |           | spans, use `[0, 0, 0]`. All spans will      | | ||||||
| |                        |           | be set to                                   | | |                                     |           | be set to                                   | | ||||||
| |                        |           | power = power_dbm + power_range_db.         | | |                                     |           | power = power_dbm + power_range_db.         | | ||||||
| |                        |           | To configure the same power in all spans    | | |                                     |           | To configure the same power in all spans    | | ||||||
| |                        |           | and 3 dB more power just for the longest    | | |                                     |           | and 3 dB more power just for the longest    | | ||||||
| |                        |           | spans: `[0, 3, 3]`. The longest spans are   | | |                                     |           | spans: `[0, 3, 3]`. The longest spans are   | | ||||||
| |                        |           | set to                                      | | |                                     |           | set to                                      | | ||||||
| |                        |           | power = power_dbm + power_range_db + 3.     | | |                                     |           | power = power_dbm + power_range_db + 3.     | | ||||||
| |                        |           | To configure a 4 dB power range across      | | |                                     |           | To configure a 4 dB power range across      | | ||||||
| |                        |           | all spans in 0.5 dB steps: `[-2, 2, 0.5]`.  | | |                                     |           | all spans in 0.5 dB steps: `[-2, 2, 0.5]`.  | | ||||||
| |                        |           | A 17 dB span is set to                      | | |                                     |           | A 17 dB span is set to                      | | ||||||
| |                        |           | power = power_dbm + power_range_db - 1,     | | |                                     |           | power = power_dbm + power_range_db - 1,     | | ||||||
| |                        |           | a 20 dB span to                             | | |                                     |           | a 20 dB span to                             | | ||||||
| |                        |           | power = power_dbm + power_range_db and      | | |                                     |           | power = power_dbm + power_range_db and      | | ||||||
| |                        |           | a 23 dB span to                             | | |                                     |           | a 23 dB span to                             | | ||||||
| |                        |           | power = power_dbm + power_range_db + 1      | | |                                     |           | power = power_dbm + power_range_db + 1      | | ||||||
| +------------------------+-----------+---------------------------------------------+ | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
| | `max_length`           | (number)  | Split fiber lengths > max_length.           | | | ``max_fiber_lineic_loss_for_raman`` | (number)  | Maximum linear fiber loss for Raman         | | ||||||
| |                        |           | Interest to support high level              | | |                                     |           | amplification use.                          | | ||||||
| |                        |           | topologies that do not specify in line      | | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
| |                        |           | amplification sites. For example the        | | | ``max_length``                      | (number)  | Split fiber lengths > max_length.           | | ||||||
| |                        |           | CORONET_Global_Topology.xls defines         | | |                                     |           | Interest to support high level              | | ||||||
| |                        |           | links > 1000km between 2 sites: it          | | |                                     |           | topologies that do not specify in line      | | ||||||
| |                        |           | couldn't be simulated if these links        | | |                                     |           | amplification sites. For example the        | | ||||||
| |                        |           | were not splitted in shorter span           | | |                                     |           | CORONET_Global_Topology.xls defines         | | ||||||
| |                        |           | lengths.                                    | | |                                     |           | links > 1000km between 2 sites: it          | | ||||||
| +------------------------+-----------+---------------------------------------------+ | |                                     |           | couldn't be simulated if these links        | | ||||||
| | `length_unit`          | "m"/"km"  | Unit for max_length.                        | | |                                     |           | were not split in shorter span lengths.     | | ||||||
| +------------------------+-----------+---------------------------------------------+ | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
| | `max_loss`             | (number)  | Not used in the current code                | | | ``length_unit``                     | "m"/"km"  | Unit for ``max_length``.                    | | ||||||
| |                        |           | implementation.                             | | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
| +------------------------+-----------+---------------------------------------------+ | | ``max_loss``                        | (number)  | Not used in the current code                | | ||||||
| | `padding`              | (number)  | In dB. Min span loss before putting an      | | |                                     |           | implementation.                             | | ||||||
| |                        |           | attenuator before fiber. Attenuator         | | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
| |                        |           | value                                       | | | ``padding``                         | (number)  | In dB. Min span loss before putting an      | | ||||||
| |                        |           | Fiber.att_in = max(0, padding - span_loss). | | |                                     |           | attenuator before fiber. Attenuator         | | ||||||
| |                        |           | Padding can be set manually to reach a      | | |                                     |           | value                                       | | ||||||
| |                        |           | higher padding value for a given fiber      | | |                                     |           | Fiber.att_in = max(0, padding - span_loss). | | ||||||
| |                        |           | by filling in the Fiber/params/att_in       | | |                                     |           | Padding can be set manually to reach a      | | ||||||
| |                        |           | field in the topology json input [1]        | | |                                     |           | higher padding value for a given fiber      | | ||||||
| |                        |           | but if span_loss = length * loss_coef       | | |                                     |           | by filling in the Fiber/params/att_in       | | ||||||
| |                        |           | + att_in + con_in + con_out < padding,      | | |                                     |           | field in the topology json input [1]        | | ||||||
| |                        |           | the specified att_in value will be          | | |                                     |           | but if span_loss = length * loss_coef       | | ||||||
| |                        |           | completed to have span_loss = padding.      | | |                                     |           | + att_in + con_in + con_out < padding,      | | ||||||
| |                        |           | Therefore it is not possible to set         | | |                                     |           | the specified att_in value will be          | | ||||||
| |                        |           | span_loss < padding.                        | | |                                     |           | completed to have span_loss = padding.      | | ||||||
| +------------------------+-----------+---------------------------------------------+ | |                                     |           | Therefore it is not possible to set         | | ||||||
| | `EOL`                  | (number)  | All fiber span loss ageing. The value       | | |                                     |           | span_loss < padding.                        | | ||||||
| |                        |           | is added to the con_out (fiber output       | | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
| |                        |           | connector). So the design and the path      | | | ``EOL``                             | (number)  | All fiber span loss ageing. The value       | | ||||||
| |                        |           | feasibility are performed with              | | |                                     |           | is added to the con_out (fiber output       | | ||||||
| |                        |           | span_loss + EOL. EOL cannot be set          | | |                                     |           | connector). So the design and the path      | | ||||||
| |                        |           | manually for a given fiber span             | | |                                     |           | feasibility are performed with              | | ||||||
| |                        |           | (workaround is to specify higher con_out    | | |                                     |           | span_loss + EOL. EOL cannot be set          | | ||||||
| |                        |           | loss for this fiber).                       | | |                                     |           | manually for a given fiber span             | | ||||||
| +------------------------+-----------+---------------------------------------------+ | |                                     |           | (workaround is to specify higher            | | ||||||
| | `con_in`, `con_out`    | (number)  | Default values if Fiber/params/con_in/out   | | |                                     |           | ``con_out`` loss for this fiber).           | | ||||||
| |                        |           | is None in the topology input               | | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
| |                        |           | description. This default value is          | | | ``con_in``,                         | (number)  | Default values if Fiber/params/con_in/out   | | ||||||
| |                        |           | ignored if a Fiber/params/con_in/out        | | | ``con_out``                         |           | is None in the topology input               | | ||||||
| |                        |           | value is input in the topology for a        | | |                                     |           | description. This default value is          | | ||||||
| |                        |           | given Fiber.                                | | |                                     |           | ignored if a Fiber/params/con_in/out        | | ||||||
| +------------------------+-----------+---------------------------------------------+ | |                                     |           | value is input in the topology for a        | | ||||||
|  | |                                     |           | given Fiber.                                | | ||||||
| **[1]** | +-------------------------------------+-----------+---------------------------------------------+ | ||||||
|  |  | ||||||
| .. code-block:: json | .. code-block:: json | ||||||
|  |  | ||||||
| @@ -415,33 +425,36 @@ parameters: | |||||||
| ROADMs can be configured as follows. The user can only modify the value of | ROADMs can be configured as follows. The user can only modify the value of | ||||||
| existing parameters: | existing parameters: | ||||||
|  |  | ||||||
| +-------------------------+-----------+---------------------------------------------+ | +--------------------------+-----------+---------------------------------------------+ | ||||||
| | field                   |   type    | description                                 | | | field                    |   type    | description                                 | | ||||||
| +=========================+===========+=============================================+ | +==========================+===========+=============================================+ | ||||||
| |`gain_mode_default_loss` | (number)  | Default value if Roadm/params/loss is       | | | ``target_pch_out_db``    | (number)  | Auto-design sets the ROADM egress channel   | | ||||||
| |                         |           | None in the topology input description.     | | |                          |           | power. This reflects typical control loop   | | ||||||
| |                         |           | This default value is ignored if a          | | |                          |           | algorithms that adjust ROADM losses to      | | ||||||
| |                         |           | params/loss value is input in the           | | |                          |           | equalize channels (eg coming from different | | ||||||
| |                         |           | topology for a given ROADM.                 | | |                          |           | ingress direction or add ports)             | | ||||||
| +-------------------------+-----------+---------------------------------------------+ | |                          |           | This is the default value                   | | ||||||
| |`power_mode_pref`        | (number)  | Power mode only. Auto-design sets the       | | |                          |           | Roadm/params/target_pch_out_db if no value  | | ||||||
| |                         |           | power of ROADM ingress amplifiers to        | | |                          |           | is given in the ``Roadm`` element in the    | | ||||||
| |                         |           | power_dbm + power_range_db,                 | | |                          |           | topology input description.                 | | ||||||
| |                         |           | regardless of existing gain settings        | | |                          |           | This default value is ignored if a          | | ||||||
| |                         |           | from the topology JSON input.               | | |                          |           | params/target_pch_out_db value is input in  | | ||||||
| |                         |           | Auto-design sets the Roadm loss so that     | | |                          |           | the topology for a given ROADM.             | | ||||||
| |                         |           | its egress channel power = power_mode_pref, | | +--------------------------+-----------+---------------------------------------------+ | ||||||
| |                         |           | regardless of existing loss settings        | | | ``add_drop_osnr``        | (number)  | OSNR contribution from the add/drop ports   | | ||||||
| |                         |           | from the topology JSON input. It means      | | +--------------------------+-----------+---------------------------------------------+ | ||||||
| |                         |           | that the output power from a ROADM (and      | | | ``restrictions``         | (dict of  | If non-empty, keys ``preamp_variety_list``  | | ||||||
| |                         |           | therefore its OSNR contribution) is Cte     | | |                          |  strings) | and ``booster_variety_list`` represent      | | ||||||
| |                         |           | and not depending from power_dbm and        | | |                          |           | list of ``type_variety`` amplifiers which   | | ||||||
| |                         |           | power_range_db sweep settings. This         | | |                          |           | are allowed for auto-design within ROADM's  | | ||||||
| |                         |           | choice is meant to reflect some typical     | | |                          |           | line degrees.                               | | ||||||
| |                         |           | control loop algorithms.                    | | |                          |           |                                             | | ||||||
| +-------------------------+-----------+---------------------------------------------+ | |                          |           | 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 | only modify the value of existing parameters. It defines a spectrum of N | ||||||
| identical carriers. While the code libraries allow for different carriers and | identical carriers. While the code libraries allow for different carriers and | ||||||
| power levels, the current user parametrization only allows one carrier type and | power levels, the current user parametrization only allows one carrier type and | ||||||
| @@ -450,21 +463,18 @@ one power/channel definition. | |||||||
| +----------------------+-----------+-------------------------------------------+ | +----------------------+-----------+-------------------------------------------+ | ||||||
| | field                |   type    | description                               | | | field                |   type    | description                               | | ||||||
| +======================+===========+===========================================+ | +======================+===========+===========================================+ | ||||||
| | `f_min/max`          | (number)  | In Hz. Carrier min max excursion          | | | ``f_min``,           | (number)  | In Hz. Carrier min max excursion.         | | ||||||
|  | | ``f_max``            |           |                                           | | ||||||
| +----------------------+-----------+-------------------------------------------+ | +----------------------+-----------+-------------------------------------------+ | ||||||
| | `baud_rate`          | (number)  | In Hz. Simulated baud rate.               | | | ``baud_rate``        | (number)  | In Hz. Simulated baud rate.               | | ||||||
| +----------------------+-----------+-------------------------------------------+ | +----------------------+-----------+-------------------------------------------+ | ||||||
| | `spacing`            | (number)  | In Hz. Carrier spacing.                   | | | ``spacing``          | (number)  | In Hz. Carrier spacing.                   | | ||||||
| +----------------------+-----------+-------------------------------------------+ | +----------------------+-----------+-------------------------------------------+ | ||||||
| | `roll_off`           | (number)  | Not used.                                 | | | ``roll_off``         | (number)  | Not used.                                 | | ||||||
| +----------------------+-----------+-------------------------------------------+ | +----------------------+-----------+-------------------------------------------+ | ||||||
| | `OSNR`               | (number)  | Not used.                                 | | | ``tx_osnr``          | (number)  | In dB. OSNR out from transponder.         | | ||||||
| +----------------------+-----------+-------------------------------------------+ | +----------------------+-----------+-------------------------------------------+ | ||||||
| | `bit_rate`           | (number)  | Not used.                                 | | | ``power_dbm``        | (number)  | Reference channel power. In gain mode     | | ||||||
| +----------------------+-----------+-------------------------------------------+ |  | ||||||
| | `tx_osnr`            | (number)  | In dB. OSNR out from transponder.         | |  | ||||||
| +----------------------+-----------+-------------------------------------------+ |  | ||||||
| | `power_dbm`          | (number)  | Reference channel power. In gain mode     | |  | ||||||
| |                      |           | (see spans/power_mode = false), all gain  | | |                      |           | (see spans/power_mode = false), all gain  | | ||||||
| |                      |           | settings are offset w/r/t this reference  | | |                      |           | settings are offset w/r/t this reference  | | ||||||
| |                      |           | power. In power mode, it is the           | | |                      |           | power. In power mode, it is the           | | ||||||
| @@ -477,17 +487,30 @@ one power/channel definition. | |||||||
| |                      |           | power sweep is defined (see after) the    | | |                      |           | power sweep is defined (see after) the    | | ||||||
| |                      |           | design is not repeated.                   | | |                      |           | 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   | | |                      |           | It is not the min and max channel power   | | ||||||
| |                      |           | values! The reference power becomes:      | | |                      |           | values! The reference power becomes:      | | ||||||
| |                      |           | power_range_db + power_dbm.               | | |                      |           | 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>`_ | 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.  | ||||||
| script propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing and 0 | Launch power can be overridden by using the ``--power`` argument. | ||||||
| dBm/channel. These are not yet parametrized but can be modified directly in the | 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. | ||||||
| script (via the SpectralInformation structure) to accommodate any baud rate, | The number of channel is computed based on ``spacing`` and ``f_min``, ``f_max`` values. | ||||||
| spacing, power or channel count demand. |  | ||||||
|  | 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: | 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 |      $ python path_requests_run.py -h | ||||||
|      Usage: path_requests_run.py [-h] [-v] [-o OUTPUT] [network_filename] [service_filename] [eqpt_filename] |      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: | 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 | This program requires a list of connections to be estimated and the equipment | ||||||
| library. The program computes performances for the list of services (accepts | library. The program computes performances for the list of services (accepts | ||||||
| json or excel format) using the same spectrum propagation modules as | JSON or Excel format) using the same spectrum propagation modules as | ||||||
| transmission_main_example.py. Explanation on the Excel template is provided in | ``transmission_main_example.py``. Explanation on the Excel template is provided in | ||||||
| the `Excel_userguide.rst <Excel_userguide.rst#service-sheet>`_. Template for | 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>`_. | <service-template.json>`_. | ||||||
|  |  | ||||||
| Contributing | Contributing | ||||||
| @@ -519,8 +542,8 @@ Contributing | |||||||
| ``gnpy`` is looking for additional contributors, especially those with experience | ``gnpy`` is looking for additional contributors, especially those with experience | ||||||
| planning and maintaining large-scale, real-world mesh optical networks. | planning and maintaining large-scale, real-world mesh optical networks. | ||||||
|  |  | ||||||
| To get involved, please contact James Powell | To get involved, please contact Jan Kundrát | ||||||
| <james.powell@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>. | <jan.kundrat@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>. | ||||||
|  |  | ||||||
| ``gnpy`` contributions are currently limited to members of `TIP | ``gnpy`` contributions are currently limited to members of `TIP | ||||||
| <http://telecominfraproject.com>`_. Membership is free and open to all. | <http://telecominfraproject.com>`_. Membership is free and open to all. | ||||||
| @@ -578,6 +601,11 @@ implementations. | |||||||
|   :alt: Build Status |   :alt: Build Status | ||||||
|   :scale: 100% |   :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 | TIP OOPT/PSE & PSE WG Charter | ||||||
| ----------------------------- | ----------------------------- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -874,7 +874,7 @@ month={Sept},} | |||||||
|   number = {7}, |   number = {7}, | ||||||
|   journal = {Optics Express}, |   journal = {Optics Express}, | ||||||
|   urlyear = {2017-11-14}, |   urlyear = {2017-11-14}, | ||||||
|   year = {2012-03-26}, |   date = {2012-03-26}, | ||||||
|   year = {2012}, |   year = {2012}, | ||||||
|   pages = {7777}, |   pages = {7777}, | ||||||
|   author = {Bononi, A. and Serena, P. and Rossi, N. and Grellier, E. and Vacondio, F.} |   author = {Bononi, A. and Serena, P. and Rossi, N. and Grellier, E. and Vacondio, F.} | ||||||
| @@ -1114,7 +1114,7 @@ month={Sept},} | |||||||
|   number = {26}, |   number = {26}, | ||||||
|   journal = {Optics Express}, |   journal = {Optics Express}, | ||||||
|   urlyear = {2017-11-16}, |   urlyear = {2017-11-16}, | ||||||
|   year = {2013-12-30}, |   date = {2013-12-30}, | ||||||
|   year = {2013}, |   year = {2013}, | ||||||
|   pages = {32254}, |   pages = {32254}, | ||||||
|   author = {Bononi, Alberto and Beucher, Ottmar and Serena, Paolo} |   author = {Bononi, Alberto and Beucher, Ottmar and Serena, Paolo} | ||||||
|   | |||||||
| @@ -173,5 +173,4 @@ texinfo_documents = [ | |||||||
|      'Miscellaneous'), |      '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                   | | | David    | Boertjes   | Ciena                 | dboertje@ciena.com                   | | ||||||
| +----------+------------+-----------------------+--------------------------------------+ | +----------+------------+-----------------------+--------------------------------------+ | ||||||
|  | | Diego    | Landa      | Facebook              | dlanda@fb.com                        | | ||||||
|  | +----------+------------+-----------------------+--------------------------------------+ | ||||||
| | Esther   | Le Rouzic  | Orange                | esther.lerouzic@orange.com           | | | Esther   | Le Rouzic  | Orange                | esther.lerouzic@orange.com           | | ||||||
| +----------+------------+-----------------------+--------------------------------------+ | +----------+------------+-----------------------+--------------------------------------+ | ||||||
| | Gabriele | Galimberti | Cisco                 | ggalimbe@cisco.com                   | | | Gabriele | Galimberti | Cisco                 | ggalimbe@cisco.com                   | | ||||||
| @@ -61,20 +63,28 @@ Contributors in alphabetical order | |||||||
| +----------+------------+-----------------------+--------------------------------------+ | +----------+------------+-----------------------+--------------------------------------+ | ||||||
| | James    | Powell     | Telecom Infra Project | james.powell@telecominfraproject.com | | | 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             | | | Mattia   | Cantono    | Politecnico di Torino | mattia.cantono@polito.it             | | ||||||
| +----------+------------+-----------------------+--------------------------------------+ | +----------+------------+-----------------------+--------------------------------------+ | ||||||
| | Miguel   | Garrich    | University Catalunya  | miquel.garrich@upct.es               | | | Miguel   | Garrich    | University Catalunya  | miquel.garrich@upct.es               | | ||||||
| +----------+------------+-----------------------+--------------------------------------+ | +----------+------------+-----------------------+--------------------------------------+ | ||||||
| | Stefan   | Melin      | Telia Company         | Stefan.Melin@teliacompany.com        | |  | ||||||
| +----------+------------+-----------------------+--------------------------------------+ |  | ||||||
| | Raj      | Nagarajan  | Lumentum              | raj.nagarajan@lumentum.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             | | | Vittorio | Curri      | Politecnico di Torino | vittorio.curri@polito.it             | | ||||||
| +----------+------------+-----------------------+--------------------------------------+ | +----------+------------+-----------------------+--------------------------------------+ | ||||||
|  | | Xufeng   | Liu        | Jabil                 | xufeng_liu@jabil.com                 | | ||||||
|  | +----------+------------+-----------------------+--------------------------------------+ | ||||||
|  |  | ||||||
| -------------- | -------------- | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,10 +4,39 @@ gnpy\.core package | |||||||
| Submodules | 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 | gnpy\.core\.elements module | ||||||
| --------------------------- | --------------------------- | ||||||
|  |  | ||||||
| .. automodule:: gnpy.core.elements | .. 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: |     :members: | ||||||
|     :undoc-members: |     :undoc-members: | ||||||
|     :show-inheritance: |     :show-inheritance: | ||||||
| @@ -16,30 +45,34 @@ gnpy\.core\.execute module | |||||||
| -------------------------- | -------------------------- | ||||||
|  |  | ||||||
| .. automodule:: gnpy.core.execute | .. automodule:: gnpy.core.execute | ||||||
|     :members: |  | ||||||
|     :undoc-members: |  | ||||||
|     :show-inheritance: |  | ||||||
|  |  | ||||||
| gnpy\.core\.info module | gnpy\.core\.info module | ||||||
| ----------------------- | ----------------------- | ||||||
|  |  | ||||||
| .. automodule:: gnpy.core.info | .. automodule:: gnpy.core.info | ||||||
|     :members: |  | ||||||
|     :undoc-members: |  | ||||||
|     :show-inheritance: |  | ||||||
|  |  | ||||||
| gnpy\.core\.network module | gnpy\.core\.network module | ||||||
| -------------------------- | -------------------------- | ||||||
|  |  | ||||||
| .. automodule:: gnpy.core.network | .. automodule:: gnpy.core.network | ||||||
|     :members: |  | ||||||
|     :undoc-members: |  | ||||||
|     :show-inheritance: |  | ||||||
|  |  | ||||||
| gnpy\.core\.node module | gnpy\.core\.node module | ||||||
| ----------------------- | ----------------------- | ||||||
|  |  | ||||||
| .. automodule:: gnpy.core.node | .. 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: |     :members: | ||||||
|     :undoc-members: |     :undoc-members: | ||||||
|     :show-inheritance: |     :show-inheritance: | ||||||
| @@ -48,23 +81,14 @@ gnpy\.core\.units module | |||||||
| ------------------------ | ------------------------ | ||||||
|  |  | ||||||
| .. automodule:: gnpy.core.units | .. automodule:: gnpy.core.units | ||||||
|     :members: |  | ||||||
|     :undoc-members: |  | ||||||
|     :show-inheritance: |  | ||||||
|  |  | ||||||
| gnpy\.core\.utils module | gnpy\.core\.utils module | ||||||
| ------------------------ | ------------------------ | ||||||
|  |  | ||||||
| .. automodule:: gnpy.core.utils | .. automodule:: gnpy.core.utils | ||||||
|     :members: |  | ||||||
|     :undoc-members: |  | ||||||
|     :show-inheritance: |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Module contents | Module contents | ||||||
| --------------- | --------------- | ||||||
|  |  | ||||||
| .. automodule:: gnpy.core | .. automodule:: gnpy.core | ||||||
|     :members: |  | ||||||
|     :undoc-members: |  | ||||||
|     :show-inheritance: |  | ||||||
|   | |||||||
| @@ -12,6 +12,3 @@ Module contents | |||||||
| --------------- | --------------- | ||||||
|  |  | ||||||
| .. automodule:: gnpy | .. 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. | determined based on the topology. | ||||||
| """ | """ | ||||||
|  |  | ||||||
| from sys import exit |  | ||||||
| try: | try: | ||||||
|     from xlrd import open_workbook |     from xlrd import open_workbook | ||||||
| except ModuleNotFoundError: | except ModuleNotFoundError: | ||||||
|     exit('Required: `pip install xlrd`') |     exit('Required: `pip install xlrd`') | ||||||
| from argparse import ArgumentParser | 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() |     def __str__(self): | ||||||
| parser.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls', |         return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n' | ||||||
|     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 read_excel(input_filename): | 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 |         # reading Links sheet | ||||||
|         links_sheet = wb.sheet_by_name('Links') |         links_sheet = wobo.sheet_by_name('Links') | ||||||
|         links = [] |         nodes = {} | ||||||
|         nodeoccuranceinlinks = [] |         for row in ALL_ROWS(links_sheet, start=5): | ||||||
|         links_by_src = defaultdict(list) |             try: | ||||||
|         links_by_dest = defaultdict(list) |                 nodes[row[0].value].to_node.append(row[1].value) | ||||||
|         for row in all_rows(links_sheet, start=5): |             except KeyError: | ||||||
|             links.append(Shortlink(row[0].value,row[1].value)) |                 nodes[row[0].value] = Node(row[0].value, [row[1].value]) | ||||||
|             links_by_src[row[0].value].append(Shortnode(row[1].value,'')) |             try: | ||||||
|             links_by_dest[row[1].value].append(Shortnode(row[0].value,'')) |                 nodes[row[1].value].to_node.append(row[0].value) | ||||||
|             #print(f'source {links[len(links)-1].src} dest {links[len(links)-1].dest}') |             except KeyError: | ||||||
|             nodeoccuranceinlinks.append(row[0].value) |                 nodes[row[1].value] = Node(row[1].value, [row[0].value]) | ||||||
|             nodeoccuranceinlinks.append(row[1].value) |  | ||||||
|  |  | ||||||
|         # reading Nodes sheet |         nodes_sheet = wobo.sheet_by_name('Nodes') | ||||||
|         nodes_sheet = wb.sheet_by_name('Nodes') |         for row in ALL_ROWS(nodes_sheet, start=5): | ||||||
|         nodes = [] |             node = row[0].value | ||||||
|         node_degree = [] |             eqpt = row[6].value | ||||||
|         for row in all_rows(nodes_sheet, start=5) : |             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 | def create_eqt_template(nodes, input_filename): | ||||||
|             # verify node degree to confirm eqt type |     """ writes list of node A node Z corresponding to Nodes and Links sheets in order | ||||||
|             node_degree.append(nodeoccuranceinlinks.count(row[0].value)) |     to help user populating Eqpt | ||||||
|             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): |  | ||||||
|     output_filename = f'{input_filename[:-4]}_eqpt_sheet.txt' |     output_filename = f'{input_filename[:-4]}_eqpt_sheet.txt' | ||||||
|     with open(output_filename, 'w', encoding='utf-8') as my_file: |     with open(output_filename, 'w', encoding='utf-8') as my_file: | ||||||
|         # print header similar to excel |         # 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\ |            \nNode A \tNode Z \tamp type \tatt_in \tamp gain \ttilt \tatt_out\ | ||||||
|            amp type   \tatt_in \tamp gain   \ttilt   \tatt_out\n') |            amp type   \tatt_in \tamp gain   \ttilt   \tatt_out\n') | ||||||
|  |  | ||||||
|         tab = [] |  | ||||||
|         temp = [] |         for node in nodes.values(): | ||||||
|         i = 0 |             if node.eqpt == 'ILA': | ||||||
|         for lk in links: |                 my_file.write(f'{node.uid}\t{node.to_node[0]}\n') | ||||||
|             if [e for n,e in nodes if n==lk.src][0] != 'FUSED' : |             if node.eqpt == 'ROADM': | ||||||
|                 temp = [lk.src , lk.dest] |                 for to_node in node.to_node: | ||||||
|                 tab.append(temp) |                     my_file.write(f'{node.uid}\t{to_node}\n') | ||||||
|                 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 |  | ||||||
|         print(f'File {output_filename} successfully created with Node A - Node Z ' + |         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__': | if __name__ == '__main__': | ||||||
|     args = parser.parse_args() |     ARGS = PARSER.parse_args() | ||||||
|     input_filename = args.workbook |     create_eqt_template(read_excel(ARGS.workbook), 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) |  | ||||||
|   | |||||||
							
								
								
									
										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",  |     "nf_ripple": "NFR_96.txt",  | ||||||
|     "gain_ripple": "DFG0_96.txt", |     "gain_ripple": "DFG_96.txt", | ||||||
|     "dgt": "DGT_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 | Created on Tue Jan 30 12:32:00 2018 | ||||||
|  |  | ||||||
| @author: jeanluc-auge | @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 | 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 | 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 | """amplifier file names | ||||||
| convert a set of amplifier files + input json definiton file into a valid edfa_json_file: | 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 | nf_ripple: NF ripple excursion txt file | ||||||
| dfg: gain txt file | gain_ripple: gain ripple txt file | ||||||
| dgt: dynamic gain txt file | dgt: dynamic gain txt file | ||||||
| input json file in argument (defult = 'OA.json') | input json file in argument (defult = 'OA.json') | ||||||
|  |  | ||||||
| the json input file should have the following fields: | the json input file should have the following fields: | ||||||
| { | { | ||||||
|     "gain_flatmax": 25, |     "nf_fit_coeff": "nf_filename.txt", | ||||||
|     "gain_min": 15, |     "nf_ripple": "nf_ripple_filename.txt",  | ||||||
|     "p_max": 21, |     "gain_ripple": "DFG_filename.txt", | ||||||
|     "nf_fit_coeff": "pNFfit3.txt", |     "dgt": "DGT_filename.txt", | ||||||
|     "nf_ripple": "NFR_96.txt",  |  | ||||||
|     "dfg": "DFG_96.txt", |  | ||||||
|     "dgt": "DGT_96.txt", |  | ||||||
|     "nf_model":  |  | ||||||
|         { |  | ||||||
|         "enabled": true, |  | ||||||
|         "nf_min": 5.8, |  | ||||||
|         "nf_max": 10 |  | ||||||
|         } |  | ||||||
| } | } | ||||||
| gain_flat = max flat gain (dB) |  | ||||||
| gain_min = min gain (dB) : will consider an input VOA if below (TBD vs throwing an exception) |  | ||||||
| p_max = max power (dBm) |  | ||||||
| nf_fit = boolean (True, False) :  |  | ||||||
|         if False nf_fit_coeff are ignored and nf_model fields are used |  | ||||||
| """ | """ | ||||||
|  |  | ||||||
| input_json_file_name = "OA.json" #default path | input_json_file_name = "OA.json" #default path | ||||||
| output_json_file_name = "default_edfa_config.json" | output_json_file_name = "default_edfa_config.json" | ||||||
| param_field  ="params" | gain_ripple_field = "gain_ripple" | ||||||
| gain_min_field = "gain_min" |  | ||||||
| gain_max_field = "gain_flatmax" |  | ||||||
| gain_ripple_field = "dfg" |  | ||||||
| nf_ripple_field = "nf_ripple" | nf_ripple_field = "nf_ripple" | ||||||
| nf_fit_coeff = "nf_fit_coeff" | 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): | def read_file(field, file_name): | ||||||
|     """read and format the 96 channels txt files describing the amplifier NF and ripple |     """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 |         #consider ripple excursion only to avoid redundant information | ||||||
|         #because the max flat_gain is already given by the 'gain_flat' field in json |         #because the max flat_gain is already given by the 'gain_flat' field in json | ||||||
|         #remove the mean component |         #remove the mean component | ||||||
|  |         print(file_name, ', mean value =', data.mean(), ' is substracted') | ||||||
|         data = data - data.mean() |         data = data - data.mean() | ||||||
|     data = data.tolist() |     data = data.tolist() | ||||||
|     return data |     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":[{ | {     "Edfa":[{ | ||||||
|             "type_variety": "high_detail_model_example", |             "type_variety": "high_detail_model_example", | ||||||
|  |             "type_def": "advanced_model", | ||||||
|             "gain_flatmax": 25, |             "gain_flatmax": 25, | ||||||
|             "gain_min": 15, |             "gain_min": 15, | ||||||
|             "p_max": 21, |             "p_max": 21, | ||||||
|             "advanced_config_from_json": "std_medium_gain_advanced_config.json", |             "advanced_config_from_json": "std_medium_gain_advanced_config.json", | ||||||
|             "out_voa_auto": false, |             "out_voa_auto": false, | ||||||
|             "allowed_for_design": 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_variety": "operator_model_example", | ||||||
|             "type_def": "variable_gain", |             "type_def": "variable_gain", | ||||||
| @@ -37,6 +47,17 @@ | |||||||
|             "allowed_for_design": false |             "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_variety": "std_medium_gain", | ||||||
|             "type_def": "variable_gain", |             "type_def": "variable_gain", | ||||||
|             "gain_flatmax": 26, |             "gain_flatmax": 26, | ||||||
| @@ -59,6 +80,17 @@ | |||||||
|             "allowed_for_design": true |             "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_variety": "std_fixed_gain", | ||||||
|             "type_def": "fixed_gain", |             "type_def": "fixed_gain", | ||||||
|             "gain_flatmax": 21, |             "gain_flatmax": 21, | ||||||
| @@ -66,7 +98,50 @@ | |||||||
|             "p_max": 21, |             "p_max": 21, | ||||||
|             "nf0": 5.5, |             "nf0": 5.5, | ||||||
|             "allowed_for_design": false |             "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":[{ |       "Fiber":[{ | ||||||
|             "type_variety": "SSMF", |             "type_variety": "SSMF", | ||||||
| @@ -84,9 +159,41 @@ | |||||||
|             "gamma": 0.000843 |             "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, |             "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, |             "max_length": 150, | ||||||
|             "length_units": "km", |             "length_units": "km", | ||||||
|             "max_loss": 28, |             "max_loss": 28, | ||||||
| @@ -96,10 +203,13 @@ | |||||||
|             "con_out": 0 |             "con_out": 0 | ||||||
|             } |             } | ||||||
|       ], |       ], | ||||||
|       "Roadms":[{ |       "Roadm":[{ | ||||||
|             "gain_mode_default_loss": 20, |             "target_pch_out_db": -20, | ||||||
|             "power_mode_pout_target": -20, |             "add_drop_osnr": 38, | ||||||
|             "add_drop_osnr": 38 |             "restrictions": { | ||||||
|  |                             "preamp_variety_list":[], | ||||||
|  |                             "booster_variety_list":[] | ||||||
|  |                             }             | ||||||
|             }], |             }], | ||||||
|       "SI":[{ |       "SI":[{ | ||||||
|             "f_min": 191.3e12, |             "f_min": 191.3e12, | ||||||
| @@ -107,10 +217,10 @@ | |||||||
|             "f_max":195.1e12, |             "f_max":195.1e12, | ||||||
|             "spacing": 50e9, |             "spacing": 50e9, | ||||||
|             "power_dbm": 0, |             "power_dbm": 0, | ||||||
|             "power_range_db": [0,0,0.5], |             "power_range_db": [0,0,1], | ||||||
|             "roll_off": 0.15, |             "roll_off": 0.15, | ||||||
|             "tx_osnr": 40, |             "tx_osnr": 40, | ||||||
|             "sys_margins": 0 |             "sys_margins": 2 | ||||||
|             }], |             }], | ||||||
|       "Transceiver":[ |       "Transceiver":[ | ||||||
|             { |             { | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							| @@ -623,31 +623,469 @@ | |||||||
|         "con_in": null, |         "con_in": null, | ||||||
|         "con_out": 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": [ |   "connections": [ | ||||||
|     { |     { | ||||||
|       "from_node": "roadm Lannion_CAS", |       "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" |       "to_node": "fiber (Lannion_CAS → Corlay)-F061" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Corlay → Lannion_CAS)-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" |       "to_node": "roadm Lannion_CAS" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_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" |       "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Stbrieuc → Lannion_CAS)-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" |       "to_node": "roadm Lannion_CAS" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_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" |       "to_node": "fiber (Lannion_CAS → Morlaix)-F059" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Morlaix → Lannion_CAS)-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" |       "to_node": "roadm Lannion_CAS" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
| @@ -684,18 +1122,34 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "roadm Lorient_KMA", |       "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" |       "to_node": "fiber (Lorient_KMA → Loudeac)-F054" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Loudeac → Lorient_KMA)-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" |       "to_node": "roadm Lorient_KMA" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_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" |       "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Vannes_KBE → Lorient_KMA)-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" |       "to_node": "roadm Lorient_KMA" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
| @@ -708,10 +1162,18 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "roadm Vannes_KBE", |       "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" |       "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Lorient_KMA → Vannes_KBE)-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" |       "to_node": "roadm Vannes_KBE" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
| @@ -724,18 +1186,34 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056", |       "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" |       "to_node": "fiber (Stbrieuc → Rennes_STA)-F057" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Rennes_STA → Stbrieuc)-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" |       "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "roadm Rennes_STA", |       "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" |       "to_node": "fiber (Rennes_STA → Stbrieuc)-F057" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Stbrieuc → Rennes_STA)-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" |       "to_node": "roadm Rennes_STA" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
| @@ -764,10 +1242,18 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "roadm Brest_KLA", |       "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" |       "to_node": "fiber (Brest_KLA → Morlaix)-F060" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "fiber (Morlaix → Brest_KLA)-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" |       "to_node": "roadm Brest_KLA" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|   | |||||||
										
											Binary file not shown.
										
									
								
							| @@ -2,10 +2,11 @@ | |||||||
|   "path-request": [ |   "path-request": [ | ||||||
|     { |     { | ||||||
|       "request-id": "0", |       "request-id": "0", | ||||||
|       "source": "Lorient_KMA", |       "source": "trx Lorient_KMA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "trx Vannes_KBE", | ||||||
|       "src-tp-id": "trx Lorient_KMA", |       "src-tp-id": "trx Lorient_KMA", | ||||||
|       "dst-tp-id": "trx Vannes_KBE", |       "dst-tp-id": "trx Vannes_KBE", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -13,8 +14,8 @@ | |||||||
|           "trx_mode": null, |           "trx_mode": null, | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -22,17 +23,15 @@ | |||||||
|           "output-power": 0.0012589254117941673, |           "output-power": 0.0012589254117941673, | ||||||
|           "path_bandwidth": 100000000000.0 |           "path_bandwidth": 100000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "1", |       "request-id": "1", | ||||||
|       "source": "Brest_KLA", |       "source": "trx Brest_KLA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "trx Vannes_KBE", | ||||||
|       "src-tp-id": "trx Brest_KLA", |       "src-tp-id": "trx Brest_KLA", | ||||||
|       "dst-tp-id": "trx Vannes_KBE", |       "dst-tp-id": "trx Vannes_KBE", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -40,8 +39,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -50,66 +49,42 @@ | |||||||
|           "path_bandwidth": 200000000000.0 |           "path_bandwidth": 200000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Brest_KLA", |               "node-id": "roadm Brest_KLA", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 1, |             "index": 1, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Lannion_CAS", |               "node-id": "roadm Lannion_CAS", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 2, |             "index": 2, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Lorient_KMA", |               "node-id": "roadm Lorient_KMA", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 3, |             "index": 3, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Vannes_KBE", |               "node-id": "roadm Vannes_KBE", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -117,10 +92,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "3", |       "request-id": "3", | ||||||
|       "source": "Lannion_CAS", |       "source": "trx Lannion_CAS", | ||||||
|       "destination": "Rennes_STA", |       "destination": "trx Rennes_STA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
|       "dst-tp-id": "trx Rennes_STA", |       "dst-tp-id": "trx Rennes_STA", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -128,8 +104,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -137,17 +113,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 60000000000.0 |           "path_bandwidth": 60000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "4", |       "request-id": "4", | ||||||
|       "source": "Rennes_STA", |       "source": "trx Rennes_STA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "trx Lannion_CAS", | ||||||
|       "src-tp-id": "trx Rennes_STA", |       "src-tp-id": "trx Rennes_STA", | ||||||
|       "dst-tp-id": "trx Lannion_CAS", |       "dst-tp-id": "trx Lannion_CAS", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -155,8 +129,8 @@ | |||||||
|           "trx_mode": null, |           "trx_mode": null, | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 75000000000.0, |           "spacing": 75000000000.0, | ||||||
| @@ -164,17 +138,15 @@ | |||||||
|           "output-power": 0.0019952623149688794, |           "output-power": 0.0019952623149688794, | ||||||
|           "path_bandwidth": 150000000000.0 |           "path_bandwidth": 150000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "5", |       "request-id": "5", | ||||||
|       "source": "Rennes_STA", |       "source": "trx Rennes_STA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "trx Lannion_CAS", | ||||||
|       "src-tp-id": "trx Rennes_STA", |       "src-tp-id": "trx Rennes_STA", | ||||||
|       "dst-tp-id": "trx Lannion_CAS", |       "dst-tp-id": "trx Lannion_CAS", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -182,8 +154,8 @@ | |||||||
|           "trx_mode": "mode 2", |           "trx_mode": "mode 2", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 75000000000.0, |           "spacing": 75000000000.0, | ||||||
| @@ -191,17 +163,15 @@ | |||||||
|           "output-power": 0.0019952623149688794, |           "output-power": 0.0019952623149688794, | ||||||
|           "path_bandwidth": 20000000000.0 |           "path_bandwidth": 20000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "6", |       "request-id": "6", | ||||||
|       "source": "Lannion_CAS", |       "source": "trx Lannion_CAS", | ||||||
|       "destination": "Lorient_KMA", |       "destination": "trx Lorient_KMA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
|       "dst-tp-id": "trx Lorient_KMA", |       "dst-tp-id": "trx Lorient_KMA", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -209,8 +179,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -218,17 +188,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "7", |       "request-id": "7", | ||||||
|       "source": "Lannion_CAS", |       "source": "trx Lannion_CAS", | ||||||
|       "destination": "Lorient_KMA", |       "destination": "trx Lorient_KMA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
|       "dst-tp-id": "trx Lorient_KMA", |       "dst-tp-id": "trx Lorient_KMA", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -236,8 +204,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -245,17 +213,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 400000000000.0 |           "path_bandwidth": 400000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "7b", |       "request-id": "7b", | ||||||
|       "source": "Lannion_CAS", |       "source": "trx Lannion_CAS", | ||||||
|       "destination": "Lorient_KMA", |       "destination": "trx Lorient_KMA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
|       "dst-tp-id": "trx Lorient_KMA", |       "dst-tp-id": "trx Lorient_KMA", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -263,8 +229,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 75000000000.0, |           "spacing": 75000000000.0, | ||||||
| @@ -272,9 +238,6 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 400000000000.0 |           "path_bandwidth": 400000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
| @@ -282,9 +245,8 @@ | |||||||
|     { |     { | ||||||
|       "synchronization-id": "3", |       "synchronization-id": "3", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "false", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "3", |           "3", | ||||||
|           "1" |           "1" | ||||||
| @@ -294,9 +256,8 @@ | |||||||
|     { |     { | ||||||
|       "synchronization-id": "4", |       "synchronization-id": "4", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "false", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "4", |           "4", | ||||||
|           "5" |           "5" | ||||||
|   | |||||||
| @@ -18,77 +18,107 @@ from pathlib import Path | |||||||
| from collections import namedtuple | from collections import namedtuple | ||||||
| from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO | from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO | ||||||
| from json import dumps, loads | from json import dumps, loads | ||||||
| from networkx import (draw_networkx_nodes, draw_networkx_edges, |  | ||||||
|                       draw_networkx_labels) |  | ||||||
| from numpy import mean | from numpy import mean | ||||||
| from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element | from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element | ||||||
| from gnpy.core.utils import load_json | from gnpy.core.utils import load_json | ||||||
| from gnpy.core.network import load_network, build_network, set_roadm_loss, save_network | 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, automatic_spacing | from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | ||||||
| from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused, Fiber | from gnpy.core.elements import Transceiver, Roadm | ||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
| from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, | from gnpy.core.request import (Path_request, Result_element, | ||||||
|                               propagate, jsontocsv, Disjunction, compute_path_dsjctn, requests_aggregation, |                                propagate, jsontocsv, Disjunction, compute_path_dsjctn, | ||||||
|                               propagate_and_optimize_mode) |                                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 copy import copy, deepcopy | ||||||
| from textwrap import dedent | from textwrap import dedent | ||||||
| from math import ceil | 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' | #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 = ArgumentParser(description='A function that computes performances for a list of ' + | ||||||
| parser.add_argument('network_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') |                         'services provided in a json file or an excel sheet.') | ||||||
| parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') | PARSER.add_argument('network_filename', nargs='?', type=Path,\ | ||||||
| parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') |                     default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\ | ||||||
| parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') |                     help='input topology file in xls or json') | ||||||
| parser.add_argument('-o', '--output', type = Path) | 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 = [] |     requests_list = [] | ||||||
|  |  | ||||||
|     for req in json_data['path-request']: |     for req in json_data['path-request']: | ||||||
|         # init all params from request |         # init all params from request | ||||||
|         params = {} |         params = {} | ||||||
|         params['request_id'] = req['request-id'] |         params['request_id'] = req['request-id'] | ||||||
|         params['source'] = req['src-tp-id'] |         params['source'] = req['source'] | ||||||
|         params['destination'] = req['dst-tp-id'] |         params['bidir'] = req['bidirectional'] | ||||||
|  |         params['destination'] = req['destination'] | ||||||
|         params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type'] |         params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type'] | ||||||
|         params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode'] |         params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode'] | ||||||
|         params['format'] = params['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'] |         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 |         # recover trx physical param (baudrate, ...) from type and mode | ||||||
|         # in trx_mode_params optical power is read from equipment['SI']['default'] and |         # in trx_mode_params optical power is read from equipment['SI']['default'] and | ||||||
|         # nb_channel is computed based on min max frequency and spacing |         # 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) |         params.update(trx_params) | ||||||
|         # print(trx_params['min_spacing']) |         # 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 |         # params['power'] is updated | ||||||
|         if req['path-constraints']['te-bandwidth']['output-power']: |         try: | ||||||
|             params['power'] = req['path-constraints']['te-bandwidth']['output-power'] |             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 |         # same process for nb-channel | ||||||
|         f_min = params['f_min'] |         f_min = params['f_min'] | ||||||
|         f_max_from_si = params['f_max'] |         f_max_from_si = params['f_max'] | ||||||
|         if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None : |         try: | ||||||
|             nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel']  |             if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None: | ||||||
|             params['nb_channel'] = nch          |                 nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] | ||||||
|             spacing = params['spacing'] |                 params['nb_channel'] = nch | ||||||
|             params['f_max'] = f_min + nch*spacing |                 spacing = params['spacing'] | ||||||
|         else : |                 params['f_max'] = f_min + nch*spacing | ||||||
|             params['nb_channel'] = automatic_nch(f_min,f_max_from_si,params['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) |         consistency_check(params, f_max_from_si) | ||||||
|  |  | ||||||
|         try : |         try: | ||||||
|             params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] |             params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] | ||||||
|         except KeyError: |         except KeyError: | ||||||
|             pass |             pass | ||||||
| @@ -96,304 +126,444 @@ def requests_from_json(json_data,equipment): | |||||||
|     return requests_list |     return requests_list | ||||||
|  |  | ||||||
| def consistency_check(params, f_max_from_si): | 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_min = params['f_min'] | ||||||
|     f_max = params['f_max'] |     f_max = params['f_max'] | ||||||
|     max_recommanded_nb_channels = automatic_nch(f_min,f_max, |     max_recommanded_nb_channels = automatic_nch(f_min, f_max, params['spacing']) | ||||||
|                 params['spacing']) |  | ||||||
|     if params['baud_rate'] is not None: |     if params['baud_rate'] is not None: | ||||||
|         #implicitely means that a mode is defined with min_spacing |         #implicitely means that a mode is defined with min_spacing | ||||||
|         if params['min_spacing']>params['spacing'] :  |         if params['min_spacing'] > params['spacing']: | ||||||
|             msg = f'Request {params["request_id"]} has spacing below transponder {params["trx_type"]}'+\ |             msg = f'Request {params["request_id"]} has spacing below transponder ' +\ | ||||||
|                 f' {params["trx_mode"]} min spacing value {params["min_spacing"]*1e-9}GHz.\n'+\ |                   f'{params["trx_type"]} {params["trx_mode"]} min spacing value ' +\ | ||||||
|                 'Computation stopped' |                   f'{params["min_spacing"]*1e-9}GHz.\nComputation stopped' | ||||||
|             print(msg) |             print(msg) | ||||||
|             logger.critical(msg) |             LOGGER.critical(msg) | ||||||
|             exit() |             raise ServiceError(msg) | ||||||
|         if f_max>f_max_from_si: |         if f_max > f_max_from_si: | ||||||
|             msg = dedent(f''' |             msg = dedent(f''' | ||||||
|             Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz  |             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. |             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} |             max recommanded nb of channels is {max_recommanded_nb_channels} | ||||||
|             Computation stopped.''') |             Computation stopped.''') | ||||||
|             logger.critical(msg) |             LOGGER.critical(msg) | ||||||
|             exit()     |             raise ServiceError(msg) | ||||||
|  |  | ||||||
|  |  | ||||||
| def disjunctions_from_json(json_data): | 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 = [] |     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 |     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': |     if filename.suffix.lower() == '.xls': | ||||||
|         logger.info('Automatically converting requests from XLS to JSON') |         LOGGER.info('Automatically converting requests from XLS to JSON') | ||||||
|         json_data = convert_service_sheet(filename,eqpt_filename) |         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: |     else: | ||||||
|         with open(filename, encoding='utf-8') as f: |         with open(filename, encoding='utf-8') as my_f: | ||||||
|             json_data = loads(f.read()) |             json_data = loads(my_f.read()) | ||||||
|     return json_data |     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): | def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): | ||||||
|      |     """ use a list but a dictionnary might be helpful to find path based on request_id | ||||||
|     # 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 ! | ||||||
|     # TODO change all these req, dsjct, res lists into dict ! |     """ | ||||||
|     path_res_list = [] |     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 |         # use the power specified in requests but might be different from the one | ||||||
|         # the power is an optional parameter for requests definition |         # specified for design the power is an optional parameter for requests | ||||||
|         # if optional, use the one defines in eqt_config.json |         # definition if optional, use the one defines in eqt_config.json | ||||||
|         p_db = lin2db(pathreq.power*1e3) |         p_db = lin2db(pathreq.power*1e3) | ||||||
|         p_total_db = p_db + lin2db(pathreq.nb_channel) |         p_total_db = p_db + lin2db(pathreq.nb_channel) | ||||||
|         print(f'request {pathreq.request_id}') |         print(f'request {pathreq.request_id}') | ||||||
|         print(f'Computing path from {pathreq.source} to {pathreq.destination}') |         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] |         # pathlist[i] contains the whole path information for request i | ||||||
|         print(f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}\n') |         # 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 |         # for debug | ||||||
|         # print(f'{pathreq.baud_rate}   {pathreq.power}   {pathreq.spacing}   {pathreq.nb_channel}') |         # 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: |             if pathreq.baud_rate is not None: | ||||||
|                 total_path = propagate(total_path,pathreq,equipment, show=False) |                 # means that at this point the mode was entered/forced by user and thus a | ||||||
|                 temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))),2) |                 # baud_rate was defined | ||||||
|                 if temp_snr01nm < pathreq.OSNR : |                 total_path = propagate(total_path, pathreq, equipment) | ||||||
|                     msg = f'\tWarning! Request {pathreq.request_id} computed path from {pathreq.source} to {pathreq.destination} does not pass with {pathreq.tsp_mode}\n' +\ |                 temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2) | ||||||
|                     f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}\n' |                 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) |                     print(msg) | ||||||
|                     logger.warning(msg) |                     LOGGER.warning(msg) | ||||||
|                     total_path = [] |                     pathreq.blocking_reason = 'MODE_NOT_FEASIBLE' | ||||||
|             else: |             else: | ||||||
|                 total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment) |                 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 |                 # if no baudrate satisfies spacing, no mode is returned and the last explored mode | ||||||
|                 # a warning is shown in the propagate_and_optimize_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 | ||||||
|                     # 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 | ||||||
|                     # that passes. if no mode passes, then it returns an empty path |                 # 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.baud_rate = mode['baud_rate'] | ||||||
|                     pathreq.tsp_mode = mode['format'] |                     pathreq.tsp_mode = mode['format'] | ||||||
|                     pathreq.format = mode['format'] |                     pathreq.format = mode['format'] | ||||||
|                     pathreq.OSNR = mode['OSNR'] |                     pathreq.OSNR = mode['OSNR'] | ||||||
|                     pathreq.tx_osnr = mode['tx_osnr'] |                     pathreq.tx_osnr = mode['tx_osnr'] | ||||||
|                     pathreq.bit_rate = mode['bit_rate'] |                     pathreq.bit_rate = mode['bit_rate'] | ||||||
|                 else : |  | ||||||
|                     total_path = [] |             # reversed path is needed for correct spectrum assignment | ||||||
|         # we record the last tranceiver object in order to have th whole  |             reversed_path = find_reversed_path(pathlist[i]) | ||||||
|         # information about spectrum. Important Note: since transceivers  |             if pathreq.bidir: | ||||||
|         # attached to roadms are actually logical elements to simulate |                 # only propagate if bidir is true, but needs the reversed path anyway for | ||||||
|         # performance, several demands having the same destination may use  |                 # correct spectrum assignment | ||||||
|         # the same transponder for the performance simaulation. This is why  |                 rev_p = deepcopy(reversed_path) | ||||||
|         # we use deepcopy: to ensure each propagation is recorded and not  |  | ||||||
|         # overwritten  |                 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') | ||||||
|         path_res_list.append(deepcopy(total_path)) |                 propagated_reversed_path = propagate(rev_p, pathreq, equipment) | ||||||
|     return path_res_list |                 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): | def correct_route_list(network, pathreqlist): | ||||||
|     # prepares the format of route list of nodes to be consistant |     """ prepares the format of route list of nodes to be consistant | ||||||
|     # remove wrong names, remove endpoints |         remove wrong names, remove endpoints | ||||||
|     # also correct source and destination |         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 |     anytype = [n.uid for n in network.nodes()] | ||||||
|     # so fiber constraint is not supported |     # 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)] |     transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)] | ||||||
|     for pathreq in pathreqlist: |     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 |             # replace possibly wrong name with a formated roadm name | ||||||
|             # print(n_id) |             # 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 \ |                 nodes_suggestion = [uid for uid in anytype \ | ||||||
|                     if n_id.lower() in uid.lower()] |                     if n_id.lower() in uid.lower() and uid not in transponders] | ||||||
|                 if pathreq.loose_list[i] == 'loose': |                 if pathreq.loose_list[i] == 'LOOSE': | ||||||
|                     if len(nodes_suggestion)>0 : |                     if len(nodes_suggestion) > 0: | ||||||
|                         new_n = nodes_suggestion[0] |                         new_n = nodes_suggestion[0] | ||||||
|                         print(f'invalid route node specified:\ |                         print(f'invalid route node specified:\ | ||||||
|                         \n\'{n_id}\', replaced with \'{new_n}\'') |                         \n\'{n_id}\', replaced with \'{new_n}\'') | ||||||
|                         pathreq.nodes_list[i] = new_n |                         pathreq.nodes_list[i] = new_n | ||||||
|                     else: |                     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.nodes_list.remove(n_id) | ||||||
|                         pathreq.loose_list.pop(i) |                         pathreq.loose_list.pop(i) | ||||||
|                 else: |                 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' |                     msg = f'\x1b[1;33;40m'+f'could not find node: {n_id} in network topology.' +\ | ||||||
|                     logger.critical(msg) |                           f' Strict constraint can not be applied.' + '\x1b[0m' | ||||||
|  |                     LOGGER.critical(msg) | ||||||
|                     raise ValueError(msg) |                     raise ValueError(msg) | ||||||
|         if pathreq.source not in transponders: |         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' |             msg = f'\x1b[1;31;40m' + f'Request: {pathreq.request_id}: could not find' +\ | ||||||
|             logger.critical(msg) |                   f' transponder source: {pathreq.source}.'+'\x1b[0m' | ||||||
|  |             LOGGER.critical(msg) | ||||||
|             print(f'{msg}\nComputation stopped.') |             print(f'{msg}\nComputation stopped.') | ||||||
|             exit() |             raise ServiceError(msg) | ||||||
|              |  | ||||||
|         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() |  | ||||||
|  |  | ||||||
|         # 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 |     return pathreqlist | ||||||
|  |  | ||||||
| def correct_disjn(disjn): | def correct_disjn(disjn): | ||||||
|  |     """ clean disjunctions to remove possible repetition | ||||||
|  |     """ | ||||||
|     local_disjn = disjn.copy() |     local_disjn = disjn.copy() | ||||||
|     for el in local_disjn: |     for elem in local_disjn: | ||||||
|         for d in local_disjn: |         for dis_elem in local_disjn: | ||||||
|             if set(el.disjunctions_req) == set(d.disjunctions_req) and\ |             if set(elem.disjunctions_req) == set(dis_elem.disjunctions_req) and\ | ||||||
|              el.disjunction_id != d.disjunction_id: |              elem.disjunction_id != dis_elem.disjunction_id: | ||||||
|                 local_disjn.remove(d) |                 local_disjn.remove(dis_elem) | ||||||
|     return local_disjn |     return local_disjn | ||||||
|  |  | ||||||
|  |  | ||||||
| def path_result_json(pathresult): | def path_result_json(pathresult): | ||||||
|  |     """ create the response dictionnary | ||||||
|  |     """ | ||||||
|     data = { |     data = { | ||||||
|         'path': [n.json for n in pathresult] |         'response': [n.json for n in pathresult] | ||||||
|     } |     } | ||||||
|     return data |     return data | ||||||
|  |  | ||||||
|  | def compute_requests(network, data, equipment): | ||||||
| if __name__ == '__main__': |     """ Main program calling functions | ||||||
|     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) |  | ||||||
|  |  | ||||||
|     # Build the network once using the default power defined in SI in eqpt config |     # 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 |     # TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||||
|     # spacing, f_min and f_max  |     # spacing, f_min and f_max | ||||||
|     p_db = equipment['SI']['default'].power_dbm |     p_db = equipment['SI']['default'].power_dbm | ||||||
|      |  | ||||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|     build_network(network, equipment, p_db, p_total_db) |     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  |     try: | ||||||
|     # mess the computation : better to stop the computation |         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] |     all_ids = [r.request_id for r in rqs] | ||||||
|     if len(all_ids) != len(set(all_ids)): |     if len(all_ids) != len(set(all_ids)): | ||||||
|         for a in list(set(all_ids)): |         for item in list(set(all_ids)): | ||||||
|             all_ids.remove(a) |             all_ids.remove(item) | ||||||
|         msg = f'Requests id {all_ids} are not unique' |         msg = f'Requests id {all_ids} are not unique' | ||||||
|         logger.critical(msg) |         LOGGER.critical(msg) | ||||||
|         exit() |         raise ServiceError(msg) | ||||||
|     rqs = correct_route_list(network, rqs) |     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) |     # pths = compute_path(network, equipment, rqs) | ||||||
|     dsjn = disjunctions_from_json(data) |     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) |     print(dsjn) | ||||||
|     # need to warn or correct in case of wrong disjunction form |     # need to warn or correct in case of wrong disjunction form | ||||||
|     # disjunction must not be repeated with same or different ids |     # disjunction must not be repeated with same or different ids | ||||||
|     dsjn = correct_disjn(dsjn) |     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 |     # 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(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') |     print('\x1b[1;34;40m' + f'Computing all paths with constraints' + '\x1b[0m') | ||||||
|     propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) |     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') |     print('\x1b[1;34;40m'+f'Result summary'+ '\x1b[0m') | ||||||
|      |     header = ['req id', '  demand', '  snr@bandwidth A-Z (Z-A)', '  snr@0.1nm A-Z (Z-A)',\ | ||||||
|     header = ['req id', '  demand','  snr@bandwidth','  snr@0.1nm','  Receiver minOSNR', '  mode', '  Gbit/s' , '  nb of tsp pairs'] |               '  Receiver minOSNR', '  mode', '  Gbit/s', '  nb of tsp pairs',\ | ||||||
|  |               'N,M or blocking reason'] | ||||||
|     data = [] |     data = [] | ||||||
|     data.append(header) |     data.append(header) | ||||||
|     for i, p in enumerate(propagatedpths): |     for i, this_p in enumerate(propagatedpths): | ||||||
|         if p: |         rev_pth = reversed_propagatedpths[i] | ||||||
|             line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ |         if rev_pth and this_p: | ||||||
|                 f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ |             psnrb = f'{round(mean(this_p[-1].snr),2)} ({round(mean(rev_pth[-1].snr),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) }'] |             psnr = f'{round(mean(this_p[-1].snr_01nm), 2)}' +\ | ||||||
|         else: |                    f' ({round(mean(rev_pth[-1].snr_01nm),2)})' | ||||||
|             line = [f'{rqs[i].request_id}',f' {rqs[i].source} to {rqs[i].destination} : not feasible '] |         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) |         data.append(line) | ||||||
|  |  | ||||||
|     col_width = max(len(word) for row in data for word in row[2:])   # padding |     col_width = max(len(word) for row in data for word in row[2:])   # padding | ||||||
|     firstcol_width = max(len(row[0]) for row in data )   # padding |     firstcol_width = max(len(row[0]) for row in data)   # padding | ||||||
|     secondcol_width = max(len(row[1]) for row in data )   # padding |     secondcol_width = max(len(row[1]) for row in data)   # padding | ||||||
|     for row in data: |     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)) |         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(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 = [] |         result = [] | ||||||
|         # assumes that list of rqs and list of propgatedpths have same order |         # assumes that list of rqs and list of propgatedpths have same order | ||||||
|         for i,p in enumerate(propagatedpths): |         for i, pth in enumerate(propagatedpths): | ||||||
|             result.append(Result_element(rqs[i],p)) |             result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) | ||||||
|         temp = path_result_json(result) |         temp = path_result_json(result) | ||||||
|         fnamecsv = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.csv' |         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' |         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: |         with open(fnamejson, 'w', encoding='utf-8') as fjson: | ||||||
|             f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) |             fjson.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) | ||||||
|             with open(fnamecsv,"w", encoding='utf-8') as fcsv : |             with open(fnamecsv, "w", encoding='utf-8') as fcsv: | ||||||
|                 jsontocsv(temp,equipment,fcsv) |                 jsontocsv(temp, equipment, fcsv) | ||||||
|                 print('\x1b[1;34;40m'+f'saving in {args.output} and {fnamecsv}'+ '\x1b[0m') |                 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, |       0.0359549, | ||||||
|       5.82851 |       5.82851 | ||||||
|       ], |       ], | ||||||
|  |       "f_min": 191.35e12, | ||||||
|  |       "f_max": 196.1e12, | ||||||
|       "nf_ripple": [ |       "nf_ripple": [ | ||||||
|       -0.3110761646066259, |       -0.3110761646066259, | ||||||
|       -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 json import loads | ||||||
| from collections import Counter | from collections import Counter | ||||||
| from logging import getLogger, basicConfig, INFO, ERROR, DEBUG | 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 matplotlib.pyplot import show, axis, figure, title, text | ||||||
| from networkx import (draw_networkx_nodes, draw_networkx_edges, | from networkx import (draw_networkx_nodes, draw_networkx_edges, | ||||||
|                       draw_networkx_labels, dijkstra_path) |                       draw_networkx_labels, dijkstra_path) | ||||||
| from gnpy.core.network import load_network, build_network, save_network | from gnpy.core.network import load_network, build_network, save_network, load_sim_params, configure_network | ||||||
| from gnpy.core.elements import Transceiver, Fiber, Edfa, Roadm | 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.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.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__) | logger = getLogger(__name__) | ||||||
|  |  | ||||||
| @@ -97,7 +99,7 @@ def plot_results(network, path, source, destination, infos): | |||||||
|     show() |     show() | ||||||
|  |  | ||||||
|  |  | ||||||
| def main(network, equipment, source, destination, req = None): | def main(network, equipment, source, destination, sim_params, req=None): | ||||||
|     result_dicts = {} |     result_dicts = {} | ||||||
|     network_data = [{ |     network_data = [{ | ||||||
|                     'network_name'  : str(args.filename), |                     'network_name'  : str(args.filename), | ||||||
| @@ -106,8 +108,8 @@ def main(network, equipment, source, destination, req = None): | |||||||
|                     }] |                     }] | ||||||
|     result_dicts.update({'network': network_data}) |     result_dicts.update({'network': network_data}) | ||||||
|     design_data = [{ |     design_data = [{ | ||||||
|                     'power_mode'        : equipment['Spans']['default'].power_mode, |                     'power_mode'        : equipment['Span']['default'].power_mode, | ||||||
|                     'span_power_range'  : equipment['Spans']['default'].delta_power_range_db, |                     'span_power_range'  : equipment['Span']['default'].delta_power_range_db, | ||||||
|                     'design_pch'        : equipment['SI']['default'].power_dbm, |                     'design_pch'        : equipment['SI']['default'].power_dbm, | ||||||
|                     'baud_rate'         : equipment['SI']['default'].baud_rate |                     'baud_rate'         : equipment['SI']['default'].baud_rate | ||||||
|                     }] |                     }] | ||||||
| @@ -115,17 +117,23 @@ def main(network, equipment, source, destination, req = None): | |||||||
|     simulation_data = [] |     simulation_data = [] | ||||||
|     result_dicts.update({'simulation results': 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}', |     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_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) |     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) |     build_network(network, equipment, pref_ch_db, pref_total_db) | ||||||
|     path = compute_constrained_path(network, req) |     path = compute_constrained_path(network, req) | ||||||
|  |  | ||||||
|     spans = [s.length for s in path if isinstance(s, Fiber)] |     if len([s.length for s in path if isinstance(s, RamanFiber)]): | ||||||
|     print(f'\nThere are {len(spans)} fiber spans over {sum(spans):.0f}m between {source.uid} and {destination.uid}') |         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}:') |     print(f'\nNow propagating between {source.uid} and {destination.uid}:') | ||||||
|  |  | ||||||
|     try: |     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]') |         print('invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]') | ||||||
|         power_range = [0] |         power_range = [0] | ||||||
|  |  | ||||||
|  |     if not power_mode: | ||||||
|  |         #power cannot be changed in gain mode | ||||||
|  |         power_range = [0] | ||||||
|     for dp_db in power_range: |     for dp_db in power_range: | ||||||
|         req.power = db2lin(pref_ch_db + dp_db)*1e-3 |         req.power = db2lin(pref_ch_db + dp_db)*1e-3 | ||||||
|         print(f'\nPropagating with input power = {lin2db(req.power*1e3):.2f}dBm :') |         if power_mode: | ||||||
|         infos = propagate2(path, req, equipment, show=len(power_range)==1) |             print(f'\nPropagating with input power = {ansi_escapes.cyan}{lin2db(req.power*1e3):.2f} dBm{ansi_escapes.reset}:') | ||||||
|         print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :') |         else: | ||||||
|         print(destination) |             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'\n !!!!!!!!!!!!!!!!!     TEST POINT         !!!!!!!!!!!!!!!!!!!!!') | ||||||
|         #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}') |         #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}') | ||||||
|         # => use "in" or "out" parameter |         # => use "in" or "out" parameter | ||||||
|         # => use "nli" or "ase" or "signal" or "total" parameter |         # => use "nli" or "ase" or "signal" or "total" parameter         | ||||||
|  |         if power_mode: | ||||||
|         simulation_data.append({ |             simulation_data.append({ | ||||||
|                     'Pch_dBm'               : pref_ch_db + dp_db, |                         'Pch_dBm'               : pref_ch_db + dp_db, | ||||||
|                     'OSNR_ASE_0.1nm'        : round(mean(destination.osnr_ase_01nm),2), |                         'OSNR_ASE_0.1nm'        : round(mean(destination.osnr_ase_01nm),2), | ||||||
|                     'OSNR_ASE_signal_bw'    : round(mean(destination.osnr_ase),2), |                         'OSNR_ASE_signal_bw'    : round(mean(destination.osnr_ase),2), | ||||||
|                     'SNR_nli_signal_bw'     : round(mean(destination.osnr_nli),2), |                         'SNR_nli_signal_bw'     : round(mean(destination.osnr_nli),2), | ||||||
|                     'SNR_total_signal_bw'   : round(mean(destination.snr),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') |     write_csv(result_dicts, 'simulation_result.csv') | ||||||
|     return path, infos |     return path, infos | ||||||
|  |  | ||||||
| @@ -162,6 +192,9 @@ def main(network, equipment, source, destination, req = None): | |||||||
| parser = ArgumentParser() | parser = ArgumentParser() | ||||||
| parser.add_argument('-e', '--equipment', type=Path, | parser.add_argument('-e', '--equipment', type=Path, | ||||||
|                     default=Path(__file__).parent / 'eqpt_config.json') |                     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('-pl', '--plot', action='store_true') | ||||||
| parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') | parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') | ||||||
| parser.add_argument('-l', '--list-nodes', action='store_true', help='list all transceiver nodes') | parser.add_argument('-l', '--list-nodes', action='store_true', help='list all transceiver nodes') | ||||||
| @@ -177,8 +210,19 @@ if __name__ == '__main__': | |||||||
|     args = parser.parse_args() |     args = parser.parse_args() | ||||||
|     basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, DEBUG)) |     basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, DEBUG)) | ||||||
|  |  | ||||||
|     equipment = load_equipment(args.equipment) |     try: | ||||||
|     network = load_network(args.filename, equipment, args.names_matching) |         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: |     if args.plot: | ||||||
|         plot_baseline(network) |         plot_baseline(network) | ||||||
| @@ -238,6 +282,7 @@ if __name__ == '__main__': | |||||||
|     params['trx_mode'] = '' |     params['trx_mode'] = '' | ||||||
|     params['source'] = source.uid |     params['source'] = source.uid | ||||||
|     params['destination'] = destination.uid |     params['destination'] = destination.uid | ||||||
|  |     params['bidir'] = False | ||||||
|     params['nodes_list'] = [destination.uid] |     params['nodes_list'] = [destination.uid] | ||||||
|     params['loose_list'] = ['strict'] |     params['loose_list'] = ['strict'] | ||||||
|     params['format'] = '' |     params['format'] = '' | ||||||
| @@ -247,9 +292,19 @@ if __name__ == '__main__': | |||||||
|         trx_params['power'] = db2lin(float(args.power))*1e-3 |         trx_params['power'] = db2lin(float(args.power))*1e-3 | ||||||
|     params.update(trx_params) |     params.update(trx_params) | ||||||
|     req = Path_request(**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) |     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: |     if not args.source: | ||||||
|         print(f'\n(No source node specified: picked {source.uid})') |         print(f'\n(No source node specified: picked {source.uid})') | ||||||
|     elif not valid_source: |     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 json import dumps | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from difflib import get_close_matches | from difflib import get_close_matches | ||||||
|  | from gnpy.core.utils import silent_remove | ||||||
|  | from gnpy.core.exceptions import NetworkTopologyError | ||||||
| import time | import time | ||||||
|  |  | ||||||
| all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) | all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) | ||||||
| @@ -54,7 +56,9 @@ class Node(object): | |||||||
|         'region':       '', |         'region':       '', | ||||||
|         'latitude':     0, |         'latitude':     0, | ||||||
|         'longitude':    0, |         'longitude':    0, | ||||||
|         'node_type':    'ILA' |         'node_type':    'ILA', | ||||||
|  |         'booster_restriction' : '', | ||||||
|  |         'preamp_restriction'  : '' | ||||||
|     } |     } | ||||||
|  |  | ||||||
| class Link(object): | class Link(object): | ||||||
| @@ -111,11 +115,12 @@ class Eqpt(object): | |||||||
|     { |     { | ||||||
|             'from_city':        '', |             'from_city':        '', | ||||||
|             'to_city':          '', |             'to_city':          '', | ||||||
|             'east_amp_type':         '', |             'east_amp_type':    '', | ||||||
|             'east_att_in':           0, |             'east_att_in':      0, | ||||||
|             'east_amp_gain':         0, |             'east_amp_gain':    None, | ||||||
|             'east_tilt':             0, |             'east_amp_dp':      None, | ||||||
|             'east_att_out':          0 |             '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) |             slice_out = read_slice(my_sheet, start_line+iteration, slice_in, h0) | ||||||
|             iteration += 1 |             iteration += 1 | ||||||
|         if slice_out == (-1, -1): |         if slice_out == (-1, -1): | ||||||
|             if h0 == 'east': |             if h0 in ('east', 'Node A', 'Node Z', 'City') : | ||||||
|                 print(f'\x1b[1;31;40m'+f'CRITICAL: missing _east_ header above other headers (hierarchical) _ ABORT'+ '\x1b[0m') |                 print(f'\x1b[1;31;40m'+f'CRITICAL: missing _{h0}_ header: EXECUTION ENDS'+ '\x1b[0m') | ||||||
|                 exit() |                 exit() | ||||||
|             else: |             else: | ||||||
|                 print(f'missing header {h0}') |                 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=[]): | def convert_file(input_filename, names_matching=False, filter_region=[]): | ||||||
|     nodes, links, eqpts = parse_excel(input_filename) |     nodes, links, eqpts = parse_excel(input_filename) | ||||||
|  |  | ||||||
|     if filter_region: |     if filter_region: | ||||||
|         nodes = [n for n in nodes if n.region.lower() in filter_region] |         nodes = [n for n in nodes if n.region.lower() in filter_region] | ||||||
|         cities = {n.city for n in nodes} |         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} |         cities = {lnk.from_city for lnk in links} | {lnk.to_city for lnk in links} | ||||||
|         nodes = [n for n in nodes if n.city in cities] |         nodes = [n for n in nodes if n.city in cities] | ||||||
|  |  | ||||||
|  |  | ||||||
|     global nodes_by_city |     global nodes_by_city | ||||||
|     nodes_by_city = {n.city: n for n in nodes} |     nodes_by_city = {n.city: n for n in nodes} | ||||||
|  |  | ||||||
|     #create matching dictionary for node name mismatch analysis |     #create matching dictionary for node name mismatch analysis | ||||||
|  |  | ||||||
|     cities = {''.join(c.strip() for c in n.city.split('C+L')).lower(): n.city for n in nodes} |     cities = {''.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, |                                         'latitude':  x.latitude, | ||||||
|                                         'longitude': x.longitude}}, |                                         'longitude': x.longitude}}, | ||||||
|               'type': 'Roadm'} |               '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}', |             [{'uid': f'west fused spans in {x.city}', | ||||||
|               'metadata': {'location': {'city':      x.city, |               'metadata': {'location': {'city':      x.city, | ||||||
|                                         'region':    x.region, |                                         'region':    x.region, | ||||||
| @@ -344,10 +361,12 @@ def convert_file(input_filename, names_matching=False, filter_region=[]): | |||||||
|               'type': 'Edfa', |               'type': 'Edfa', | ||||||
|               'type_variety': e.east_amp_type, |               'type_variety': e.east_amp_type, | ||||||
|               'operational': {'gain_target': e.east_amp_gain, |               'operational': {'gain_target': e.east_amp_gain, | ||||||
|  |                               'delta_p':     e.east_amp_dp, | ||||||
|                               'tilt_target': e.east_tilt, |                               'tilt_target': e.east_tilt, | ||||||
|                               'out_voa'    : e.east_att_out} |                               '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}', |             [{'uid': f'west edfa in {e.from_city} to {e.to_city}', | ||||||
|               'metadata': {'location': {'city':      nodes_by_city[e.from_city].city, |               'metadata': {'location': {'city':      nodes_by_city[e.from_city].city, | ||||||
|                                         'region':    nodes_by_city[e.from_city].region, |                                         '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': 'Edfa', | ||||||
|               'type_variety': e.west_amp_type, |               'type_variety': e.west_amp_type, | ||||||
|               'operational': {'gain_target': e.west_amp_gain, |               'operational': {'gain_target': e.west_amp_gain, | ||||||
|  |                               'delta_p':     e.west_amp_dp, | ||||||
|                               'tilt_target': e.west_tilt, |                               'tilt_target': e.west_tilt, | ||||||
|                               'out_voa'    : e.west_att_out} |                               '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': |         'connections': | ||||||
|             list(chain.from_iterable([eqpt_connection_by_city(n.city) |             list(chain.from_iterable([eqpt_connection_by_city(n.city) | ||||||
|             for n in nodes])) |             for n in nodes])) | ||||||
| @@ -411,7 +454,9 @@ def parse_excel(input_filename): | |||||||
|         'Region':       'region', |         'Region':       'region', | ||||||
|         'Latitude':     'latitude', |         'Latitude':     'latitude', | ||||||
|         'Longitude':    'longitude', |         'Longitude':    'longitude', | ||||||
|         'Type':         'node_type' |         'Type':         'node_type', | ||||||
|  |         'Booster_restriction': 'booster_restriction', | ||||||
|  |         'Preamp_restriction': 'preamp_restriction' | ||||||
|     } |     } | ||||||
|     eqpt_headers = \ |     eqpt_headers = \ | ||||||
|     {  'Node A': 'from_city', |     {  'Node A': 'from_city', | ||||||
| @@ -420,6 +465,7 @@ def parse_excel(input_filename): | |||||||
|             'amp type':         'east_amp_type', |             'amp type':         'east_amp_type', | ||||||
|             'att_in':           'east_att_in', |             'att_in':           'east_att_in', | ||||||
|             'amp gain':         'east_amp_gain', |             'amp gain':         'east_amp_gain', | ||||||
|  |             'delta p':          'east_amp_dp', | ||||||
|             'tilt':             'east_tilt', |             'tilt':             'east_tilt', | ||||||
|             'att_out':          'east_att_out' |             'att_out':          'east_att_out' | ||||||
|        }, |        }, | ||||||
| @@ -427,6 +473,7 @@ def parse_excel(input_filename): | |||||||
|             'amp type':         'west_amp_type', |             'amp type':         'west_amp_type', | ||||||
|             'att_in':           'west_att_in', |             'att_in':           'west_att_in', | ||||||
|             'amp gain':         'west_amp_gain', |             'amp gain':         'west_amp_gain', | ||||||
|  |             'delta p':          'west_amp_dp', | ||||||
|             'tilt':             'west_tilt', |             'tilt':             'west_tilt', | ||||||
|             'att_out':          'west_att_out' |             'att_out':          'west_att_out' | ||||||
|        } |        } | ||||||
| @@ -462,10 +509,13 @@ def parse_excel(input_filename): | |||||||
|     # sanity check |     # sanity check | ||||||
|     all_cities = Counter(n.city for n in nodes) |     all_cities = Counter(n.city for n in nodes) | ||||||
|     if len(all_cities) != len(nodes): |     if len(all_cities) != len(nodes): | ||||||
|         ValueError(f'Duplicate city: {all_cities}') |         raise ValueError(f'Duplicate city: {all_cities}') | ||||||
|     if any(ln.from_city not in all_cities or |     bad_links = [] | ||||||
|            ln.to_city   not in all_cities for ln in links): |     for lnk in links: | ||||||
|         ValueError(f'Bad link.') |         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 |     return nodes, links, eqpts | ||||||
|  |  | ||||||
| @@ -566,12 +616,12 @@ def midpoint(city_a, city_b): | |||||||
| #output_json_file_name = 'coronet_conus_example.json' | #output_json_file_name = 'coronet_conus_example.json' | ||||||
| #TODO get column size automatically from tupple size | #TODO get column size automatically from tupple size | ||||||
|  |  | ||||||
| NODES_COLUMN = 7 | NODES_COLUMN = 10 | ||||||
| NODES_LINE = 4 | NODES_LINE = 4 | ||||||
| LINKS_COLUMN = 16 | LINKS_COLUMN = 16 | ||||||
| LINKS_LINE = 3 | LINKS_LINE = 3 | ||||||
| EQPTS_LINE = 3 | EQPTS_LINE = 3 | ||||||
| EQPTS_COLUMN = 12 | EQPTS_COLUMN = 14 | ||||||
| parser = ArgumentParser() | parser = ArgumentParser() | ||||||
| parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls') | parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls') | ||||||
| parser.add_argument('-f', '--filter-region', action='append', default=[]) | parser.add_argument('-f', '--filter-region', action='append', default=[]) | ||||||
|   | |||||||
| @@ -7,25 +7,26 @@ gnpy.core.elements | |||||||
|  |  | ||||||
| This module contains standard network 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 | object and returns a copy with appropriate fields affected. This structure | ||||||
| represents spectral information that is "propogated" by this network element. | represents spectral information that is "propogated" by this network element. | ||||||
| Network elements must have only a local "view" of the network and propogate | 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. | self-contained. | ||||||
|  |  | ||||||
| Network elements MUST implement two attributes .uid and .name representing a | Network elements MUST implement two attributes .uid and .name representing a | ||||||
| unique identifier and a printable name. | 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 numpy import interp, log10, mean, pi, polyfit, polyval, sum | ||||||
| from scipy.constants import c, h | from scipy.constants import c, h | ||||||
| from collections import namedtuple | from collections import namedtuple | ||||||
|  |  | ||||||
| from gnpy.core.node import Node | from gnpy.core.node import Node | ||||||
| from gnpy.core.units import UNITS | 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): | class Transceiver(Node): | ||||||
|     def __init__(self, *args, **kwargs): |     def __init__(self, *args, **kwargs): | ||||||
| @@ -41,7 +42,6 @@ class Transceiver(Node): | |||||||
|         with errstate(divide='ignore'): |         with errstate(divide='ignore'): | ||||||
|             self.baud_rate = [c.baud_rate for c in spectral_info.carriers] |             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] |             ratio_01nm = [lin2db(12.5e9/b_rate) for b_rate in self.baud_rate] | ||||||
|              |  | ||||||
|         #set raw values to record original calculation, before update_snr()             |         #set raw values to record original calculation, before update_snr()             | ||||||
|             self.raw_osnr_ase = [lin2db(divide(c.power.signal, c.power.ase)) |             self.raw_osnr_ase = [lin2db(divide(c.power.signal, c.power.ase)) | ||||||
|                             for c in spectral_info.carriers] |                             for c in spectral_info.carriers] | ||||||
| @@ -51,11 +51,14 @@ class Transceiver(Node): | |||||||
|                              for c in spectral_info.carriers] |                              for c in spectral_info.carriers] | ||||||
|             self.raw_snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase))  |             self.raw_snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase))  | ||||||
|                         for c in spectral_info.carriers] |                         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 = self.raw_osnr_ase | ||||||
|             self.osnr_ase_01nm = self.raw_osnr_ase_01nm |             self.osnr_ase_01nm = self.raw_osnr_ase_01nm | ||||||
|             self.osnr_nli = self.raw_osnr_nli |             self.osnr_nli = self.raw_osnr_nli | ||||||
|             self.snr = self.raw_snr |             self.snr = self.raw_snr | ||||||
|  |             self.snr_01nm = self.raw_snr_01nm | ||||||
|                          |                          | ||||||
|     def update_snr(self, *args): |     def update_snr(self, *args): | ||||||
|         """ |         """ | ||||||
| @@ -75,6 +78,8 @@ class Transceiver(Node): | |||||||
|                         self.raw_snr, self.baud_rate)) |                         self.raw_snr, self.baud_rate)) | ||||||
|         self.osnr_ase_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),  |         self.osnr_ase_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),  | ||||||
|                         self.raw_osnr_ase_01nm)) |                         self.raw_osnr_ase_01nm)) | ||||||
|  |         self.snr_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),  | ||||||
|  |                         self.raw_snr_01nm)) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def to_json(self): |     def to_json(self): | ||||||
| @@ -100,40 +105,45 @@ class Transceiver(Node): | |||||||
|         snr = round(mean(self.snr),2) |         snr = round(mean(self.snr),2) | ||||||
|         osnr_ase = round(mean(self.osnr_ase),2) |         osnr_ase = round(mean(self.osnr_ase),2) | ||||||
|         osnr_ase_01nm = round(mean(self.osnr_ase_01nm), 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}', |         return '\n'.join([f'{type(self).__name__} {self.uid}', | ||||||
|  |  | ||||||
|                           f'  OSNR ASE (0.1nm, dB):      {osnr_ase_01nm:.2f}', |                           f'  OSNR ASE (0.1nm, dB):      {osnr_ase_01nm:.2f}', | ||||||
|                           f'  OSNR ASE (signal bw, dB):  {osnr_ase:.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): |     def __call__(self, spectral_info): | ||||||
|         self._calc_snr(spectral_info) |         self._calc_snr(spectral_info) | ||||||
|         return 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): | class Roadm(Node): | ||||||
|     def __init__(self, *args, params=None, **kwargs): |     def __init__(self, *args, params, **kwargs): | ||||||
|         if params is None: |         if 'per_degree_target_pch_out_db' not in params.keys(): | ||||||
|             # default loss value if not mentioned in loaded network json |             params['per_degree_target_pch_out_db'] = [] | ||||||
|             params = {'loss':None} |  | ||||||
|         super().__init__(*args, params=RoadmParams(**params), **kwargs) |         super().__init__(*args, params=RoadmParams(**params), **kwargs) | ||||||
|         self.loss = self.params.loss |         self.loss = 0 #auto-design interest | ||||||
|         self.target_pch_out_db = None #set in Networks.py by def set_roadm_loss |         self.effective_loss = None | ||||||
|         self.effective_pch_out_db = None |         self.effective_pch_out_db = self.params.target_pch_out_db | ||||||
|         self.effective_loss = None #set in self.propagate |  | ||||||
|         self.passive = True |         self.passive = True | ||||||
|  |         self.restrictions = self.params.restrictions | ||||||
|  |         self.per_degree_target_pch_out_db = self.params.per_degree_target_pch_out_db | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def to_json(self): |     def to_json(self): | ||||||
|         return {'uid'       : self.uid, |         return {'uid'       : self.uid, | ||||||
|                 'type'      : type(self).__name__, |                 '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'      : { |                 'metadata'      : { | ||||||
|                     'location': self.metadata['location']._asdict() |                     'location': self.metadata['location']._asdict() | ||||||
|                                     } |                                 } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
| @@ -141,35 +151,45 @@ class Roadm(Node): | |||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         return '\n'.join([f'{type(self).__name__} {self.uid}', |         return '\n'.join([f'{type(self).__name__} {self.uid}', | ||||||
|                           f'  loss (dB):     {self.effective_loss:.2f}', |                           f'  effective loss (dB):  {self.effective_loss:.2f}', | ||||||
|                           f'  pch out (dBm): {self.effective_pch_out_db!r}']) |                           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'] |         #pin_target and loss are read from eqpt_config.json['Roadm'] | ||||||
|         #all ingress channels in xpress are set to this power level |         #all ingress channels in xpress are set to this power level | ||||||
|         #but add channels are not, so we define an effective loss |         #but add channels are not, so we define an effective loss | ||||||
|         #in the case of add channels |         #in the case of add channels | ||||||
|         if self.target_pch_out_db: |         if self.per_degree_target_pch_out_db: | ||||||
|             self.effective_loss = pref.pi - self.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: |         else: | ||||||
|              self.effective_loss = self.loss |             # if no per degree target power are defined, use the global one | ||||||
|         self.effective_pch_out_db = pref.pi - self.effective_loss |             temp = self.params.target_pch_out_db | ||||||
|         attenuation = db2lin(self.effective_loss) |         self.effective_pch_out_db = min(pref.p_spani, temp) | ||||||
|  |         self.effective_loss = pref.p_spani - self.effective_pch_out_db | ||||||
|         for carrier in carriers: |         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 = carrier.power | ||||||
|             pwr = pwr._replace(signal=pwr.signal/attenuation, |             pwr = pwr._replace( signal = pwr.signal/carrier_att, | ||||||
|                                nonlinear_interference=pwr.nli/attenuation, |                                 nli = pwr.nli/carrier_att, | ||||||
|                                amplified_spontaneous_emission=pwr.ase/attenuation) |                                 ase = pwr.ase/carrier_att) | ||||||
|             yield carrier._replace(power=pwr) |             yield carrier._replace(power=pwr) | ||||||
|  |  | ||||||
|     def update_pref(self, pref): |     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): |     def __call__(self, spectral_info, degree): | ||||||
|         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) |         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers, degree=degree)) | ||||||
|         pref = self.update_pref(spectral_info.pref) |         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') | FusedParams = namedtuple('FusedParams', 'loss') | ||||||
|  |  | ||||||
| @@ -186,6 +206,9 @@ class Fused(Node): | |||||||
|     def to_json(self): |     def to_json(self): | ||||||
|         return {'uid'       : self.uid, |         return {'uid'       : self.uid, | ||||||
|                 'type'      : type(self).__name__, |                 'type'      : type(self).__name__, | ||||||
|  |                 'params'    :{ | ||||||
|  |                     'loss': self.loss | ||||||
|  |                 }, | ||||||
|                 'metadata'      : { |                 'metadata'      : { | ||||||
|                     'location': self.metadata['location']._asdict() |                     'location': self.metadata['location']._asdict() | ||||||
|                                     } |                                     } | ||||||
| @@ -204,17 +227,17 @@ class Fused(Node): | |||||||
|         for carrier in carriers: |         for carrier in carriers: | ||||||
|             pwr = carrier.power |             pwr = carrier.power | ||||||
|             pwr = pwr._replace(signal=pwr.signal/attenuation, |             pwr = pwr._replace(signal=pwr.signal/attenuation, | ||||||
|                                nonlinear_interference=pwr.nli/attenuation, |                                nli=pwr.nli/attenuation, | ||||||
|                                amplified_spontaneous_emission=pwr.ase/attenuation) |                                ase=pwr.ase/attenuation) | ||||||
|             yield carrier._replace(power=pwr) |             yield carrier._replace(power=pwr) | ||||||
|  |  | ||||||
|     def update_pref(self, pref): |     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): |     def __call__(self, spectral_info): | ||||||
|         carriers = tuple(self.propagate(*spectral_info.carriers)) |         carriers = tuple(self.propagate(*spectral_info.carriers)) | ||||||
|         pref = self.update_pref(spectral_info.pref) |         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 \ | FiberParams = namedtuple('FiberParams', 'type_variety length loss_coef length_units \ | ||||||
|                                          att_in con_in con_out dispersion gamma') |                                          att_in con_in con_out dispersion gamma') | ||||||
| @@ -283,12 +306,12 @@ class Fiber(Node): | |||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def fiber_loss(self): |     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 |         return self.loss_coef * self.length + self.con_in + self.con_out | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def loss(self): |     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 |         return self.loss_coef * self.length + self.con_in + self.con_out + self.att_in | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
| @@ -313,16 +336,13 @@ class Fiber(Node): | |||||||
|  |  | ||||||
|     def carriers(self, loc, attr): |     def carriers(self, loc, attr): | ||||||
|         """retrieve carriers information |         """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')): |         if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')): | ||||||
|             yield None |             yield None | ||||||
|             return |             return | ||||||
|         power_dict = { |  | ||||||
|                         'nli':      'nonlinear_interference', |  | ||||||
|                         'ase':      'amplified_spontaneous_emission' |  | ||||||
|                     } |  | ||||||
|         attr = power_dict.get(attr, attr) |  | ||||||
|         loc_attr = 'carriers_'+loc |         loc_attr = 'carriers_'+loc | ||||||
|         for c in getattr(self, loc_attr) : |         for c in getattr(self, loc_attr) : | ||||||
|             if attr == 'total': |             if attr == 'total': | ||||||
| @@ -330,46 +350,30 @@ class Fiber(Node): | |||||||
|             else: |             else: | ||||||
|                 yield c.power._asdict().get(attr, None) |                 yield c.power._asdict().get(attr, None) | ||||||
|  |  | ||||||
|     def beta2(self, ref_wavelength=None): |     def beta2(self, ref_wavelength=1550e-9): | ||||||
|         """ Returns beta2 from dispersion parameter. |         """Returns beta2 from dispersion parameter. | ||||||
|         Dispersion is entered in ps/nm/km. |         Dispersion is entered in ps/nm/km. | ||||||
|         Disperion can be a numpy array or a single value.  If a |         Disperion can be a numpy array or a single value. | ||||||
|         value ref_wavelength is not entered 1550e-9m will be assumed. |  | ||||||
|         ref_wavelength can be a numpy array. |         :param ref_wavelength: can be a numpy array; default: 1550nm | ||||||
|         """ |         """ | ||||||
|         # TODO|jla: discuss beta2 as method or attribute |         # TODO|jla: discuss beta2 as method or attribute | ||||||
|         wl = 1550e-9 if ref_wavelength is None else ref_wavelength |  | ||||||
|         D = abs(self.dispersion) |         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 |         return b2 # s/Hz/m | ||||||
|  |  | ||||||
|     def dbkm_2_lin(self): |     def dbkm_2_lin(self): | ||||||
|         """ calculates the linear loss coefficient |         """calculates the linear loss coefficient""" | ||||||
|         """ |         # linear loss coefficient in dB/km^-1 | ||||||
|         # alpha_pcoef is linear loss coefficient in dB/km^-1 |  | ||||||
|         # alpha_acoef is linear loss field amplitude coefficient in m^-1 |  | ||||||
|         alpha_pcoef = self.loss_coef |         alpha_pcoef = self.loss_coef | ||||||
|  |         # linear loss field amplitude coefficient in m^-1 | ||||||
|         alpha_acoef = alpha_pcoef / (2 * 10 * log10(exp(1))) |         alpha_acoef = alpha_pcoef / (2 * 10 * log10(exp(1))) | ||||||
|         return alpha_pcoef, alpha_acoef |         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): |     def _gn_analytic(self, carrier, *carriers): | ||||||
|         """ Computes the nonlinear interference power on a single carrier. |         """Computes the nonlinear interference power on a single carrier. | ||||||
|         The method uses eq. 120 from arXiv:1209.0394. |         The method uses eq. 120 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__. | ||||||
|  |  | ||||||
|         :param carrier: the signal under analysis |         :param carrier: the signal under analysis | ||||||
|         :param carriers: the full WDM comb |         :param carriers: the full WDM comb | ||||||
|         :return: carrier_nli: the amount of nonlinear interference in W on the under analysis |         :return: carrier_nli: the amount of nonlinear interference in W on the under analysis | ||||||
| @@ -377,7 +381,7 @@ class Fiber(Node): | |||||||
|  |  | ||||||
|         g_nli = 0 |         g_nli = 0 | ||||||
|         for interfering_carrier in carriers: |         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 \ |             g_nli += (interfering_carrier.power.signal/interfering_carrier.baud_rate)**2 \ | ||||||
|                      * (carrier.power.signal/carrier.baud_rate) * psi |                      * (carrier.power.signal/carrier.baud_rate) * psi | ||||||
|  |  | ||||||
| @@ -396,8 +400,8 @@ class Fiber(Node): | |||||||
|         for carrier in carriers: |         for carrier in carriers: | ||||||
|             pwr = carrier.power |             pwr = carrier.power | ||||||
|             pwr = pwr._replace(signal=pwr.signal/attenuation, |             pwr = pwr._replace(signal=pwr.signal/attenuation, | ||||||
|                                nonlinear_interference=pwr.nli/attenuation, |                                nli=pwr.nli/attenuation, | ||||||
|                                amplified_spontaneous_emission=pwr.ase/attenuation) |                                ase=pwr.ase/attenuation) | ||||||
|             carrier = carrier._replace(power=pwr) |             carrier = carrier._replace(power=pwr) | ||||||
|             chan.append(carrier) |             chan.append(carrier) | ||||||
|  |  | ||||||
| @@ -409,20 +413,77 @@ class Fiber(Node): | |||||||
|             pwr = carrier.power |             pwr = carrier.power | ||||||
|             carrier_nli = self._gn_analytic(carrier, *carriers) |             carrier_nli = self._gn_analytic(carrier, *carriers) | ||||||
|             pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation/attenuation, |             pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation/attenuation, | ||||||
|                                nonlinear_interference=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation, |                                nli=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation, | ||||||
|                                amplified_spontaneous_emission=pwr.ase/self.lin_attenuation/attenuation) |                                ase=pwr.ase/self.lin_attenuation/attenuation) | ||||||
|             yield carrier._replace(power=pwr) |             yield carrier._replace(power=pwr) | ||||||
|  |  | ||||||
|     def update_pref(self, pref): |     def update_pref(self, pref): | ||||||
|         self.pch_out_db = round(pref.pi - self.loss, 2) |         self.pch_out_db = round(pref.p_spani - self.loss, 2) | ||||||
|         return pref._replace(p_span0=pref.p0, p_spani=self.pch_out_db) |         return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db) | ||||||
|  |  | ||||||
|     def __call__(self, spectral_info): |     def __call__(self, spectral_info): | ||||||
|         self.carriers_in = spectral_info.carriers |         self.carriers_in = spectral_info.carriers | ||||||
|         carriers = tuple(self.propagate(*spectral_info.carriers)) |         carriers = tuple(self.propagate(*spectral_info.carriers)) | ||||||
|         pref = self.update_pref(spectral_info.pref) |         pref = self.update_pref(spectral_info.pref) | ||||||
|         self.carriers_out = carriers |         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: | class EdfaParams: | ||||||
|     def __init__(self, **params): |     def __init__(self, **params): | ||||||
| @@ -430,16 +491,16 @@ class EdfaParams: | |||||||
|         if params == {}: |         if params == {}: | ||||||
|             self.type_variety = '' |             self.type_variety = '' | ||||||
|             self.type_def = '' |             self.type_def = '' | ||||||
|             self.gain_flatmax = 0 |             # self.gain_flatmax = 0 | ||||||
|             self.gain_min = 0 |             # self.gain_min = 0 | ||||||
|             self.p_max = 0 |             # self.p_max = 0 | ||||||
|             self.nf_model = None |             # self.nf_model = None | ||||||
|             self.nf_fit_coeff = None |             # self.nf_fit_coeff = None | ||||||
|             self.nf_ripple = None |             # self.nf_ripple = None | ||||||
|             self.dgt = None |             # self.dgt = None | ||||||
|             self.gain_ripple = None |             # self.gain_ripple = None | ||||||
|             self.out_voa_auto = False |             # self.out_voa_auto = False | ||||||
|             self.allowed_for_design = None |             # self.allowed_for_design = None | ||||||
|  |  | ||||||
|     def update_params(self, kwargs): |     def update_params(self, kwargs): | ||||||
|         for k,v in kwargs.items() : |         for k,v in kwargs.items() : | ||||||
| @@ -447,22 +508,33 @@ class EdfaParams: | |||||||
|                 if isinstance(v, dict) else v) |                 if isinstance(v, dict) else v) | ||||||
|  |  | ||||||
| class EdfaOperational: | class EdfaOperational: | ||||||
|     def __init__(self, gain_target, tilt_target, out_voa=None): |     default_values = \ | ||||||
|         self.gain_target = gain_target |     { | ||||||
|         self.tilt_target = tilt_target |         'gain_target':      None, | ||||||
|         self.out_voa = out_voa |         '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): |     def __repr__(self): | ||||||
|         return (f'{type(self).__name__}(' |         return (f'{type(self).__name__}(' | ||||||
|                 f'gain_target={self.gain_target!r}, ' |                 f'gain_target={self.gain_target!r}, ' | ||||||
|                 f'tilt_target={self.tilt_target!r})') |                 f'tilt_target={self.tilt_target!r})') | ||||||
|  |  | ||||||
| class Edfa(Node): | class Edfa(Node): | ||||||
|     def __init__(self, *args, params={}, operational={}, **kwargs): |     def __init__(self, *args, params=None, operational=None, **kwargs): | ||||||
|         #TBC is this useful? put in comment for now: |         if params is None: | ||||||
|         #if params is None: |             params = {} | ||||||
|         #    params = {} |         if operational is None: | ||||||
|         #if operational is None: |             operational = {} | ||||||
|         #    operational = {} |  | ||||||
|         super().__init__( |         super().__init__( | ||||||
|             *args, |             *args, | ||||||
|             params=EdfaParams(**params), |             params=EdfaParams(**params), | ||||||
| @@ -479,14 +551,16 @@ class Edfa(Node): | |||||||
|         self.pin_db = None |         self.pin_db = None | ||||||
|         self.nch = None |         self.nch = None | ||||||
|         self.pout_db = 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.target_pch_out_db = None | ||||||
|         self.effective_pch_out_db = None |         self.effective_pch_out_db = None | ||||||
|         self.passive = False |         self.passive = False | ||||||
|         self.effective_gain = self.operational.gain_target |  | ||||||
|         self.att_in = None |         self.att_in = None | ||||||
|         self.carriers_in = None |         self.carriers_in = None | ||||||
|         self.carriers_out = 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 |     @property | ||||||
|     def to_json(self): |     def to_json(self): | ||||||
| @@ -494,9 +568,10 @@ class Edfa(Node): | |||||||
|                 'type'          : type(self).__name__, |                 'type'          : type(self).__name__, | ||||||
|                 'type_variety'  : self.params.type_variety, |                 'type_variety'  : self.params.type_variety, | ||||||
|                 'operational'   : { |                 'operational'   : { | ||||||
|                     'gain_target' : self.operational.gain_target, |                     'gain_target' : self.effective_gain, | ||||||
|                     'tilt_target' : self.operational.tilt_target, |                     'delta_p'     : self.delta_p, | ||||||
|                     'out_voa'     : self.operational.out_voa |                     'tilt_target' : self.tilt_target, | ||||||
|  |                     'out_voa'     : self.out_voa | ||||||
|                 }, |                 }, | ||||||
|                 'metadata'      : { |                 'metadata'      : { | ||||||
|                     'location': self.metadata['location']._asdict() |                     'location': self.metadata['location']._asdict() | ||||||
| @@ -528,23 +603,20 @@ class Edfa(Node): | |||||||
|                           f'  pad att_in (dB):        {self.att_in:.2f}', |                           f'  pad att_in (dB):        {self.att_in:.2f}', | ||||||
|                           f'  Power In (dBm):         {self.pin_db:.2f}', |                           f'  Power In (dBm):         {self.pin_db:.2f}', | ||||||
|                           f'  Power Out (dBm):        {self.pout_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'  target pch (dBm):       {self.target_pch_out_db!r}', | ||||||
|                           f'  effective pch (dBm):    {self.effective_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): |     def carriers(self, loc, attr): | ||||||
|         """retrieve carriers information |         """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')): |         if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')): | ||||||
|             yield None |             yield None | ||||||
|             return |             return | ||||||
|         power_dict = { |  | ||||||
|                         'nli':      'nonlinear_interference', |  | ||||||
|                         'ase':      'amplified_spontaneous_emission' |  | ||||||
|                     } |  | ||||||
|         attr = power_dict.get(attr, attr) |  | ||||||
|         loc_attr = 'carriers_'+loc |         loc_attr = 'carriers_'+loc | ||||||
|         for c in getattr(self, loc_attr) : |         for c in getattr(self, loc_attr) : | ||||||
|             if attr == 'total': |             if attr == 'total': | ||||||
| @@ -553,69 +625,106 @@ class Edfa(Node): | |||||||
|                 yield c.power._asdict().get(attr, None) |                 yield c.power._asdict().get(attr, None) | ||||||
|  |  | ||||||
|     def interpol_params(self, frequencies, pin, baud_rates, pref): |     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 : |         set the edfa class __init__ None parameters : | ||||||
|                 self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple |                 self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple | ||||||
|         """ |         """ | ||||||
|         # TODO|jla: read amplifier actual frequencies from additional params in json |         # 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.channel_freq = frequencies | ||||||
|         self.interpol_dgt = interp(self.channel_freq, amplifier_freq, self.params.dgt) |         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_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.interpol_nf_ripple =interp(self.channel_freq, amplifier_freq, self.params.nf_ripple) | ||||||
|  |  | ||||||
|         self.nch = frequencies.size |         self.nch = frequencies.size | ||||||
|         self.pin_db = lin2db(sum(pin*1e3)) |         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""" |         This power target is used calculate the amplifier gain""" | ||||||
|         if self.dp_db is not None: |         if self.delta_p is not None: | ||||||
|             self.target_pch_out_db = round(self.dp_db + pref.p0, 2) |             self.target_pch_out_db = round(self.delta_p + pref.p_span0, 2) | ||||||
|             self.effective_gain = self.target_pch_out_db - pref.pi |             self.effective_gain = self.target_pch_out_db - pref.p_spani | ||||||
|         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) |  | ||||||
|  |  | ||||||
|  |         """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.nf = self._calc_nf() | ||||||
|         self.gprofile = self._gain_profile(pin) |         self.gprofile = self._gain_profile(pin) | ||||||
|  |  | ||||||
|         pout = (pin + self.noise_profile(baud_rates))*db2lin(self.gprofile) |         pout = (pin + self.noise_profile(baud_rates))*db2lin(self.gprofile) | ||||||
|         self.pout_db = lin2db(sum(pout*1e3)) |         self.pout_db = lin2db(sum(pout*1e3)) | ||||||
|         self.operational.gain_target = self.effective_gain |  | ||||||
|         # ase & nli are only calculated in signal bandwidth |         # ase & nli are only calculated in signal bandwidth | ||||||
|         #    pout_db is not the absolute full output power (negligible if sufficient channels) |         #    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): |     def _calc_nf(self, avg = False): | ||||||
|         """nf calculation based on 2 models: self.params.nf_model.enabled from json import: |         """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 |         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""" |         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: |         # gain_min > gain_target TBD: | ||||||
|         pad = max(self.params.gain_min - self.effective_gain, 0) |         if self.params.type_def == 'dual_stage': | ||||||
|         self.att_in = pad |             g1 = self.params.preamp_gain_flatmax | ||||||
|         gain_target = self.effective_gain + pad |             g2 = self.effective_gain - g1 | ||||||
|         dg = max(self.params.gain_flatmax - gain_target, 0) |             nf1_avg, pad = self._nf( self.params.preamp_type_def,  | ||||||
|         if self.params.type_def == 'variable_gain': |                                 self.params.preamp_nf_model, | ||||||
|             g1a = gain_target - self.params.nf_model.delta_p - dg |                                 self.params.preamp_nf_fit_coeff, | ||||||
|             nf_avg = lin2db(db2lin(self.params.nf_model.nf1) + db2lin(self.params.nf_model.nf2)/db2lin(g1a)) |                                 self.params.preamp_gain_min, | ||||||
|         elif self.params.type_def == 'fixed_gain': |                                 self.params.preamp_gain_flatmax,  | ||||||
|             nf_avg = self.params.nf_model.nf0 |                                 g1) | ||||||
|         elif self.params.type_def == 'openroadm': |             #no padding expected for the 1stage because g1 = gain_max | ||||||
|             pin_ch = self.pin_db - lin2db(self.nch) |             nf2_avg, pad = self._nf( self.params.booster_type_def, | ||||||
|             # model OSNR = f(Pin) |                                 self.params.booster_nf_model, | ||||||
|             nf_avg = pin_ch - polyval(self.params.nf_model.nf_coef, pin_ch) + 58 |                                 self.params.booster_nf_fit_coeff, | ||||||
|         else: |                                 self.params.booster_gain_min, | ||||||
|             nf_avg = polyval(self.params.nf_fit_coeff, -dg) |                                 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: |         if avg: | ||||||
|             return nf_avg + pad |             return nf_avg | ||||||
|         else: |         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): |     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 |         noise is calculated at amplifier input | ||||||
|  |  | ||||||
|         :bw: signal bandwidth = baud rate in Hz |         :bw: signal bandwidth = baud rate in Hz | ||||||
| @@ -705,7 +814,7 @@ class Edfa(Node): | |||||||
|  |  | ||||||
|         # Calculate the target slope - currently assumes equal spaced channels |         # Calculate the target slope - currently assumes equal spaced channels | ||||||
|         # TODO|jla: support arbitrary channel spacing |         # 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 |         # first estimate of DGT scaling | ||||||
|         if abs(dgt_slope) > 0.001: # check for zero value due to flat dgt |         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 |         return g1st - voa + array(self.interpol_dgt) * dgts3 | ||||||
|  |  | ||||||
|     def propagate(self, pref, *carriers): |     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 |         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]) |         freq = array([c.frequency for c in carriers]) | ||||||
|         brate = array([c.baud_rate for c in carriers]) |         brate = array([c.baud_rate for c in carriers]) | ||||||
| @@ -779,22 +888,22 @@ class Edfa(Node): | |||||||
|  |  | ||||||
|         gains = db2lin(self.gprofile) |         gains = db2lin(self.gprofile) | ||||||
|         carrier_ases = self.noise_profile(brate) |         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): |         for gain, carrier_ase, carrier in zip(gains, carrier_ases, carriers): | ||||||
|             pwr = carrier.power |             pwr = carrier.power | ||||||
|             pwr = pwr._replace(signal=pwr.signal*gain/att, |             pwr = pwr._replace(signal=pwr.signal*gain/att, | ||||||
|                                nonlinear_interference=pwr.nli*gain/att, |                                nli=pwr.nli*gain/att, | ||||||
|                                amplified_spontaneous_emission=(pwr.ase+carrier_ase)*gain/att) |                                ase=(pwr.ase+carrier_ase)*gain/att) | ||||||
|             yield carrier._replace(power=pwr) |             yield carrier._replace(power=pwr) | ||||||
|  |  | ||||||
|     def update_pref(self, pref): |     def update_pref(self, pref): | ||||||
|         return pref._replace(p_span0=pref.p0, |         return pref._replace(p_span0=pref.p_span0, | ||||||
|                             p_spani=pref.pi + self.effective_gain - self.operational.out_voa) |                             p_spani=pref.p_spani + self.effective_gain - self.out_voa) | ||||||
|  |  | ||||||
|     def __call__(self, spectral_info): |     def __call__(self, spectral_info): | ||||||
|         self.carriers_in = spectral_info.carriers |         self.carriers_in = spectral_info.carriers | ||||||
|         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) |         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) | ||||||
|         pref = self.update_pref(spectral_info.pref) |         pref = self.update_pref(spectral_info.pref) | ||||||
|         self.carriers_out = carriers |         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 numpy import clip, polyval | ||||||
| from sys import exit |  | ||||||
| from operator import itemgetter | from operator import itemgetter | ||||||
| from math import isclose | from math import isclose | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| @@ -17,64 +16,169 @@ from json import load | |||||||
| from gnpy.core.utils import lin2db, db2lin, load_json | from gnpy.core.utils import lin2db, db2lin, load_json | ||||||
| from collections import namedtuple | from collections import namedtuple | ||||||
| from gnpy.core.elements import Edfa | from gnpy.core.elements import Edfa | ||||||
|  | from gnpy.core.exceptions import EquipmentConfigError | ||||||
|  | import time | ||||||
|  |  | ||||||
| Model_vg = namedtuple('Model_vg', 'nf1 nf2 delta_p') | Model_vg = namedtuple('Model_vg', 'nf1 nf2 delta_p') | ||||||
| Model_fg = namedtuple('Model_fg', 'nf0') | Model_fg = namedtuple('Model_fg', 'nf0') | ||||||
| Model_openroadm = namedtuple('Model_openroadm', 'nf_coef') | Model_openroadm = namedtuple('Model_openroadm', 'nf_coef') | ||||||
| Fiber = namedtuple('Fiber', 'type_variety dispersion gamma') | Model_hybrid = namedtuple('Model_hybrid', 'nf_ram gain_ram edfa_variety') | ||||||
| Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \ | Model_dual_stage = namedtuple('Model_dual_stage', 'preamp_variety booster_variety') | ||||||
|                              max_loss padding EOL con_in con_out') |  | ||||||
| Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') | class common: | ||||||
| Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target add_drop_osnr') |     def update_attr(self, default_values, kwargs, name): | ||||||
| SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ |         clean_kwargs = {k:v for k, v in kwargs.items() if v != ''} | ||||||
|                        power_dbm power_range_db tx_osnr sys_margins') |         for k, v in default_values.items(): | ||||||
| AmpBase = namedtuple( |             setattr(self, k, clean_kwargs.get(k, v)) | ||||||
|     'AmpBase', |             if k not in clean_kwargs and name != 'Amp': | ||||||
|     'type_variety type_def gain_flatmax gain_min p_max' |                 print(f'\x1b[1;31;40m'+ | ||||||
|     ' nf_model nf_fit_coeff nf_ripple dgt gain_ripple out_voa_auto allowed_for_design') |                       f'\n WARNING missing {k} attribute in eqpt_config.json[{name}]'+ | ||||||
| class Amp(AmpBase): |                       f'\n default value is {k} = {v}'+ | ||||||
|     def __new__(cls, |                       f'\x1b[0m') | ||||||
|             type_variety, type_def, gain_flatmax, gain_min, p_max, nf_model=None, |                 time.sleep(1) | ||||||
|             nf_fit_coeff=None, nf_ripple=None, dgt=None, gain_ripple=None, |  | ||||||
|              out_voa_auto=False, allowed_for_design=True): | class SI(common): | ||||||
|         return super().__new__(cls, |     default_values =\ | ||||||
|             type_variety, type_def, gain_flatmax, gain_min, p_max, |     { | ||||||
|             nf_model, nf_fit_coeff, nf_ripple, dgt, gain_ripple, |         "f_min":            191.35e12, | ||||||
|             out_voa_auto, allowed_for_design) |         "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 |     @classmethod | ||||||
|     def from_advanced_json(cls, filename, **kwargs): |     def from_json(cls, filename, **kwargs): | ||||||
|         with open(filename, encoding='utf-8') as f: |         config = Path(filename).parent / 'default_edfa_config.json' | ||||||
|             json_data = load(f) |  | ||||||
|         return cls(**{**kwargs, **json_data, 'type_def':None, 'nf_model':None}) |  | ||||||
|  |  | ||||||
|     @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_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 |         nf_def = None | ||||||
|  |         dual_stage_def = None | ||||||
|  |  | ||||||
|         if type_def == 'fixed_gain': |         if type_def == 'fixed_gain': | ||||||
|             try: |             try: | ||||||
|                 nf0 = kwargs.pop('nf0') |                 nf0 = kwargs.pop('nf0') | ||||||
|             except KeyError: #nf0 is expected for a fixed gain amp |             except KeyError: #nf0 is expected for a fixed gain amp | ||||||
|                 print(f'missing nf0 value input for amplifier: {type_variety} in eqpt_config.json') |                 raise EquipmentConfigError(f'missing nf0 value input for amplifier: {type_variety} in equipment config') | ||||||
|                 exit() |             for k in ('nf_min', 'nf_max'): | ||||||
|             try: #remove all remaining nf inputs |                 try: | ||||||
|                 del kwargs['nf_min'] |                     del kwargs[k] | ||||||
|                 del kwargs['nf_max'] |                 except KeyError: | ||||||
|             except KeyError: pass #nf_min and nf_max are not needed for fixed gain amp |                     pass | ||||||
|             nf_def = Model_fg(nf0) |             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': |         elif type_def == 'variable_gain': | ||||||
|             gain_min, gain_max = kwargs['gain_min'], kwargs['gain_flatmax'] |             gain_min, gain_max = kwargs['gain_min'], kwargs['gain_flatmax'] | ||||||
|             try: #nf_min and nf_max are expected for a variable gain amp |             try: #nf_min and nf_max are expected for a variable gain amp | ||||||
|                 nf_min = kwargs.pop('nf_min') |                 nf_min = kwargs.pop('nf_min') | ||||||
|                 nf_max = kwargs.pop('nf_max') |                 nf_max = kwargs.pop('nf_max') | ||||||
|             except KeyError: |             except KeyError: | ||||||
|                 print(f'missing nf_min/max value input for amplifier: {type_variety} in eqpt_config.json') |                 raise EquipmentConfigError(f'missing nf_min or nf_max value input for amplifier: {type_variety} in equipment config') | ||||||
|                 exit() |  | ||||||
|             try: #remove all remaining nf inputs |             try: #remove all remaining nf inputs | ||||||
|                 del kwargs['nf0'] |                 del kwargs['nf0'] | ||||||
|             except KeyError: pass #nf0 is not needed for variable gain amp |             except KeyError: pass #nf0 is not needed for variable gain amp | ||||||
| @@ -84,19 +188,28 @@ class Amp(AmpBase): | |||||||
|             try: |             try: | ||||||
|                 nf_coef = kwargs.pop('nf_coef') |                 nf_coef = kwargs.pop('nf_coef') | ||||||
|             except KeyError: #nf_coef is expected for openroadm amp |             except KeyError: #nf_coef is expected for openroadm amp | ||||||
|                 print(f'missing nf_coef input for amplifier: {type_variety} in eqpt_config.json') |                 raise EquipmentConfigError(f'missing nf_coef input for amplifier: {type_variety} in equipment config') | ||||||
|                 exit() |  | ||||||
|             nf_def = Model_openroadm(nf_coef) |             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): | def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max): | ||||||
|     if nf_min < -10: |     if nf_min < -10: | ||||||
|         print(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}') |         raise EquipmentConfigError(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}') | ||||||
|         exit() |  | ||||||
|     if nf_max < -10: |     if nf_max < -10: | ||||||
|         print(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}') |         raise EquipmentConfigError(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}') | ||||||
|         exit() |  | ||||||
|  |  | ||||||
|     # NF estimation model based on nf_min and nf_max |     # NF estimation model based on nf_min and nf_max | ||||||
|     # delta_p:  max power dB difference between first and second stage coils |     # 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)) |     nf1 = lin2db(db2lin(nf_min) - db2lin(nf2)/db2lin(g1a_max)) | ||||||
|  |  | ||||||
|     if nf1 < 4: |     if nf1 < 4: | ||||||
|         print(f'First coil value too low {nf1} for amplifier {type_variety}') |         raise EquipmentConfigError(f'First coil value too low {nf1} for amplifier {type_variety}') | ||||||
|         exit() |  | ||||||
|  |  | ||||||
|     # Check 1 dB < delta_p < 6 dB to ensure nf_min and nf_max values make sense. |     # 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: |     # 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))) |         g1a_max = lin2db(db2lin(nf2) / (db2lin(nf_min) - db2lin(nf1))) | ||||||
|         delta_p = gain_max - g1a_max |         delta_p = gain_max - g1a_max | ||||||
|         g1a_min = gain_min - (gain_max-gain_min) - delta_p |         g1a_min = gain_min - (gain_max-gain_min) - delta_p | ||||||
|         if not 1 < delta_p < 6: |         if not 1 < delta_p < 11: | ||||||
|             print(f'Computed \N{greek capital letter delta}P invalid \ |             raise EquipmentConfigError(f'Computed \N{greek capital letter delta}P invalid \ | ||||||
|                 \n 1st coil vs 2nd coil calculated DeltaP {delta_p:.2f} for \ |                 \n 1st coil vs 2nd coil calculated DeltaP {delta_p:.2f} for \ | ||||||
|                 \n amplifier {type_variety} is not valid: revise inputs \ |                 \n amplifier {type_variety} is not valid: revise inputs \ | ||||||
|                 \n calculated 1st coil NF = {nf1:.2f}, 2nd coil NF = {nf2:.2f}') |                 \n calculated 1st coil NF = {nf1:.2f}, 2nd coil NF = {nf2:.2f}') | ||||||
|             exit() |  | ||||||
|     # Check calculated values for nf1 and nf2 |     # Check calculated values for nf1 and nf2 | ||||||
|     calc_nf_min = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_max)) |     calc_nf_min = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_max)) | ||||||
|     if not isclose(nf_min, calc_nf_min, abs_tol=0.01): |     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}') |         raise EquipmentConfigError(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}') | ||||||
|         exit() |  | ||||||
|     calc_nf_max = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_min)) |     calc_nf_max = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_min)) | ||||||
|     if not isclose(nf_max, calc_nf_max, abs_tol=0.01): |     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}') |         raise EquipmentConfigError(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}') | ||||||
|         exit() |  | ||||||
|  |  | ||||||
|     return nf1, nf2, delta_p |     return nf1, nf2, delta_p | ||||||
|  |  | ||||||
| @@ -145,7 +254,7 @@ def edfa_nf(gain_target, variety_type, equipment): | |||||||
|     amp_params = equipment['Edfa'][variety_type] |     amp_params = equipment['Edfa'][variety_type] | ||||||
|     amp = Edfa( |     amp = Edfa( | ||||||
|             uid = f'calc_NF', |             uid = f'calc_NF', | ||||||
|             params = amp_params._asdict(), |             params = amp_params.__dict__, | ||||||
|             operational = { |             operational = { | ||||||
|                 'gain_target': gain_target, |                 'gain_target': gain_target, | ||||||
|                 'tilt_target': 0 |                 '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)""" |     """return the trx and SI parameters from eqpt_config for a given type_variety and mode (ie format)""" | ||||||
|     trx_params = {} |     trx_params = {} | ||||||
|     default_si_data = equipment['SI']['default'] |     default_si_data = equipment['SI']['default'] | ||||||
|      |  | ||||||
|     try: |     try: | ||||||
|         trxs = equipment['Transceiver'] |         trxs = equipment['Transceiver'] | ||||||
|         #if called from path_requests_run.py, trx_mode is filled with None when not specified by user |         #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} |             trx_params = {**mode_params} | ||||||
|             # sanity check: spacing baudrate must be smaller than min spacing |             # sanity check: spacing baudrate must be smaller than min spacing | ||||||
|             if trx_params['baud_rate'] > trx_params['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"]}" '+\ |                 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}.' |                     f'has baud rate: {trx_params["baud_rate"]*1e-9} GHz greater than min_spacing {trx_params["min_spacing"]*1e-9}.') | ||||||
|                 print(msg) |  | ||||||
|                 exit() |  | ||||||
|         else: |         else: | ||||||
|             mode_params = {"format": "undetermined", |             mode_params = {"format": "undetermined", | ||||||
|                        "baud_rate": None, |                            "baud_rate": None, | ||||||
|                        "OSNR": None, |                            "OSNR": None, | ||||||
|                        "bit_rate": None, |                            "bit_rate": None, | ||||||
|                        "roll_off": None, |                            "roll_off": None, | ||||||
|                        "tx_osnr":None, |                            "tx_osnr":None, | ||||||
|                        "min_spacing":None, |                            "min_spacing":None, | ||||||
|                        "cost":None} |                            "cost":None} | ||||||
|             trx_params = {**mode_params}  |             trx_params = {**mode_params} | ||||||
|         trx_params['f_min'] = equipment['Transceiver'][trx_type_variety].frequency['min'] |         trx_params['f_min'] = equipment['Transceiver'][trx_type_variety].frequency['min'] | ||||||
|         trx_params['f_max'] = equipment['Transceiver'][trx_type_variety].frequency['max'] |         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}') |         # print(f'spacing {temp}') | ||||||
|     except StopIteration : |     except StopIteration : | ||||||
|         if error_message: |         if error_message: | ||||||
|             print(f'could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library') |             raise EquipmentConfigError(f'Computation stoped: could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library') | ||||||
|             print('Computation stopped.') |  | ||||||
|             exit() |  | ||||||
|         else: |         else: | ||||||
|             # default transponder charcteristics |             # default transponder charcteristics | ||||||
|             # mainly used with transmission_main_example.py |             # 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']) |             nch = automatic_nch(trx_params['f_min'], trx_params['f_max'], trx_params['spacing']) | ||||||
|             trx_params['nb_channel'] = nch |             trx_params['nb_channel'] = nch | ||||||
|             print(f'There are {nch} channels propagating') |             print(f'There are {nch} channels propagating') | ||||||
|                  |  | ||||||
|     trx_params['power'] =  db2lin(default_si_data.power_dbm)*1e-3 |     trx_params['power'] =  db2lin(default_si_data.power_dbm)*1e-3 | ||||||
|  |  | ||||||
|     return trx_params |     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): | def automatic_spacing(baud_rate): | ||||||
|     """return the min possible channel spacing for a given baud rate""" |     """return the min possible channel spacing for a given baud rate""" | ||||||
|     # TODO : this should parametrized in a cfg file |     # 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 |     # list of possible tuples [(max_baud_rate, spacing_for_this_baud_rate)] | ||||||
|                                                 #[(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) |     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): | 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 |             m['OSNR'] = m['OSNR'] + equipment['SI']['default'].sys_margins | ||||||
|     return equipment |     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): | def equipment_from_json(json_data, filename): | ||||||
|     """build global dictionnary eqpt_library that stores all eqpt characteristics: |     """build global dictionnary eqpt_library that stores all eqpt characteristics: | ||||||
|     edfa type type_variety, fiber type_variety |     edfa type type_variety, fiber type_variety | ||||||
| @@ -257,15 +390,12 @@ def equipment_from_json(json_data, filename): | |||||||
|         equipment[key] = {} |         equipment[key] = {} | ||||||
|         typ = globals()[key] |         typ = globals()[key] | ||||||
|         for entry in entries: |         for entry in entries: | ||||||
|             subkey = entry.get('type_variety', 'default')            |             subkey = entry.get('type_variety', 'default') | ||||||
|             if key == 'Edfa': |             if key == 'Edfa': | ||||||
|                 if 'advanced_config_from_json' in entry: |                 equipment[key][subkey] = Amp.from_json(filename, **entry) | ||||||
|                     config = Path(filename).parent / entry.pop('advanced_config_from_json') |             else: | ||||||
|                     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] = typ(**entry) |                 equipment[key][subkey] = typ(**entry) | ||||||
|     equipment = update_trx_osnr(equipment) |     equipment = update_trx_osnr(equipment) | ||||||
|  |     equipment = update_dual_stage(equipment) | ||||||
|  |     roadm_restrictions_sanity_check(equipment) | ||||||
|     return 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 | 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.utils import load_json | ||||||
| from gnpy.core.equipment import automatic_nch, automatic_spacing | from gnpy.core.equipment import automatic_nch, automatic_spacing | ||||||
|  |  | ||||||
| class ConvenienceAccess: | class Power(namedtuple('Power', 'signal nli ase')): | ||||||
|  |     """carriers power in W""" | ||||||
|     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 nonlinear_interference amplified_spontaneous_emission'), ConvenienceAccess): | class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power')): | ||||||
|  |     pass | ||||||
|     _ABBREVS = {'nli': 'nonlinear_interference', |  | ||||||
|                 'ase': 'amplified_spontaneous_emission',} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 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', |     def __new__(cls, pref, carriers): | ||||||
|                 'pi' :  'p_spani'} |  | ||||||
|  |  | ||||||
| class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers'), ConvenienceAccess): |  | ||||||
|  |  | ||||||
|     def __new__(cls, pref=Pref(0, 0), *carriers): |  | ||||||
|         return super().__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): | 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 in dB : convert power lin into power in dB | ||||||
|     pref = lin2db(power * 1e3) |     pref = lin2db(power * 1e3) | ||||||
|     si = SpectralInformation(pref=Pref(pref, pref)) |  | ||||||
|     nb_channel = automatic_nch(f_min, f_max, spacing) |     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), |             Channel(f, (f_min+spacing*f), | ||||||
|             baud_rate, roll_off, Power(power, 0, 0)) for f in range(1,nb_channel+1) |             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() |     si = SpectralInformation() | ||||||
|     spacing = 0.05 # THz |     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}') | ||||||
|     print(f'si = {si.carriers[0].power.nli}') |     print(f'si = {si.carriers[0].power.nli}') | ||||||
|     print(f'si = {si.carriers[20].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)) |                               for c in si.carriers)) | ||||||
|     print(f'si2 = {si2}') |     print(f'si2 = {si2}') | ||||||
|   | |||||||
| @@ -14,13 +14,15 @@ from numpy import arange | |||||||
| from scipy.interpolate import interp1d | from scipy.interpolate import interp1d | ||||||
| from logging import getLogger | from logging import getLogger | ||||||
| from os import path | from os import path | ||||||
| from operator import itemgetter | from operator import itemgetter, attrgetter | ||||||
| from gnpy.core import elements | 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.equipment import edfa_nf | ||||||
|  | from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError | ||||||
| from gnpy.core.units import UNITS | from gnpy.core.units import UNITS | ||||||
| from gnpy.core.utils import load_json, save_json, round2float, db2lin, lin2db | from gnpy.core.utils import (load_json, save_json, round2float, db2lin, | ||||||
| from sys import exit |                             merge_amplifier_restrictions) | ||||||
|  | from gnpy.core.science_utils import SimParams | ||||||
| from collections import namedtuple | from collections import namedtuple | ||||||
|  |  | ||||||
| logger = getLogger(__name__) | logger = getLogger(__name__) | ||||||
| @@ -52,11 +54,12 @@ def network_from_json(json_data, equipment): | |||||||
|         variety = el_config.pop('type_variety', 'default') |         variety = el_config.pop('type_variety', 'default') | ||||||
|         if typ in equipment and variety in equipment[typ]: |         if typ in equipment and variety in equipment[typ]: | ||||||
|             extra_params = equipment[typ][variety] |             extra_params = equipment[typ][variety] | ||||||
|             el_config.setdefault('params', {}).update(extra_params._asdict()) |             temp = el_config.setdefault('params', {}) | ||||||
|         elif typ in ['Edfa', 'Fiber']: #catch it now because the code will crash later! |             temp = merge_amplifier_restrictions(temp, extra_params.__dict__) | ||||||
|             print( f'The {typ} of variety type {variety} was not recognized:' |             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') |                     '\nplease check it is properly defined in the eqpt_config json file') | ||||||
|             exit() |  | ||||||
|         cls = getattr(elements, typ) |         cls = getattr(elements, typ) | ||||||
|         el = cls(**el_config) |         el = cls(**el_config) | ||||||
|         g.add_node(el) |         g.add_node(el) | ||||||
| @@ -66,11 +69,13 @@ def network_from_json(json_data, equipment): | |||||||
|     for cx in json_data['connections']: |     for cx in json_data['connections']: | ||||||
|         from_node, to_node = cx['from_node'], cx['to_node'] |         from_node, to_node = cx['from_node'], cx['to_node'] | ||||||
|         try: |         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: |         except KeyError: | ||||||
|             msg = f'In {__name__} network_from_json function:\n\tcan not find {from_node} or {to_node} defined in {cx}' |             raise NetworkTopologyError(f'can not find {from_node} or {to_node} defined in {cx}') | ||||||
|             print(msg) |  | ||||||
|             exit(1) |  | ||||||
|  |  | ||||||
|     return g |     return g | ||||||
|  |  | ||||||
| @@ -87,16 +92,27 @@ def network_to_json(network): | |||||||
|     data.update(connections) |     data.update(connections) | ||||||
|     return data |     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 |     """amplifer selection algorithm | ||||||
|     @Orange Jean-Luc Augé |     @Orange Jean-Luc Augé | ||||||
|     """ |     """ | ||||||
|     Edfa_list = namedtuple('Edfa_list', 'variety power gain nf') |     Edfa_list = namedtuple('Edfa_list', 'variety power gain_min nf') | ||||||
|     TARGET_EXTENDED_GAIN = 2.1 |     TARGET_EXTENDED_GAIN = equipment['Span']['default'].target_extended_gain | ||||||
|     #MAX_EXTENDED_GAIN = 5 |  | ||||||
|     edfa_dict = equipment['Edfa'] |     # 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 |     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( |     edfa_list = [Edfa_list( | ||||||
|                 variety=edfa_variety, |                 variety=edfa_variety, | ||||||
|                 power=min( |                 power=min( | ||||||
| @@ -106,88 +122,118 @@ def select_edfa(gain_target, power_target, equipment): | |||||||
|                     edfa.p_max |                     edfa.p_max | ||||||
|                     ) |                     ) | ||||||
|                     -power_target, |                     -power_target, | ||||||
|                 gain=edfa.gain_flatmax-gain_target, |                 gain_min= | ||||||
|  |                     gain_target+3 | ||||||
|  |                     -edfa.gain_min, | ||||||
|                 nf=edfa_nf(gain_target, edfa_variety, equipment)) \ |                 nf=edfa_nf(gain_target, edfa_variety, equipment)) \ | ||||||
|                 for edfa_variety, edfa in edfa_dict.items() |                 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 = \ |     #consider a Raman list because of different gain_min requirement:  | ||||||
|     list(filter(lambda x : x.gain>-TARGET_EXTENDED_GAIN, edfa_list)) |     #do not allow extended gain min for Raman | ||||||
|     if len(acceptable_gain_list) < 1: |     raman_list = [Edfa_list( | ||||||
|         #no amplifier satisfies the required gain, so pick the highest gain: |                 variety=edfa_variety, | ||||||
|         gain_max = max(edfa_list, key=itemgetter(2)).gain |                 power=min( | ||||||
|         #pick up all amplifiers that share this max gain: |                     pin | ||||||
|         acceptable_gain_list = \ |                     +edfa.gain_flatmax | ||||||
|         list(filter(lambda x : x.gain-gain_max>-0.1, edfa_list)) |                     +TARGET_EXTENDED_GAIN, | ||||||
|     acceptable_power_list = \ |                     edfa.p_max | ||||||
|     list(filter(lambda x : x.power>=0, acceptable_gain_list)) |                     ) | ||||||
|  |                     -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: |     if len(acceptable_power_list) < 1: | ||||||
|         #no amplifier satisfies the required power, so pick the highest power: |         #no amplifier satisfies the required power, so pick the highest power(s): | ||||||
|         power_max = \ |         power_max = max(acceptable_gain_min_list, key=attrgetter('power')).power | ||||||
|         max(acceptable_gain_list, key=itemgetter(1)).power |         #check and pick if other amplifiers may have a similar gain/power | ||||||
|         #pick up all amplifiers that share this max gain: |         #allow a 0.3dB power range  | ||||||
|         acceptable_power_list = \ |         #this allows to chose an amplifier with a better NF subsequentely | ||||||
|         list(filter(lambda x : x.power-power_max>-0.1, acceptable_gain_list)) |         acceptable_power_list = [x for x in acceptable_gain_min_list | ||||||
|  |                                  if x.power-power_max>-0.3] | ||||||
|  |  | ||||||
|  |      | ||||||
|     # gain and power requirements are resolved, |     # gain and power requirements are resolved, | ||||||
|     #       =>chose the amp with the best NF among the acceptable ones: |     #       =>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): |     return selected_edfa.variety, power_reduction | ||||||
|     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 |  | ||||||
|  |  | ||||||
|     for roadm in roadms: | def target_power(network, node, equipment): #get_fiber_dp | ||||||
|         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 |  | ||||||
|     SPAN_LOSS_REF = 20 |     SPAN_LOSS_REF = 20 | ||||||
|     POWER_SLOPE = 0.3 |     POWER_SLOPE = 0.3 | ||||||
|     power_mode = equipment['Spans']['default'].power_mode |     power_mode = equipment['Span']['default'].power_mode | ||||||
|     dp_range = list(equipment['Spans']['default'].delta_power_range_db) |     dp_range = list(equipment['Span']['default'].delta_power_range_db) | ||||||
|     node_loss = span_loss(network, node) |     node_loss = span_loss(network, node) | ||||||
|  |  | ||||||
|     dp_gain_mode = 0 |  | ||||||
|     try: |     try: | ||||||
|         dp_power_mode = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2]) |         dp = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2]) | ||||||
|         dp_power_mode = max(dp_range[0], dp_power_mode) |         dp = max(dp_range[0], dp) | ||||||
|         dp_power_mode = min(dp_range[1], dp_power_mode) |         dp = min(dp_range[1], dp) | ||||||
|     except KeyError: |     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]') |               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): |     if isinstance(node, Roadm): | ||||||
|         dp_power_mode = 0 |         dp = 0 | ||||||
|  |  | ||||||
|     dp = dp_power_mode if power_mode else dp_gain_mode |  | ||||||
|     #print(f'{repr(node)} delta power in:\n{dp}dB') |  | ||||||
|  |  | ||||||
|     return dp |     return dp | ||||||
|  |  | ||||||
|  |  | ||||||
| def prev_node_generator(network, node): | def prev_node_generator(network, node): | ||||||
|     """fused spans interest: |     """fused spans interest: | ||||||
|     iterate over all predecessors while they are Fused or Fiber type""" |     iterate over all predecessors while they are Fused or Fiber type""" | ||||||
|     try: |     try: | ||||||
|         prev_node = next(n for n in network.predecessors(node)) |         prev_node = next(n for n in network.predecessors(node)) | ||||||
|     except StopIteration: |     except StopIteration: | ||||||
|         msg = f'In {__name__} prev_node_generator function:\n\t{node.uid} is not properly connected, please check network topology' |         raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology') | ||||||
|         print(msg) |  | ||||||
|         logger.critical(msg) |  | ||||||
|         exit(1) |  | ||||||
|     # yield and re-iterate |     # 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 prev_node | ||||||
|         yield from prev_node_generator(network, prev_node) |         yield from prev_node_generator(network, prev_node) | ||||||
|     else: |     else: | ||||||
| @@ -199,10 +245,9 @@ def next_node_generator(network, node): | |||||||
|     try: |     try: | ||||||
|         next_node = next(n for n in network.successors(node)) |         next_node = next(n for n in network.successors(node)) | ||||||
|     except StopIteration: |     except StopIteration: | ||||||
|         print(f'In {__name__} next_node_generator function:\n\t{node.uid}  is not properly connected, please check network topology') |         raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology') | ||||||
|         exit(1)         |  | ||||||
|     # yield and re-iterate |     # 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 next_node | ||||||
|         yield from next_node_generator(network, next_node) |         yield from next_node_generator(network, next_node) | ||||||
|     else: |     else: | ||||||
| @@ -244,23 +289,22 @@ def find_last_node(network, node): | |||||||
|         pass |         pass | ||||||
|     return this_node |     return this_node | ||||||
|  |  | ||||||
| def set_amplifier_voa(amp, pref_total_db, power_mode): | def set_amplifier_voa(amp, power_target, power_mode): | ||||||
|     VOA_MARGIN = 0 |     VOA_MARGIN = 1 #do not maximize the VOA optimization | ||||||
|     if amp.operational.out_voa is None: |     if amp.out_voa is None: | ||||||
|         if power_mode: |         if power_mode: | ||||||
|             gain_target = amp.operational.gain_target |             gain_target = amp.effective_gain | ||||||
|             pout = pref_total_db + amp.dp_db |             voa = min(amp.params.p_max-power_target, | ||||||
|             voa = min(amp.params.p_max-pout, |                       amp.params.gain_flatmax-amp.effective_gain) | ||||||
|                       amp.params.gain_flatmax-amp.operational.gain_target) |             voa = max(round2float(max(voa, 0), 0.5) - VOA_MARGIN, 0) if amp.params.out_voa_auto else 0 | ||||||
|             voa = round2float(max(voa, 0), 0.5) - VOA_MARGIN if amp.params.out_voa_auto else 0 |             amp.delta_p = amp.delta_p + voa | ||||||
|             amp.dp_db = amp.dp_db + voa |             amp.effective_gain = amp.effective_gain + voa | ||||||
|             amp.operational.gain_target = amp.operational.gain_target + voa |  | ||||||
|         else: |         else: | ||||||
|             voa = 0 # no output voa optimization in gain mode |             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): | 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)) |     next_oms = (n for n in network.successors(roadm) if not isinstance(n, Transceiver)) | ||||||
|     for oms in next_oms: |     for oms in next_oms: | ||||||
|         #go through all the OMS departing from the Roadm |         #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) |         #     node = find_last_node(next_node) | ||||||
|         #     next_node = next(n for n in network.successors(node)) |         #     next_node = next(n for n in network.successors(node)) | ||||||
|         #     next_node = find_last_node(next_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: |         while True: | ||||||
|         #go through all nodes in the OMS (loop until next Roadm instance) |         #go through all nodes in the OMS (loop until next Roadm instance) | ||||||
|             if isinstance(node, Edfa): |             if isinstance(node, Edfa): | ||||||
|                 node_loss = span_loss(network, prev_node) |                 node_loss = span_loss(network, prev_node) | ||||||
|                 dp_from_gain = prev_dp + node.operational.gain_target - node_loss \ |                 voa = node.out_voa if node.out_voa else 0 | ||||||
|                     if node.operational.gain_target > 0 else None |                 if node.delta_p is None: | ||||||
|                 dp = target_power(dp_from_gain, network, next_node, equipment) |                     dp = target_power(network, next_node, equipment) | ||||||
|                 gain_target = node_loss + dp - prev_dp |                 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: |                 power_target = pref_total_db + dp          | ||||||
|                     node.dp_db = dp |  | ||||||
|                 node.operational.gain_target = gain_target |  | ||||||
|  |  | ||||||
|                 if node.params.type_variety == '': |                 raman_allowed = False | ||||||
|                     power_target = pref_total_db + dp |                 if isinstance(prev_node, Fiber): | ||||||
|                     edfa_variety = select_edfa(gain_target, power_target, equipment) |                     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] |                     extra_params = equipment['Edfa'][edfa_variety] | ||||||
|                     node.params.update_params(extra_params._asdict()) |                     node.params.update_params(extra_params.__dict__) | ||||||
|                 set_amplifier_voa(node, pref_total_db, power_mode) |                     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): |             if isinstance(next_node, Roadm) or isinstance(next_node, Transceiver): | ||||||
|                 break |                 break | ||||||
|             prev_dp = dp |             prev_dp = dp | ||||||
|  |             prev_voa = voa | ||||||
|             prev_node = node |             prev_node = node | ||||||
|             node = next_node |             node = next_node | ||||||
|             # print(f'{node.uid}') |             # print(f'{node.uid}') | ||||||
| @@ -319,12 +415,16 @@ def add_egress_amplifier(network, node): | |||||||
|                         } |                         } | ||||||
|                     }, |                     }, | ||||||
|                     operational = { |                     operational = { | ||||||
|                         'gain_target': 0, |                         'gain_target': None, | ||||||
|                         'tilt_target': 0, |                         'tilt_target': 0, | ||||||
|                     }) |                     }) | ||||||
|         network.add_node(amp) |         network.add_node(amp) | ||||||
|         network.add_edge(node, amp) |         if isinstance(node,Fiber): | ||||||
|         network.add_edge(amp, next_node) |             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): | 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)) |         next_node = next(network.successors(fiber)) | ||||||
|         prev_node = next(network.predecessors(fiber)) |         prev_node = next(network.predecessors(fiber)) | ||||||
|     except StopIteration: |     except StopIteration: | ||||||
|  |         raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology') | ||||||
|         print(f'In {__name__} split_fiber function:\n\t{fiber.uid}   is not properly connected, please check network topology') |  | ||||||
|         exit() |  | ||||||
|  |  | ||||||
|     network.remove_node(fiber) |     network.remove_node(fiber) | ||||||
|  |  | ||||||
| @@ -385,17 +483,25 @@ def split_fiber(network, fiber, bounds, target_length, equipment): | |||||||
|                             } |                             } | ||||||
|                           }, |                           }, | ||||||
|                           params = fiber_params) |                           params = fiber_params) | ||||||
|         network.add_edge(prev_node, new_span) |         if isinstance(prev_node,Fiber): | ||||||
|         prev_node = new_span |             edgeweight = prev_node.params.length | ||||||
|     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 |  | ||||||
|         else: |         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): | def add_fiber_padding(network, fibers, padding): | ||||||
|     """last_fibers = (fiber for n in network.nodes() |     """last_fibers = (fiber for n in network.nodes() | ||||||
| @@ -407,33 +513,32 @@ def add_fiber_padding(network, fibers, padding): | |||||||
|         try: |         try: | ||||||
|             next_node = next(network.successors(fiber)) |             next_node = next(network.successors(fiber)) | ||||||
|         except StopIteration: |         except StopIteration: | ||||||
|             msg = f'In {__name__} add_fiber_padding function:\n\t{fiber.uid}   is not properly connected, please check network topology' |             raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology') | ||||||
|             print(msg) |  | ||||||
|             logger.critical(msg) |  | ||||||
|             exit(1)             |  | ||||||
|         if this_span_loss < padding and not (isinstance(next_node, Fused)): |         if this_span_loss < padding and not (isinstance(next_node, Fused)): | ||||||
|             #add a padding att_in at the input of the 1st fiber: |             #add a padding att_in at the input of the 1st fiber: | ||||||
|             #address the case when several fibers are spliced together |             #address the case when several fibers are spliced together | ||||||
|             first_fiber = find_first_node(network, fiber) |             first_fiber = find_first_node(network, fiber) | ||||||
|             if first_fiber.att_in is None: |             # in order to support no booster , fused might be placed | ||||||
|                 first_fiber.att_in = padding - this_span_loss |             # just after a roadm: need to check that first_fiber is really a fiber | ||||||
|             else : |             if isinstance(first_fiber,Fiber): | ||||||
|                 first_fiber.att_in = first_fiber.att_in + padding - this_span_loss |                 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): | 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]) |     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) |     min_length = max(int(default_span_data.padding/0.2*1e3),50_000) | ||||||
|     bounds = range(min_length, max_length) |     bounds = range(min_length, max_length) | ||||||
|     target_length = max(min_length, 90_000) |     target_length = max(min_length, 90_000) | ||||||
|     con_in = default_span_data.con_in |     default_con_in = default_span_data.con_in | ||||||
|     con_out = default_span_data.con_out + default_span_data.EOL |     default_con_out = default_span_data.con_out | ||||||
|     padding = default_span_data.padding |     padding = default_span_data.padding | ||||||
|  |  | ||||||
|     #set raodm loss for gain_mode before to build network |     #set roadm loss for gain_mode before to build network | ||||||
|     set_roadm_loss(network, equipment, pref_ch_db) |  | ||||||
|     fibers = [f for f in network.nodes() if isinstance(f, Fiber)] |     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) |     add_fiber_padding(network, fibers, padding) | ||||||
|     # don't group split fiber and add amp in the same loop |     # don't group split fiber and add amp in the same loop | ||||||
|     # =>for code clarity (at the expense of speed): |     # =>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() |     amplified_nodes = [n for n in network.nodes() | ||||||
|                         if isinstance(n, Fiber) or isinstance(n, Roadm)] |                         if isinstance(n, Fiber) or isinstance(n, Roadm)] | ||||||
|  |  | ||||||
|     for node in amplified_nodes: |     for node in amplified_nodes: | ||||||
|         add_egress_amplifier(network, node) |         add_egress_amplifier(network, node) | ||||||
|  |  | ||||||
| @@ -455,3 +561,11 @@ def build_network(network, equipment, pref_ch_db, pref_total_db): | |||||||
|         for t in trx: |         for t in trx: | ||||||
|             set_egress_amplifier(network, t, equipment, pref_total_db) |             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. | This module contains the base class for a network element. | ||||||
|  |  | ||||||
| Strictly, a network element is any callable which accepts an immutable | Strictly, a network element is any callable which accepts an immutable | ||||||
| .info.SpectralInformation object and returns a .info.SpectralInformation object | :class:`.info.SpectralInformation` object and returns an :class:`.info.SpectralInformation` object | ||||||
| (a copy.) | (a copy). | ||||||
|  |  | ||||||
| Network elements MUST implement two attributes .uid and .name representing a | Network elements MUST implement two attributes .uid and .name representing a | ||||||
| unique identifier and a printable name. | 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. | via subclassing. | ||||||
| ''' | ''' | ||||||
|  |  | ||||||
| @@ -26,10 +26,12 @@ class Location(namedtuple('Location', 'latitude longitude city region')): | |||||||
|         return super().__new__(cls, latitude, longitude, city, region) |         return super().__new__(cls, latitude, longitude, city, region) | ||||||
|  |  | ||||||
| class Node: | 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: |         if name is None: | ||||||
|             name = uid |             name = uid | ||||||
|         self.uid, self.name = uid, name |         self.uid, self.name = uid, name | ||||||
|  |         if metadata is None: | ||||||
|  |             metadata = {'location': {}} | ||||||
|         if metadata and not isinstance(metadata.get('location'), Location): |         if metadata and not isinstance(metadata.get('location'), Location): | ||||||
|             metadata['location'] = Location(**metadata.pop('location', {})) |             metadata['location'] = Location(**metadata.pop('location', {})) | ||||||
|         self.params, self.metadata, self.operational = params, metadata, operational |         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 pathlib import Path | ||||||
| from gnpy.core.equipment import load_equipment | from gnpy.core.equipment import load_equipment | ||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
|  | from gnpy.core.exceptions import ServiceError | ||||||
|  |  | ||||||
| SERVICES_COLUMN = 12 | SERVICES_COLUMN = 12 | ||||||
| #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | ||||||
| @@ -43,17 +44,18 @@ class Element: | |||||||
|         return hash((type(self), self.uid)) |         return hash((type(self), self.uid)) | ||||||
|  |  | ||||||
| class Request_element(Element): | class Request_element(Element): | ||||||
|     def __init__(self,Request,eqpt_filename): |     def __init__(self, Request, eqpt_filename, bidir): | ||||||
|         # request_id is str |         # request_id is str | ||||||
|         # excel has automatic number formatting that adds .0 on integer values |         # excel has automatic number formatting that adds .0 on integer values | ||||||
|         # the next lines recover the pure int value, assuming this .0 is unwanted |         # 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.request_id = correct_xlrd_int_to_str_reading(Request.request_id) | ||||||
|         self.source = Request.source |         self.source = f'trx {Request.source}' | ||||||
|         self.destination = Request.destination |         self.destination = f'trx {Request.destination}' | ||||||
|         # TODO: the automatic naming generated by excel parser requires that source and dest name  |         # 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. |         # be a string starting with 'trx' : this is manually added here. | ||||||
|         self.srctpid = f'trx {Request.source}' |         self.srctpid = f'trx {Request.source}' | ||||||
|         self.dsttpid = f'trx {Request.destination}' |         self.dsttpid = f'trx {Request.destination}' | ||||||
|  |         self.bidir = bidir | ||||||
|         # test that trx_type belongs to eqpt_config.json |         # test that trx_type belongs to eqpt_config.json | ||||||
|         # if not replace it with a default |         # if not replace it with a default | ||||||
|         equipment = load_equipment(eqpt_filename) |         equipment = load_equipment(eqpt_filename) | ||||||
| @@ -73,17 +75,17 @@ class Request_element(Element): | |||||||
|                 Requestmode = None |                 Requestmode = None | ||||||
|                 self.mode = Request.mode |                 self.mode = Request.mode | ||||||
|         except KeyError: |         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) |             #print(msg) | ||||||
|             logger.critical(msg) |             logger.critical(msg) | ||||||
|             exit() |             raise ServiceError(msg) | ||||||
|         # excel input are in GHz and dBm |         # excel input are in GHz and dBm | ||||||
|         if Request.spacing is not None: |         if Request.spacing is not None: | ||||||
|             self.spacing = Request.spacing * 1e9 |             self.spacing = Request.spacing * 1e9 | ||||||
|         else: |         else: | ||||||
|             msg = f'Request {self.request_id} missing spacing: spacing is mandatory.\ncomputation stopped' |             msg = f'Request {self.request_id} missing spacing: spacing is mandatory.\ncomputation stopped' | ||||||
|             logger.critical(msg) |             logger.critical(msg) | ||||||
|             exit() |             raise ServiceError(msg) | ||||||
|         if Request.power is not None: |         if Request.power is not None: | ||||||
|             self.power =  db2lin(Request.power) * 1e-3 |             self.power =  db2lin(Request.power) * 1e-3 | ||||||
|         else: |         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. |         # 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 |         # user can change this per node in the generated json | ||||||
|         self.loose = 'loose' |         self.loose = 'LOOSE' | ||||||
|         if Request.is_loose == 'no' : |         if Request.is_loose == 'no' : | ||||||
|             self.loose = 'strict' |             self.loose = 'STRICT' | ||||||
|         self.path_bandwidth = None |         self.path_bandwidth = None | ||||||
|         if Request.path_bandwidth is not None: |         if Request.path_bandwidth is not None: | ||||||
|             self.path_bandwidth = Request.path_bandwidth * 1e9 |             self.path_bandwidth = Request.path_bandwidth * 1e9 | ||||||
| @@ -132,46 +134,41 @@ class Request_element(Element): | |||||||
|     uid = property(lambda self: repr(self)) |     uid = property(lambda self: repr(self)) | ||||||
|     @property |     @property | ||||||
|     def pathrequest(self): |     def pathrequest(self): | ||||||
|  |         # Default assumption for bidir is False | ||||||
|         req_dictionnary = { |         req_dictionnary = { | ||||||
|                     'request-id':self.request_id, |                     'request-id':self.request_id, | ||||||
|                     'source':    self.source, |                     'source':    self.source, | ||||||
|                     'destination':  self.destination, |                     'destination':  self.destination, | ||||||
|                     'src-tp-id': self.srctpid, |                     'src-tp-id': self.srctpid, | ||||||
|                     'dst-tp-id': self.dsttpid, |                     'dst-tp-id': self.dsttpid, | ||||||
|  |                     'bidirectional': self.bidir, | ||||||
|                     'path-constraints':{ |                     'path-constraints':{ | ||||||
|                         'te-bandwidth': { |                         'te-bandwidth': { | ||||||
|                             'technology': 'flexi-grid', |                             'technology': 'flexi-grid', | ||||||
|                             'trx_type'  : self.trx_type, |                             'trx_type'  : self.trx_type, | ||||||
|                             'trx_mode'  : self.mode, |                             'trx_mode'  : self.mode, | ||||||
|                             'effective-freq-slot':[{'n': 'null','m': 'null'}] , |                             'effective-freq-slot':[{'N': 'null', 'M': 'null'}], | ||||||
|                             'spacing'   : self.spacing, |                             'spacing'   : self.spacing, | ||||||
|                             'max-nb-of-channel'  : self.nb_channel, |                             'max-nb-of-channel'  : self.nb_channel, | ||||||
|                             'output-power'       : self.power |                             'output-power'       : self.power | ||||||
|                             # 'path_bandwidth'       : self.path_bandwidth  |  | ||||||
|                         } |                         } | ||||||
|                     }, |                     } | ||||||
|                     'optimizations': { |                 } | ||||||
|                         'explicit-route-include-objects': [ |  | ||||||
|                         { |         if self.nodes_list: | ||||||
|                             'index': self.nodes_list.index(node), |             req_dictionnary['explicit-route-objects'] = {} | ||||||
|                             'unnumbered-hop':{ |             temp = {'route-object-include-exclude' : [ | ||||||
|                                 'node-id': f'{node}', |                         {'explicit-route-usage': 'route-include-ero', | ||||||
|                                 'link-tp-id': 'link-tp-id is not used', |                         'index': self.nodes_list.index(node), | ||||||
|                                 'hop-type': f'{self.loose}', |                         'num-unnum-hop': { | ||||||
|                                 'direction': 'direction is not used' |                             'node-id': f'{node}', | ||||||
|                             }, |                             'link-tp-id': 'link-tp-id is not used', | ||||||
|                             'label-hop':{ |                             'hop-type': f'{self.loose}', | ||||||
|                                 'te-label': { |  | ||||||
|                                     'generic': 'generic is not used', |  | ||||||
|                                     'direction': 'direction is not used' |  | ||||||
|                                 } |  | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         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: |         if self.path_bandwidth is not None: | ||||||
|             req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth |             req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth | ||||||
|              |              | ||||||
| @@ -181,33 +178,44 @@ class Request_element(Element): | |||||||
|         if self.disjoint_from : |         if self.disjoint_from : | ||||||
|             return {'synchronization-id':self.request_id, |             return {'synchronization-id':self.request_id, | ||||||
|                 'svec': { |                 'svec': { | ||||||
|                     'relaxable' : 'False', |                     'relaxable' : 'false', | ||||||
|                     'link-diverse': 'True', |                     'disjointness': 'node link', | ||||||
|                     'node-diverse': 'True', |  | ||||||
|                     'request-id-number': [self.request_id]+ [n for n in self.disjoint_from] |                     '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 |         # TO-DO: avoid multiple entries with same synchronisation vectors | ||||||
|     @property |     @property | ||||||
|     def json(self): |     def json(self): | ||||||
|         return self.pathrequest , self.pathsync |         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) |     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 |     # dumps the output into a json file with name | ||||||
|     # split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]] |     # split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]] | ||||||
|     if output_filename=='': |     if output_filename=='': | ||||||
|         output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json' |         output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json' | ||||||
|     # for debug |     # for debug | ||||||
|     # print(json_filename) |     # print(json_filename) | ||||||
|     data = { |     # if there is no sync vector , do not write any synchronization | ||||||
|         'path-request': [n.json[0] for n in req], |     synchro = [n.json[1] for n in req if n.json[1] is not None] | ||||||
|         'synchronization': [n.json[1] for n in req |     if synchro: | ||||||
|         if n.json[1] is not None] |         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: |     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 |     return data | ||||||
|  |  | ||||||
| def correct_xlrd_int_to_str_reading(v) : | def correct_xlrd_int_to_str_reading(v) : | ||||||
| @@ -232,25 +240,29 @@ def parse_excel(input_filename): | |||||||
|     return services |     return services | ||||||
|  |  | ||||||
| def parse_service_sheet(service_sheet): | def parse_service_sheet(service_sheet): | ||||||
|         logger.info(f'Validating headers on {service_sheet.name!r}') |     """ reads each column according to authorized fieldnames. order is not important. | ||||||
|         # 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 |     logger.info(f'Validating headers on {service_sheet.name!r}') | ||||||
|         header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN] if len(x.value.strip())>0] |     # 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 |     # create a service_fieldname independant from the excel column order | ||||||
|         # to be compatible with any version of the sheet |     # to be compatible with any version of the sheet | ||||||
|         # the following dictionnary records the excel field names and the corresponding parameter's name |     # the following dictionnary records the excel field names and the corresponding parameter's name | ||||||
|  |  | ||||||
|         authorized_fieldnames = {'route id':'request_id', 'Source':'source', 'Destination':'destination', \ |     authorized_fieldnames = { | ||||||
|          'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \ |         'route id':'request_id', 'Source':'source', 'Destination':'destination', \ | ||||||
|          'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\ |         'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \ | ||||||
|          'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\ |         'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\ | ||||||
|          'routing: is loose?':'is_loose', 'path bandwidth':'path_bandwidth'} |         'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\ | ||||||
|         try : |         'routing: is loose?':'is_loose', 'path bandwidth':'path_bandwidth'} | ||||||
|             service_fieldnames = [authorized_fieldnames[e] for e in header] |     try: | ||||||
|         except KeyError: |         service_fieldnames = [authorized_fieldnames[e] for e in header] | ||||||
|             msg = f'Malformed header on Service sheet: {header} field not in {authorized_fieldnames}' |     except KeyError: | ||||||
|             logger.critical(msg) |         msg = f'Malformed header on Service sheet: {header} field not in {authorized_fieldnames}' | ||||||
|             raise ValueError(msg) |         logger.critical(msg) | ||||||
|         for row in all_rows(service_sheet, start=5): |         raise ValueError(msg) | ||||||
|             yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames)) |     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 json | ||||||
|  |  | ||||||
| import numpy as np |  | ||||||
| from csv import writer | from csv import writer | ||||||
|  | import numpy as np | ||||||
| from numpy import pi, cos, sqrt, log10 | from numpy import pi, cos, sqrt, log10 | ||||||
| from scipy import constants | from scipy import constants | ||||||
|  |  | ||||||
| @@ -73,21 +73,19 @@ def c(): | |||||||
|     return constants.c |     return constants.c | ||||||
|  |  | ||||||
|  |  | ||||||
| def itufs(spacing, startf=191.35, stopf=196.10): | def arrange_frequencies(length, start, stop): | ||||||
|     """Creates an array of frequencies whose default range is |     """Create an array of frequencies | ||||||
|     191.35-196.10 THz |  | ||||||
|  |  | ||||||
|     :param spacing: Frequency spacing in THz |     :param length: number of elements | ||||||
|     :param starf: Start frequency in THz |     :param star: Start frequency in THz | ||||||
|     :param stopf: Stop frequency in THz |     :param stop: Stop frequency in THz | ||||||
|     :type spacing: float |     :type length: integer | ||||||
|     :type startf: float |     :type start: float | ||||||
|     :type stopf: float |     :type stop: float | ||||||
|     :return an array of frequnecies determined by the spacing parameter |     :return an array of frequencies determined by the spacing parameter | ||||||
|     :rtype: numpy.ndarray |     :rtype: numpy.ndarray | ||||||
|     """ |     """ | ||||||
|     return np.arange(startf, stopf + spacing / 2, spacing) |     return np.linspace(start, stop, length) | ||||||
|  |  | ||||||
|  |  | ||||||
| def h(): | 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)) |     p_inds = np.where(np.logical_and(np.abs(ffs) > 0, np.abs(ffs) < l_lim)) | ||||||
|     hf[p_inds] = 1 |     hf[p_inds] = 1 | ||||||
|     return sqrt(hf) |     return sqrt(hf) | ||||||
|  |  | ||||||
|  | def merge_amplifier_restrictions(dict1, dict2): | ||||||
|  |     """Updates contents of dicts recursively | ||||||
|  |  | ||||||
|  |     >>> d1 = {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}} | ||||||
|  |     >>> d2 = {'params': {'target_pch_out_db': -20}} | ||||||
|  |     >>> merge_amplifier_restrictions(d1, d2) | ||||||
|  |     {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}, 'target_pch_out_db': -20}} | ||||||
|  |  | ||||||
|  |     >>> d3 = {'params': {'restrictions': {'preamp_variety_list': ['foo'], 'booster_variety_list': ['bar']}}} | ||||||
|  |     >>> merge_amplifier_restrictions(d1, d3) | ||||||
|  |     {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}} | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     copy_dict1 = dict1.copy() | ||||||
|  |     for key in dict2: | ||||||
|  |         if key in dict1: | ||||||
|  |             if isinstance(dict1[key], dict): | ||||||
|  |                 copy_dict1[key] = merge_amplifier_restrictions(copy_dict1[key], dict2[key]) | ||||||
|  |         else: | ||||||
|  |             copy_dict1[key] = dict2[key] | ||||||
|  |     return copy_dict1 | ||||||
|  |  | ||||||
|  | def silent_remove(this_list, elem): | ||||||
|  |     """Remove matching elements from a list without raising ValueError | ||||||
|  |  | ||||||
|  |     >>> li = [0, 1] | ||||||
|  |     >>> li = silent_remove(li, 1) | ||||||
|  |     >>> li | ||||||
|  |     [0] | ||||||
|  |     >>> li = silent_remove(li, 1) | ||||||
|  |     >>> li | ||||||
|  |     [0] | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         this_list.remove(elem) | ||||||
|  |     except ValueError: | ||||||
|  |         pass | ||||||
|  |     return this_list | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ type’][‘subtype’]=object** | |||||||
| Every equipment type is defined in JSON root with according name and | Every equipment type is defined in JSON root with according name and | ||||||
| array of parameters as value. | array of parameters as value. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: none | ||||||
|  |  | ||||||
|     {"Edfa": [...], |     {"Edfa": [...], | ||||||
|     "Fiber": [...] |     "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 | if only one subtype exists **"type_variety"** name is not mandatory and | ||||||
| it will be marked with **”default”** value. | it will be marked with **”default”** value. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json | ||||||
|  |  | ||||||
|     {"Edfa": [{ |     {"Edfa": [{ | ||||||
|                 "type_variety": "std_medium_gain", |                 "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 |    gain/noise figure ripple. **"advanced_config_from_json"** value | ||||||
|    contains filename. |    contains filename. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json-object | ||||||
|  |  | ||||||
|     "Edfa":[{ |     "Edfa":[{ | ||||||
|             "type_variety": "high_detail_model_example", |             "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 | -  Variable gain – with JSON file describing gain figure tilt and gain/noise | ||||||
|    figure ripple. **”default_edfa_config.json”** as source file. |    figure ripple. **”default_edfa_config.json”** as source file. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json-object | ||||||
|  |  | ||||||
|     "Edfa":[{ |     "Edfa":[{ | ||||||
|             "type_variety": "std_medium_gain", |             "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 | -  Fixed gain – with JSON file describing gain figure tilt and gain/noise | ||||||
|    figure ripple. **”default_edfa_config.json”** as source file. |    figure ripple. **”default_edfa_config.json”** as source file. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json-object | ||||||
|  |  | ||||||
|     "Edfa":[{ |     "Edfa":[{ | ||||||
|             "type_variety": "std_fixed_gain", |             "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 | - openroadm – with JSON file describing gain figure tilt and gain/noise | ||||||
|    figure ripple. **”default_edfa_config.json”** as source file.  |    figure ripple. **”default_edfa_config.json”** as source file.  | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json-object | ||||||
|  |  | ||||||
|     "Edfa":[{ |     "Edfa":[{ | ||||||
|             "type_variety": "low_noise", |             "type_variety": "low_noise", | ||||||
| @@ -156,7 +156,7 @@ location is in **transmission_main_example.py** folder: | |||||||
|  |  | ||||||
| Fiber element with its parameters: | Fiber element with its parameters: | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json-object | ||||||
|  |  | ||||||
|     "Fiber":[{ |     "Fiber":[{ | ||||||
|             "type_variety": "SSMF", |             "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 | 1.2.3 Roadm element | ||||||
| ******************* | ******************* | ||||||
|  |  | ||||||
| Roadm element with its parameters: | Roadm element with its parameters: | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json-object | ||||||
|  |  | ||||||
|       "Roadms":[{ |       "Roadms":[{ | ||||||
|             "gain_mode_default_loss": 20, |             "gain_mode_default_loss": 20, | ||||||
| @@ -184,7 +230,7 @@ Roadm element with its parameters: | |||||||
|  |  | ||||||
| Spans element with its parameters: | Spans element with its parameters: | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json-object | ||||||
|  |  | ||||||
|     "Spans":[{ |     "Spans":[{ | ||||||
|             "power_mode":true, |             "power_mode":true, | ||||||
| @@ -205,7 +251,7 @@ Spans element with its parameters: | |||||||
|  |  | ||||||
| Spectral information with its parameters: | Spectral information with its parameters: | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json-object | ||||||
|  |  | ||||||
|     "SI":[{ |     "SI":[{ | ||||||
|             "f_min": 191.3e12, |             "f_min": 191.3e12, | ||||||
| @@ -227,7 +273,9 @@ Spectral information with its parameters: | |||||||
| Transceiver element with its parameters. **”mode”** can contain multiple | Transceiver element with its parameters. **”mode”** can contain multiple | ||||||
| Transceiver operation formats. | 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":[{ |     "Transceiver":[{ | ||||||
|                     "frequency":{ |                     "frequency":{ | ||||||
| @@ -288,7 +336,7 @@ Network description JSON file root consist of three unordered parts: | |||||||
|  |  | ||||||
| -  connections – contains array of unidirectional connection objects | -  connections – contains array of unidirectional connection objects | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: none | ||||||
|  |  | ||||||
|     {"network_name": "Example Network", |     {"network_name": "Example Network", | ||||||
|     "elements": [{...}, |     "elements": [{...}, | ||||||
| @@ -317,10 +365,10 @@ obligatory. | |||||||
|  |  | ||||||
| Transceiver element with its parameters. | Transceiver element with its parameters. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json | ||||||
|  |  | ||||||
|     {"uid": "trx Site_A", |     {"uid": "trx Site_A", | ||||||
|     “metadata": { |     "metadata": { | ||||||
|                 "location": { |                 "location": { | ||||||
|                             "city": "Site_A", |                             "city": "Site_A", | ||||||
|                             "region": "", |                             "region": "", | ||||||
| @@ -339,7 +387,7 @@ Transceiver element with its parameters. | |||||||
| ROADM element with its parameters. **“params”** is optional, if not used | ROADM element with its parameters. **“params”** is optional, if not used | ||||||
| default loss value of 20dB is used. | default loss value of 20dB is used. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json | ||||||
|  |  | ||||||
|     {"uid": "roadm Site_A", |     {"uid": "roadm Site_A", | ||||||
|     "metadata": { |     "metadata": { | ||||||
| @@ -363,12 +411,12 @@ default loss value of 20dB is used. | |||||||
| Fused element with its parameters. **“params”** is optional, if not used | Fused element with its parameters. **“params”** is optional, if not used | ||||||
| default loss value of 1dB is used. | default loss value of 1dB is used. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json | ||||||
|  |  | ||||||
|     {"uid": "ingress fused spans in Site_B", |     {"uid": "ingress fused spans in Site_B", | ||||||
|     "metadata": { |     "metadata": { | ||||||
|                 "location": { |                 "location": { | ||||||
|                             “city": "Site_B", |                             "city": "Site_B", | ||||||
|                             "region": "", |                             "region": "", | ||||||
|                             "latitude": 0, |                             "latitude": 0, | ||||||
|                             "longitude": 0 |                             "longitude": 0 | ||||||
| @@ -386,7 +434,7 @@ default loss value of 1dB is used. | |||||||
|  |  | ||||||
| Fiber element with its parameters. | Fiber element with its parameters. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json | ||||||
|  |  | ||||||
|     {"uid": "fiber (Site_A \\u2192 Site_B)", |     {"uid": "fiber (Site_A \\u2192 Site_B)", | ||||||
|     "metadata": { |     "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. | EDFA element with its parameters. | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json | ||||||
|  |  | ||||||
|     {"uid": "Edfa1", |     {"uid": "Edfa1", | ||||||
|     "type": "Edfa", |     "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 | two unordered **”from_node”** and **”to_node”** name pair with values | ||||||
| corresponding to element **”uid”** | corresponding to element **”uid”** | ||||||
|  |  | ||||||
| .. code-block:: | .. code-block:: json | ||||||
|  |  | ||||||
|     {"from_node": "roadm Site_C", |     {"from_node": "roadm Site_C", | ||||||
|     "to_node": "trx 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": { |       "response-id": null, | ||||||
|         "path-id": null, |       "path-properties": { | ||||||
|         "path-properties": { |         "path-metric": [ | ||||||
|           "path-metric": [ |           { | ||||||
|             { |             "metric-type": "SNR@bandwidth", | ||||||
|               "metric-type": null, |             "accumulative-value": null | ||||||
|               "accumulative-value": null |  | ||||||
|             } |  | ||||||
|           ], |  | ||||||
|           "path-srlgs": { |  | ||||||
|             "usage": "not used yet", |  | ||||||
|             "values": ["not used yet"] |  | ||||||
|           }, |           }, | ||||||
|           "path-route-objects": [ |           { | ||||||
|             { |             "metric-type": "SNR@0.1nm", | ||||||
|               "path-route-object": { |             "accumulative-value": null | ||||||
|                 "index": null, |           }, | ||||||
|                 "unnumbered-hop": { |           { | ||||||
|                   "node-id": null, |             "metric-type": "OSNR@bandwidth", | ||||||
|                   "link-tp-id": null, |             "accumulative-value": null | ||||||
|                   "hop-type": null, |           }, | ||||||
|                   "direction": "not used" |           { | ||||||
|                 }, |             "metric-type": "OSNR@0.1nm", | ||||||
|                 "label-hop": { |             "accumulative-value": null | ||||||
|                   "te-label": { |           }, | ||||||
|                     "generic": "not used yet", |           { | ||||||
|                     "direction": "not used yet" |             "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] | [pytest] | ||||||
| addopts = -p no:warnings | addopts = --doctest-modules | ||||||
|   | |||||||
| @@ -1,44 +1,14 @@ | |||||||
| alabaster==0.7.12 | alabaster>=0.7.12,<1 | ||||||
| appdirs==1.4.3 | docutils==0.15.2 | ||||||
| atomicwrites==1.2.1 | flask==1.0.2 | ||||||
| attrs==18.2.0 | flask-restful==0.3.7 | ||||||
| Babel==2.6.0 | matplotlib>=3.1.0,<4 | ||||||
| black==18.9b0 | networkx>=2.3,<3 | ||||||
| certifi==2018.10.15 | numpy>=1.16.1,<2 | ||||||
| chardet==3.0.4 | pandas==0.24.2 | ||||||
| Click==7.0 | Pygments>=2.4.2,<3 | ||||||
| cycler==0.10.0 | pytest>=4.0.0,<5 | ||||||
| decorator==4.3.0 | scipy>=1.3.0,<2 | ||||||
| docutils==0.14 | Sphinx>=2.1.1,<3 | ||||||
| idna==2.7 | sphinxcontrib-bibtex>=0.4.2,<1 | ||||||
| imagesize==1.1.0 | xlrd>=1.2.0,<2 | ||||||
| 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 |  | ||||||
|   | |||||||
| @@ -6,55 +6,72 @@ | |||||||
|       "destination": null, |       "destination": null, | ||||||
|       "src-tp-id": null, |       "src-tp-id": null, | ||||||
|       "dst-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": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
|           "trx_type": null, |           "trx_type": "name of the tsp type_variety as listed in the library", | ||||||
|           "trx_mode": null, |           "trx_mode": "optional, name of the mode as listed in the tsp type_variety", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "n": "null", | ||||||
|               "m": "null" |               "m": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": null, |           "spacing": mandatory decimal Hz, | ||||||
|           "max-nb-of-channel": null, |           "max-nb-of-channel": optional integer, | ||||||
|           "output-power": null, |           "output-power": optional decimal W, | ||||||
|           "path_bandwidth": null |           "path_bandwidth": optional bit/s | ||||||
|           } |  | ||||||
|       }, |  | ||||||
|       "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" |  | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           ] |  | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     }], |     } | ||||||
|   "synchronization": [ |   ], | ||||||
|  |   "synchronization": [   list of disjunctions, optional | ||||||
|     { |     { | ||||||
|       "synchronization-id": null, |       "synchronization-id": "3", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "True", |         "relaxable": "True", | ||||||
|         "link-diverse": "False", |         "disjointness": "node link", | ||||||
|         "node-diverse": "False", |  | ||||||
|         "request-id-number": [ |         "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( | setup( | ||||||
|     name='gnpy', |     name='gnpy', | ||||||
|     version='1.2.0', |     version='2.1', | ||||||
|     description='route planning and optimization tool for mesh optical networks', |     description='route planning and optimization tool for mesh optical networks', | ||||||
|     long_description=long_description, |     long_description=long_description, | ||||||
|     long_description_content_type='text/x-rst; charset=UTF-8', |     long_description_content_type='text/x-rst; charset=UTF-8', | ||||||
|     url='https://github.com/Telecominfraproject/gnpy', |     url='https://github.com/Telecominfraproject/gnpy', | ||||||
|     author='Telecom Infra Project', |     author='Telecom Infra Project', | ||||||
|     author_email='james.powell@telecominfraproject.com', |     author_email='jan.kundrat@telecominfraproject.com', | ||||||
|     classifiers=[ |     classifiers=[ | ||||||
|         'Development Status :: 3 - Alpha', |         'Development Status :: 3 - Alpha', | ||||||
|         'Intended Audience :: Developers', |         'Intended Audience :: Developers', | ||||||
|   | |||||||
| @@ -205,6 +205,36 @@ | |||||||
|                     "longitude": 0 |                     "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", |       "from_node": "Edfa5", | ||||||
|  |       "to_node": "Att_F" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Att_F", | ||||||
|       "to_node": "trx F" |       "to_node": "trx F" | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
| @@ -255,6 +289,10 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "from_node": "Edfa1", |       "from_node": "Edfa1", | ||||||
|  |       "to_node": "Att_B" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "from_node": "Att_B", | ||||||
|       "to_node": "trx B" |       "to_node": "trx B" | ||||||
|     } |     } | ||||||
|   ] |   ] | ||||||
|   | |||||||
| @@ -77,8 +77,11 @@ def compare_networks(expected, actual): | |||||||
| def compare_services(expected, actual): | def compare_services(expected, actual): | ||||||
|     requests = compare(expected['path-request'], actual['path-request'], |     requests = compare(expected['path-request'], actual['path-request'], | ||||||
|                        key=lambda el: el['request-id']) |                        key=lambda el: el['request-id']) | ||||||
|     synchronizations = compare(expected['synchronization'], actual['synchronization'], |     synchronizations = compare(expected['path-request'], expected['path-request'], | ||||||
|                                key=lambda el: el['synchronization-id']) |                                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) |     return ServicesResults(requests, synchronizations) | ||||||
|  |  | ||||||
| def compare_paths(expected_output, actual_output): | 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":[{ | {     "Edfa":[{ | ||||||
|             "type_variety": "CienaDB_medium_gain", |             "type_variety": "CienaDB_medium_gain", | ||||||
|  |             "type_def": "advanced_model", | ||||||
|             "gain_flatmax": 25, |             "gain_flatmax": 25, | ||||||
|             "gain_min": 15, |             "gain_min": 15, | ||||||
|             "p_max": 21, |             "p_max": 21, | ||||||
|             "advanced_config_from_json": "std_medium_gain_advanced_config.json", |             "advanced_config_from_json": "std_medium_gain_advanced_config.json", | ||||||
|  |             "out_voa_auto": false, | ||||||
|             "allowed_for_design": true |             "allowed_for_design": true | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
| @@ -14,6 +16,7 @@ | |||||||
|             "p_max": 21, |             "p_max": 21, | ||||||
|             "nf_min": 6, |             "nf_min": 6, | ||||||
|             "nf_max": 10, |             "nf_max": 10, | ||||||
|  |             "out_voa_auto": false, | ||||||
|             "allowed_for_design": true |             "allowed_for_design": true | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
| @@ -24,6 +27,7 @@ | |||||||
|             "p_max": 21, |             "p_max": 21, | ||||||
|             "nf_min": 7, |             "nf_min": 7, | ||||||
|             "nf_max": 11, |             "nf_max": 11, | ||||||
|  |             "out_voa_auto": false, | ||||||
|             "allowed_for_design": true |             "allowed_for_design": true | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
| @@ -34,6 +38,7 @@ | |||||||
|             "p_max": 21, |             "p_max": 21, | ||||||
|             "nf_min": 5.8, |             "nf_min": 5.8, | ||||||
|             "nf_max": 10, |             "nf_max": 10, | ||||||
|  |             "out_voa_auto": false, | ||||||
|             "allowed_for_design": true |             "allowed_for_design": true | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
| @@ -44,7 +49,16 @@ | |||||||
|             "p_max": 21, |             "p_max": 21, | ||||||
|             "nf0": 5, |             "nf0": 5, | ||||||
|             "allowed_for_design": true |             "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":[{ |       "Fiber":[{ | ||||||
|             "type_variety": "SSMF", |             "type_variety": "SSMF", | ||||||
| @@ -52,9 +66,11 @@ | |||||||
|             "gamma": 0.00127 |             "gamma": 0.00127 | ||||||
|             } |             } | ||||||
|       ], |       ], | ||||||
|       "Spans":[{ |       "Span":[{ | ||||||
|             "power_mode": true, |             "power_mode":true, | ||||||
|             "delta_power_range_db": [0,0,1], |             "delta_power_range_db": [0,0,0.5], | ||||||
|  |             "max_fiber_lineic_loss_for_raman": 0.25, | ||||||
|  |             "target_extended_gain": 2.5, | ||||||
|             "max_length": 150, |             "max_length": 150, | ||||||
|             "length_units": "km", |             "length_units": "km", | ||||||
|             "max_loss": 28, |             "max_loss": 28, | ||||||
| @@ -64,10 +80,13 @@ | |||||||
|             "con_out": 0 |             "con_out": 0 | ||||||
|             } |             } | ||||||
|       ], |       ], | ||||||
|       "Roadms":[{ |       "Roadm":[{ | ||||||
|             "gain_mode_default_loss": 20, |             "target_pch_out_db": -20, | ||||||
|             "power_mode_pout_target": -20, |             "add_drop_osnr": 38, | ||||||
|             "add_drop_osnr": 100 |             "restrictions": { | ||||||
|  |                             "preamp_variety_list":[], | ||||||
|  |                             "booster_variety_list":[] | ||||||
|  |                             }     | ||||||
|             }], |             }], | ||||||
|       "SI":[{ |       "SI":[{ | ||||||
|             "f_min": 191.3e12, |             "f_min": 191.3e12, | ||||||
| @@ -75,7 +94,7 @@ | |||||||
|             "baud_rate": 32e9, |             "baud_rate": 32e9, | ||||||
|             "spacing": 50e9, |             "spacing": 50e9, | ||||||
|             "power_dbm": 0, |             "power_dbm": 0, | ||||||
|             "power_range_db": [0,0.5,0.5], |             "power_range_db": [0,0,0.5], | ||||||
|             "roll_off": 0.15, |             "roll_off": 0.15, | ||||||
|             "tx_osnr": 100, |             "tx_osnr": 100, | ||||||
|             "sys_margins": 0             |             "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": [ |   "path-request": [ | ||||||
|     { |     { | ||||||
|       "request-id": "1", |       "request-id": "1", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "g", |       "destination": "trx g", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx g", |       "dst-tp-id": "trx g", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -22,17 +23,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "2a", |       "request-id": "2a", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "h", |       "destination": "trx h", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx h", |       "dst-tp-id": "trx h", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -49,17 +48,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "3", |       "request-id": "3", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -76,17 +73,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "ee", |       "request-id": "ee", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -104,36 +99,23 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm e", |               "node-id": "roadm e", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "hop-type": "LOOSE" | ||||||
|               "hop-type": "loose", |  | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 1, |             "index": 1, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm g", |               "node-id": "roadm g", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -141,10 +123,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "ff", |       "request-id": "ff", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -161,17 +144,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "10", |       "request-id": "10", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "g", |       "destination": "trx g", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx g", |       "dst-tp-id": "trx g", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -188,17 +169,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "11", |       "request-id": "11", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "h", |       "destination": "trx h", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx h", |       "dst-tp-id": "trx h", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -216,21 +195,15 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "bb", |               "node-id": "bb", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -238,10 +211,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "12", |       "request-id": "12", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -259,21 +233,15 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "trx b", |               "node-id": "trx b", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -281,10 +249,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "13", |       "request-id": "13", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -301,17 +270,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "14", |       "request-id": "14", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -329,36 +296,23 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm e", |               "node-id": "roadm e", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "hop-type": "LOOSE" | ||||||
|               "hop-type": "loose", |  | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 1, |             "index": 1, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm g", |               "node-id": "roadm g", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -366,10 +320,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "e:1# /", |       "request-id": "e:1# /", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "g", |       "destination": "trx g", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx g", |       "dst-tp-id": "trx g", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -386,17 +341,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "b-2a", |       "request-id": "b-2a", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "h", |       "destination": "trx h", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx h", |       "dst-tp-id": "trx h", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -413,17 +366,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "3a;?", |       "request-id": "3a;?", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -440,17 +391,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "ee-s", |       "request-id": "ee-s", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -468,36 +417,23 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm e", |               "node-id": "roadm e", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "hop-type": "LOOSE" | ||||||
|               "hop-type": "loose", |  | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 1, |             "index": 1, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm g", |               "node-id": "roadm g", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -505,10 +441,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "ff-b", |       "request-id": "ff-b", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -525,17 +462,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "10-z", |       "request-id": "10-z", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "g", |       "destination": "trx g", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx g", |       "dst-tp-id": "trx g", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -552,17 +487,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "11 g", |       "request-id": "11 g", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "h", |       "destination": "trx h", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx h", |       "dst-tp-id": "trx h", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -579,17 +512,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "12<", |       "request-id": "12<", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -606,17 +537,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": null |           "path_bandwidth": null | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "12>", |       "request-id": "12>", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -633,9 +562,6 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": null |           "path_bandwidth": null | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
| @@ -644,8 +570,7 @@ | |||||||
|       "synchronization-id": "1", |       "synchronization-id": "1", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "1", |           "1", | ||||||
|           "2a" |           "2a" | ||||||
| @@ -656,8 +581,7 @@ | |||||||
|       "synchronization-id": "3", |       "synchronization-id": "3", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "3", |           "3", | ||||||
|           "1" |           "1" | ||||||
| @@ -668,8 +592,7 @@ | |||||||
|       "synchronization-id": "ff", |       "synchronization-id": "ff", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "ff", |           "ff", | ||||||
|           "13" |           "13" | ||||||
| @@ -680,8 +603,7 @@ | |||||||
|       "synchronization-id": "13", |       "synchronization-id": "13", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "13", |           "13", | ||||||
|           "14" |           "14" | ||||||
| @@ -77,7 +77,22 @@ | |||||||
|                     "longitude": 0 |                     "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", |             "uid": "Site_B", | ||||||
|             "type": "Transceiver", |             "type": "Transceiver", | ||||||
| @@ -110,6 +125,10 @@ | |||||||
|         },         |         },         | ||||||
|         { |         { | ||||||
|             "from_node": "Edfa2", |             "from_node": "Edfa2", | ||||||
|  |             "to_node": "Att_B" | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             "from_node": "Att_B", | ||||||
|             "to_node": "Site_B" |             "to_node": "Site_B" | ||||||
|         } |         } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,8 +9,8 @@ from json import load | |||||||
| from gnpy.core.elements import Transceiver, Fiber, Edfa | from gnpy.core.elements import Transceiver, Fiber, Edfa | ||||||
| from gnpy.core.utils import lin2db, db2lin | from gnpy.core.utils import lin2db, db2lin | ||||||
| from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref | 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.equipment import load_equipment, automatic_fmax, automatic_nch | ||||||
| from gnpy.core.network import build_network, load_network, set_roadm_loss | from gnpy.core.network import build_network, load_network | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
| @@ -79,7 +79,7 @@ def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si): | |||||||
|     pin = pin/db2lin(gain) |     pin = pin/db2lin(gain) | ||||||
|     baud_rates = array([c.baud_rate for c in si.carriers]) |     baud_rates = array([c.baud_rate for c in si.carriers]) | ||||||
|     edfa.operational.gain_target = gain |     edfa.operational.gain_target = gain | ||||||
|     pref = Pref(0, -gain) |     pref = Pref(0, -gain, lin2db(len(frequencies))) | ||||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) |     edfa.interpol_params(frequencies, pin, baud_rates, pref) | ||||||
|     result = edfa.nf |     result = edfa.nf | ||||||
|     assert pytest.approx(nf_expected, abs=0.01) == result[0] |     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) |     pin = pin/db2lin(gain) | ||||||
|     baud_rates = array([c.baud_rate for c in si.carriers]) |     baud_rates = array([c.baud_rate for c in si.carriers]) | ||||||
|     edfa.operational.gain_target = gain |     edfa.operational.gain_target = gain | ||||||
|     pref = Pref(0, -gain) |     pref = Pref(0, -gain, lin2db(len(frequencies))) | ||||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) |     edfa.interpol_params(frequencies, pin, baud_rates, pref) | ||||||
|  |  | ||||||
|     assert pytest.approx(nf_expected, abs=0.01) == edfa.nf[0] |     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) |     pin = pin/db2lin(gain) | ||||||
|     baud_rates = array([c.baud_rate for c in si.carriers]) |     baud_rates = array([c.baud_rate for c in si.carriers]) | ||||||
|     edfa.operational.gain_target = gain |     edfa.operational.gain_target = gain | ||||||
|     pref = Pref(0, -gain) |     pref = Pref(0, -gain, lin2db(len(frequencies))) | ||||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) |     edfa.interpol_params(frequencies, pin, baud_rates, pref) | ||||||
|     nf_model = edfa.nf[0] |     nf_model = edfa.nf[0] | ||||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) |     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]) |     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]) |     baud_rates = array([c.baud_rate for c in si.carriers]) | ||||||
|     edfa.operational.gain_target = gain |     edfa.operational.gain_target = gain | ||||||
|     pref = Pref(0, 0) |     pref = Pref(0, 0, lin2db(len(frequencies))) | ||||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) |     edfa.interpol_params(frequencies, pin, baud_rates, pref) | ||||||
|     nf = edfa.nf |     nf = edfa.nf | ||||||
|     pin = lin2db(pin[0]*1e3) |     pin = lin2db(pin[0]*1e3) | ||||||
|   | |||||||
| @@ -18,16 +18,14 @@ from pathlib import Path | |||||||
| import pytest | import pytest | ||||||
| from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | ||||||
| from gnpy.core.network import load_network, build_network | from gnpy.core.network import load_network, build_network | ||||||
| from examples.path_requests_run import (requests_from_json , correct_route_list , | from examples.path_requests_run import requests_from_json, correct_route_list, load_requests | ||||||
|                                         load_requests , disjunctions_from_json) | from gnpy.core.request import compute_path_dsjctn, propagate, propagate_and_optimize_mode | ||||||
| from gnpy.core.request import (compute_path_dsjctn, isdisjoint , find_reversed_path, |  | ||||||
|                                propagate,propagate_and_optimize_mode)  |  | ||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
| from gnpy.core.elements import Roadm | from gnpy.core.elements import Roadm | ||||||
|  |  | ||||||
| network_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy.json' | network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json' | ||||||
| service_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_services.json' | service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json' | ||||||
| result_file_name  = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_results.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' | eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' | ||||||
|  |  | ||||||
| @pytest.mark.parametrize("net",[network_file_name]) | @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("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']]) | @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): | def test_automaticmodefeature(net,eqpt,serv,expected_mode): | ||||||
|     data = load_requests(serv,eqpt) |     data = load_requests(serv, eqpt, bidir=False) | ||||||
|     equipment = load_equipment(eqpt) |     equipment = load_equipment(eqpt) | ||||||
|     network = load_network(net,equipment) |     network = load_network(net,equipment) | ||||||
|  |  | ||||||
| @@ -72,7 +70,7 @@ def test_automaticmodefeature(net,eqpt,serv,expected_mode): | |||||||
|         if pathreq.baud_rate is not None: |         if pathreq.baud_rate is not None: | ||||||
|             print(pathreq.format) |             print(pathreq.format) | ||||||
|             path_res_list.append(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: |         else: | ||||||
|             total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment) |             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 |             # 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.request import compute_path_dsjctn, isdisjoint , find_reversed_path | ||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
| from gnpy.core.elements import Roadm | 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' | network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json' | ||||||
| service_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_services.json' | service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json' | ||||||
| result_file_name  = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_results.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' | eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' | ||||||
|  |  | ||||||
| @pytest.mark.parametrize("net",[network_file_name]) | @pytest.mark.parametrize("net",[network_file_name]) | ||||||
| @pytest.mark.parametrize("eqpt", [eqpt_library_name]) | @pytest.mark.parametrize("eqpt", [eqpt_library_name]) | ||||||
| @pytest.mark.parametrize("serv",[service_file_name]) | @pytest.mark.parametrize("serv",[service_file_name]) | ||||||
| def test_disjunction(net,eqpt,serv): | def test_disjunction(net,eqpt,serv): | ||||||
|     data = load_requests(serv,eqpt) |     data = load_requests(serv, eqpt, bidir=False) | ||||||
|     equipment = load_equipment(eqpt) |     equipment = load_equipment(eqpt) | ||||||
|     network = load_network(net,equipment) |     network = load_network(net,equipment) | ||||||
|  |  | ||||||
|     # Build the network once using the default power defined in SI in eqpt config |     # 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 |     # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||||
|     # spacing, f_min and f_max  |     # 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,\ |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|     build_network(network, equipment, p_db, p_total_db) |     build_network(network, equipment, p_db, p_total_db) | ||||||
|  |     build_oms_list(network, equipment) | ||||||
|  |  | ||||||
|     rqs = requests_from_json(data, equipment) |     rqs = requests_from_json(data, equipment) | ||||||
|     rqs = correct_route_list(network, rqs) |     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] |         rqs_id_list = [r.request_id for r in rqs] | ||||||
|         p1 = pths[rqs_id_list.index(e[0])][1:-1] |         p1 = pths[rqs_id_list.index(e[0])][1:-1] | ||||||
|         p2 = pths[rqs_id_list.index(e[1])][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 |             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 p1  if isinstance(e, Roadm)]}\n') | ||||||
|             print(f'Computed path (roadms):{[e.uid for e in p2  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("eqpt", [eqpt_library_name]) | ||||||
| @pytest.mark.parametrize("serv",[service_file_name]) | @pytest.mark.parametrize("serv",[service_file_name]) | ||||||
| def test_does_not_loop_back(net,eqpt,serv): | 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) |     equipment = load_equipment(eqpt) | ||||||
|     network = load_network(net,equipment) |     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,\ |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|     build_network(network, equipment, p_db, p_total_db) |     build_network(network, equipment, p_db, p_total_db) | ||||||
|  |     build_oms_list(network, equipment) | ||||||
|  |  | ||||||
|     rqs = requests_from_json(data, equipment) |     rqs = requests_from_json(data, equipment) | ||||||
|     rqs = correct_route_list(network, rqs) |     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 |     # check that the total agregated bandwidth is the same after aggregation | ||||||
|  |  | ||||||
|     #  |     # | ||||||
|   | |||||||
| @@ -3,43 +3,57 @@ | |||||||
| # @Author: Esther Le Rouzic | # @Author: Esther Le Rouzic | ||||||
| # @Date:   2018-06-15 | # @Date:   2018-06-15 | ||||||
|  |  | ||||||
| from gnpy.core.elements import Edfa | """ Adding tests to check the parser non regression | ||||||
| import numpy as np |     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 json import load | ||||||
|  | from pathlib import Path | ||||||
|  | from os import unlink | ||||||
|  | from pandas import read_csv | ||||||
| import pytest | 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 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.convert import convert_file | ||||||
| from gnpy.core.service_sheet import convert_service_sheet | from gnpy.core.service_sheet import convert_service_sheet | ||||||
| from pathlib import Path | from gnpy.core.equipment import load_equipment, automatic_nch | ||||||
| import filecmp | from gnpy.core.network import load_network | ||||||
| from os import unlink | 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 | TEST_DIR = Path(__file__).parent | ||||||
| DATA_DIR = TEST_DIR / 'data' | DATA_DIR = TEST_DIR / 'data' | ||||||
| eqpt_filename = DATA_DIR / 'eqpt_config.json' | 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', { | @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 / 'CORONET_Global_Topology.xls':   DATA_DIR / 'CORONET_Global_Topology_expected.json', | ||||||
|     DATA_DIR / 'meshTopologyExampleV2.xls':     DATA_DIR / 'meshTopologyExampleV2_expected.json', |     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_expected.json', | ||||||
|     DATA_DIR / 'meshTopologyExampleV2Eqpt.xls': DATA_DIR / 'meshTopologyExampleV2Eqpt_expected.json', |     }.items()) | ||||||
|  }.items()) |  | ||||||
| def test_excel_json_generation(xls_input, expected_json_output): | def test_excel_json_generation(xls_input, expected_json_output): | ||||||
|  |     """ tests generation of topology json | ||||||
|  |     """ | ||||||
|     convert_file(xls_input) |     convert_file(xls_input) | ||||||
|  |  | ||||||
|     actual_json_output = xls_input.with_suffix('.json') |     actual_json_output = xls_input.with_suffix('.json') | ||||||
|     with open(actual_json_output, encoding='utf-8') as f: |     with open(actual_json_output, encoding='utf-8') as f: | ||||||
|         actual = load(f) |         actual = load(f) | ||||||
|     #unlink(actual_json_output) |     unlink(actual_json_output) | ||||||
|  |  | ||||||
|     with open(expected_json_output, encoding='utf-8') as f: |     with open(expected_json_output, encoding='utf-8') as f: | ||||||
|         expected = load(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.extra | ||||||
|     assert not results.connections.different |     assert not results.connections.different | ||||||
|  |  | ||||||
| # assume json entries | # assume xls entries | ||||||
| # test that the build network gives correct results | # test that the build network gives correct results in gain mode | ||||||
| # TODO !! |  | ||||||
|  |  | ||||||
|  | @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', { | @pytest.mark.parametrize('xls_input,expected_json_output', { | ||||||
|     DATA_DIR / 'excelTestFile.xls':             DATA_DIR / 'excelTestFile_services_expected.json', |     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_services_expected.json', | ||||||
|     DATA_DIR / 'meshTopologyExampleV2.xls':     DATA_DIR / 'meshTopologyExampleV2_services_expected.json', |     DATA_DIR / 'testService.xls':     DATA_DIR / 'testService_services_expected.json' | ||||||
|     DATA_DIR / 'meshTopologyExampleV2Eqpt.xls': DATA_DIR / 'meshTopologyExampleV2Eqpt_services_expected.json', |     }.items()) | ||||||
| }.items()) |  | ||||||
| def test_excel_service_json_generation(xls_input, expected_json_output): | def test_excel_service_json_generation(xls_input, expected_json_output): | ||||||
|  |     """ test services creation | ||||||
|  |     """ | ||||||
|     convert_service_sheet(xls_input, eqpt_filename) |     convert_service_sheet(xls_input, eqpt_filename) | ||||||
|  |  | ||||||
|     actual_json_output = f'{str(xls_input)[:-4]}_services.json' |     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.missing | ||||||
|     assert not results.synchronizations.extra |     assert not results.synchronizations.extra | ||||||
|     assert not results.synchronizations.different |     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 = 'tests/test_network.json' | ||||||
| network_file_name = Path(__file__).parent.parent / 'tests/LinkforTest.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' | #network_file_name = Path(__file__).parent.parent / 'examples/edfa_example_network.json' | ||||||
| eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.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 = input_power | ||||||
|     p = db2lin(p) * 1e-3 |     p = db2lin(p) * 1e-3 | ||||||
|     spacing = 0.05 # THz |     spacing = 50e9 # THz | ||||||
|     si = SpectralInformation() # SI units: W, Hz |     si = create_input_spectral_information(191.3e12, 191.3e12+79*spacing, 0.15, 32e9, p, spacing) | ||||||
|     si = si.update(carriers=[ |  | ||||||
|         Channel(f, (191.3 + spacing * f) * 1e12, 32e9, 0.15, Power(p, 0, 0)) |  | ||||||
|         for f in range(1,80) |  | ||||||
|     ]) |  | ||||||
|     source = next(transceivers[uid] for uid in transceivers if uid == 'trx A') |     source = next(transceivers[uid] for uid in transceivers if uid == 'trx A') | ||||||
|     sink = next(transceivers[uid] for uid in transceivers if uid == dest) |     sink = next(transceivers[uid] for uid in transceivers if uid == dest) | ||||||
|     path = dijkstra_path(network, source, sink) |     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