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 | ||||
| services: docker | ||||
| python: | ||||
|   - "3.6" | ||||
| # command to install dependencies | ||||
| install: | ||||
|   - python setup.py install | ||||
| # command to run tests | ||||
| before_script: | ||||
|   - "3.7" | ||||
| install: skip | ||||
| script: | ||||
|   - pytest | ||||
|   - python setup.py install | ||||
|   - pip install pytest-cov rstcheck | ||||
|   - pytest --cov-report=xml --cov=gnpy | ||||
|   - rstcheck --ignore-roles cite --ignore-directives automodule --recursive --ignore-messages '(Duplicate explicit target name.*)' . | ||||
|   - ./examples/transmission_main_example.py | ||||
|   - ./examples/path_requests_run.py | ||||
|   - ./examples/transmission_main_example.py examples/raman_edfa_example_network.json --sim examples/sim_params.json --show-channels | ||||
|   - sphinx-build docs/ x-throwaway-location | ||||
| after_success: | ||||
|   - bash <(curl -s https://codecov.io/bash) | ||||
| jobs: | ||||
|   include: | ||||
|     - stage: test | ||||
|       name: Docker image | ||||
|       script: | ||||
|         - git fetch --unshallow | ||||
|         - ./.docker-travis.sh | ||||
|         - docker images | ||||
|   | ||||
							
								
								
									
										8
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| --- | ||||
| - project: | ||||
|     check: | ||||
|       jobs: | ||||
|       - noop | ||||
|     gate: | ||||
|       jobs: | ||||
|       - noop | ||||
| @@ -6,16 +6,24 @@ To learn how to contribute, please see CONTRIBUTING.md | ||||
| (*in alphabetical order*) | ||||
|  | ||||
| - Alessio Ferrari (Politecnico di Torino) <alessio.ferrari@polito.it> | ||||
| - Anders Lindgren (Telia Company) <Anders.X.Lindgren@teliacompany.com> | ||||
| - Andrea d'Amico (Politecnico di Torino) <andrea.damico@polito.it> | ||||
| - Brian Taylor (Facebook) <briantaylor@fb.com> | ||||
| - David Boertjes (Ciena) <dboertje@ciena.com> | ||||
| - Diego Landa (Facebook) <dlanda@fb.com> | ||||
| - Esther Le Rouzic (Orange) <esther.lerouzic@orange.com> | ||||
| - Gabriele Galimberti (Cisco) <ggalimbe@cisco.com> | ||||
| - Gert Grammel (Juniper Networks) <ggrammel@juniper.net> | ||||
| - Gilad Goldfarb (Facebook) <giladg@fb.com> | ||||
| - James Powell (Telecom Infra Project) <james.powell@telecominfraproject.com> | ||||
| - Jan Kundrát (Telecom Infra Project) <jan.kundrat@telecominfraproject.com> | ||||
| - Jeanluc Augé (Orange) <jeanluc.auge@orange.com> | ||||
| - Jonas Mårtensson (RISE) <jonas.martensson@ri.se> | ||||
| - Mattia Cantono (Politecnico di Torino) <mattia.cantono@polito.it> | ||||
| - Miguel Garrich (University Catalunya) <miquel.garrich@upct.es> | ||||
| - Raj Nagarajan (Lumentum) <raj.nagarajan@lumentum.com> | ||||
| - Roberts Miculens (Lattelecom) <roberts.miculens@lattelecom.lv> | ||||
| - Shengxiang Zhu (University of Arizona) <szhu@email.arizona.edu> | ||||
| - Stefan Melin (Telia Company) <Stefan.Melin@teliacompany.com> | ||||
| - Vittorio Curri (Politecnico di Torino) <vittorio.curri@polito.it> | ||||
| - Xufeng Liu (Jabil) <xufeng_liu@jabil.com> | ||||
|   | ||||
							
								
								
									
										8
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| FROM python:3.7-slim | ||||
| COPY . /oopt-gnpy | ||||
| WORKDIR /oopt-gnpy | ||||
| RUN python setup.py install | ||||
| WORKDIR /shared | ||||
| ENTRYPOINT ["/oopt-gnpy/.docker-entry.sh"] | ||||
| CMD ["python", "examples/path_requests_run.py", "examples/2019-demo-topology.json", "examples/2019-demo-services.json", "examples/2019-demo-equipment.json", "--rest"] | ||||
| EXPOSE 5000 | ||||
| @@ -19,8 +19,8 @@ In order to work the excel file MUST contain at least 2 sheets: | ||||
| Nodes sheet | ||||
| ----------- | ||||
|  | ||||
| Nodes sheet contains seven columns. | ||||
| Each line represents a 'node' (ROADM site or an in line amplifier site ILA):: | ||||
| Nodes sheet contains nine columns. | ||||
| Each line represents a 'node' (ROADM site or an in line amplifier site ILA or a Fused):: | ||||
|  | ||||
|   City (Mandatory) ; State ; Country ; Region ; Latitude ; Longitude ; Type | ||||
|  | ||||
| @@ -38,6 +38,9 @@ Each line represents a 'node' (ROADM site or an in line amplifier site ILA):: | ||||
|  | ||||
| - *Longitude*, *Latitude* are not mandatory. If filled they should contain numbers. | ||||
|  | ||||
| - **Booster_restriction** and **Preamp_restriction** are not mandatory. | ||||
|   If used, they must contain one or several amplifier type_variety names separated by ' | '. This information is used to restrict types of amplifiers used in a ROADM node during autodesign. If a ROADM booster or preamp is already specified in the Eqpt sheet , the field is ignored. The field is also ignored if the node is not a ROADM node. | ||||
|  | ||||
| **There MUST NOT be empty line(s) between two nodes lines** | ||||
|  | ||||
|  | ||||
| @@ -166,6 +169,7 @@ This generates a text file meshTopologyExampleV2_eqt_sheet.txt  whose content ca | ||||
| - **amp type** is not mandatory.  | ||||
|   If filled it must contain types listed in `eqpt_config.json <examples/eqpt_config.json>`_ in "Edfa" list "type_variety". | ||||
|   If not filled it takes "std_medium_gain" as default value. | ||||
|   If filled with fused, a fused element with 0.0 dB loss will be placed instead of an amplifier. This might be used to avoid booster amplifier on a ROADM direction. | ||||
|  | ||||
| - **amp_gain** is not mandatory. It is the value to be set on the amplifier (in dB). | ||||
|   If not filled, it will be determined with design rules in the convert.py file. | ||||
|   | ||||
							
								
								
									
										330
									
								
								README.rst
									
									
									
									
									
								
							
							
						
						
									
										330
									
								
								README.rst
									
									
									
									
									
								
							| @@ -1,13 +1,19 @@ | ||||
| .. image:: docs/images/GNPy-banner.png | ||||
|    :width: 100% | ||||
|    :align: left | ||||
|    :alt: GNPy with an OLS system | ||||
|  | ||||
| ==================================================================== | ||||
| `gnpy`: mesh optical network route planning and optimization library | ||||
| ==================================================================== | ||||
|  | ||||
| |docs| |build| | ||||
| |docs| |build| |doi| | ||||
|  | ||||
| **`gnpy` is an open-source, community-developed library for building route | ||||
| planning and optimization tools in real-world mesh optical networks.** | ||||
|  | ||||
| `gnpy <http://github.com/telecominfraproject/oopt-gnpy>`__ is: | ||||
| -------------------------------------------------------------- | ||||
|  | ||||
| - a sponsored project of the `OOPT/PSE <https://telecominfraproject.com/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_ | ||||
| - fully community-driven, fully open source library | ||||
| @@ -18,41 +24,50 @@ planning and optimization tools in real-world mesh optical networks.** | ||||
|  | ||||
| Documentation: https://gnpy.readthedocs.io | ||||
|  | ||||
| Get In Touch | ||||
| ~~~~~~~~~~~~ | ||||
|  | ||||
| There are `weekly calls <https://telecominfraproject.workplace.com/events/702894886867547/>`__ about our progress. | ||||
| Newcomers, users and telecom operators are especially welcome there. | ||||
| We encourage all interested people outside the TIP to `join the project <https://telecominfraproject.com/apply-for-membership/>`__. | ||||
|  | ||||
| Branches and Tagged Releases | ||||
| ---------------------------- | ||||
|  | ||||
| - the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, validated code. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group. For more information about the validation process, see: https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality | ||||
| - all releases are `available via GitHub <https://github.com/Telecominfraproject/oopt-gnpy/releases>`_ | ||||
| - the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, `validated code <https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality>`_. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group. | ||||
| - the `develop <https://github.com/Telecominfraproject/oopt-gnpy/tree/develop>`_ branch contains the latest code under active development, which may not be fully validated and tested. | ||||
| - the `phase-1 <https://github.com/Telecominfraproject/oopt-gnpy/tree/phase-1>`_ branch contains code for Phase I of the OOPT-PSE efforts and is kept only for reference. This branch is unmaintained. | ||||
|  | ||||
| A brief outline of major (tagged) `gnpy` releases: | ||||
|  | ||||
| +---------------+-------------+-----------------------------------------------+ | ||||
| | release date  | version tag | notes                                         | | ||||
| +===============+=============+===============================================+ | ||||
| | Mar 5, 2019   | v1.2        | - plotting fixes                              | | ||||
| |               |             | - documentation clean-up                      | | ||||
| |               |             | - bug fixes                                   | | ||||
| +---------------+-------------+-----------------------------------------------+ | ||||
| | Jan 30, 2019  | v1.1        | - XLS parser enhancements                     | | ||||
| |               |             | - Transponder and Roadm add-drop noise        | | ||||
| |               |             |   contribution and system margin included     | | ||||
| |               |             | - Automatic transponders’ mode selection      | | ||||
| |               |             | - Route selection with disjunction constraints| | ||||
| |               |             | - Detailed carrier information inspection on  | | ||||
| |               |             |   each element  along propagation             | | ||||
| |               |             | - OpenRoadm noise models                      |  | ||||
| |               |             | - bug fixes                                   | | ||||
| +---------------+-------------+-----------------------------------------------+ | ||||
| | Oct 16, 2018  | v1.0        | - first "production"-ready release            | | ||||
| |               |             | - open network element model (EDFA, GN-model) | | ||||
| |               |             | - auto-design functionality                   | | ||||
| |               |             | - path request functionality                  | | ||||
| +---------------+-------------+-----------------------------------------------+ | ||||
|  | ||||
| How to Install | ||||
| -------------- | ||||
|  | ||||
| Using prebuilt Docker images | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| Our `Docker images <https://hub.docker.com/r/telecominfraproject/oopt-gnpy>`_ contain everything needed to run all examples from this guide. | ||||
| Docker transparently fetches the image over the network upon first use. | ||||
| On Linux and Mac, run: | ||||
|  | ||||
|  | ||||
| .. code-block:: shell-session | ||||
|  | ||||
|     $ docker run -it --rm --volume $(pwd):/shared telecominfraproject/oopt-gnpy | ||||
|     root@bea050f186f7:/shared/examples# | ||||
|  | ||||
| On Windows, launch from Powershell as: | ||||
|  | ||||
| .. code-block:: powershell | ||||
|  | ||||
|     PS C:\> docker run -it --rm --volume ${PWD}:/shared telecominfraproject/oopt-gnpy | ||||
|     root@89784e577d44:/shared/examples# | ||||
|  | ||||
| In both cases, a directory named ``examples/`` will appear in your current working directory. | ||||
| GNPy automaticallly populates it with example files from the current release. | ||||
| Remove that directory if you want to start from scratch. | ||||
|  | ||||
| Using Python on your computer | ||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
|    **Note**: `gnpy` supports Python 3 only. Python 2 is not supported. | ||||
|    `gnpy` requires Python ≥3.6 | ||||
|  | ||||
| @@ -62,10 +77,9 @@ How to Install | ||||
| It is recommended that you use a "virtual environment" when installing `gnpy`. | ||||
| Do not install `gnpy` on your system Python. | ||||
|  | ||||
| We recommend the use of the Anaconda Python distribution | ||||
| (https://www.anaconda.com/download) which comes with many scientific computing | ||||
| We recommend the use of the `Anaconda Python distribution <https://www.anaconda.com/download>`_ which comes with many scientific computing | ||||
| dependencies pre-installed. Anaconda creates a base "virtual environment" for | ||||
| you automatically. You can also create and manage your conda "virtual | ||||
| you automatically. You can also create and manage your ``conda`` "virtual | ||||
| environments" yourself (see: | ||||
| https://conda.io/docs/user-guide/tasks/manage-environments.html) | ||||
|  | ||||
| @@ -111,14 +125,13 @@ of the `gnpy` repo and install it with: | ||||
|     $ python setup.py install                                    # install | ||||
|  | ||||
| To test that `gnpy` was successfully installed, you can run this command. If it | ||||
| executes without a `ModuleNotFoundError`, you have successfully installed | ||||
| executes without a ``ModuleNotFoundError``, you have successfully installed | ||||
| `gnpy`. | ||||
|  | ||||
| .. code-block:: shell | ||||
|  | ||||
|     $ python -c 'import gnpy' # attempt to import gnpy | ||||
|  | ||||
|     $ cd oopt-gnpy | ||||
|     $ pytest                  # run tests | ||||
|  | ||||
| Instructions for First Use | ||||
| @@ -130,20 +143,18 @@ It ships with a number of example programs. Release versions will ship with | ||||
| fully-functional programs. | ||||
|  | ||||
|     **Note**: *If you are a network operator or involved in route planning and | ||||
|     optimization for your organization, please contact project maintainer James | ||||
|     Powell <james.powell@telecominfraproject>. gnpy is looking for users with | ||||
|     optimization for your organization, please contact project maintainer Jan | ||||
|     Kundrát <jan.kundrat@telecominfraproject.com>. gnpy is looking for users with | ||||
|     specific, delineated use cases to drive requirements for future | ||||
|     development.* | ||||
|  | ||||
| **To get started, run the main transmission example:** | ||||
| This example demonstrates how GNPy can be used to check the expected SNR at the end of the line by varying the channel input power: | ||||
|  | ||||
|     **Note**: *Examples should be run from the examples/ folder.* | ||||
|  | ||||
| .. code-block:: shell | ||||
|     $ pwd | ||||
|     /path/to/oopt-gnpy | ||||
|     $ cd examples | ||||
|     $ python transmission_main_example.py | ||||
| .. image:: https://telecominfraproject.github.io/oopt-gnpy/docs/images/transmission_main_example.svg | ||||
|    :width: 100% | ||||
|    :align: left | ||||
|    :alt: Running a simple simulation example | ||||
|    :target: https://asciinema.org/a/252295 | ||||
|  | ||||
| By default, this script operates on a single span network defined in | ||||
| `examples/edfa_example_network.json <examples/edfa_example_network.json>`_ | ||||
| @@ -152,10 +163,9 @@ You can specify a different network at the command line as follows. For | ||||
| example, to use the CORONET Global network defined in | ||||
| `examples/CORONET_Global_Topology.json <examples/CORONET_Global_Topology.json>`_: | ||||
|  | ||||
| .. code-block:: shell | ||||
| .. code-block:: shell-session | ||||
|  | ||||
|     $ cd examples | ||||
|     $ python transmission_main_example.py CORONET_Global_Topology.json | ||||
|     $ ./examples/transmission_main_example.py examples/CORONET_Global_Topology.json | ||||
|  | ||||
| It is also possible to use an Excel file input (for example | ||||
| `examples/CORONET_Global_Topology.xls <examples/CORONET_Global_Topology.xls>`_). | ||||
| @@ -193,59 +203,59 @@ information to transmit.) | ||||
| The EDFA equipment library is a list of supported amplifiers. New amplifiers | ||||
| can be added and existing ones removed. Three different noise models are available: | ||||
|  | ||||
| 1. `'type_def': 'variable_gain'` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: `nf_min`, `nf_max`, and `gain_flatmax`. It is not a simple interpolation but a 2-stage NF calculation. | ||||
| 2. `'type_def': 'fixed_gain'` is a fixed gain model.  `NF == Cte == nf0` if `gain_min < gain < gain_flatmax` | ||||
| 3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/std_medium_gain_advanced_config.json <examples/std_medium_gain_advanced_config.json>`_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled. | ||||
| 1. ``'type_def': 'variable_gain'`` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: ``nf_min``, ``nf_max``, and ``gain_flatmax``. It is not a simple interpolation but a 2-stage NF calculation. | ||||
| 2. ``'type_def': 'fixed_gain'`` is a fixed gain model.  `NF == Cte == nf0` if `gain_min < gain < gain_flatmax` | ||||
| 3. ``'type_def': None`` is an advanced model. A detailed JSON configuration file is required (by default `examples/std_medium_gain_advanced_config.json <examples/std_medium_gain_advanced_config.json>`_). It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled. | ||||
|  | ||||
| For all amplifier models: | ||||
|  | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| +------------------------+-----------+-----------------------------------------+ | ||||
| | field                  |   type    | description                             | | ||||
| +======================+===========+=========================================+ | ||||
| | `type_variety`       | (string)  | a unique name to ID the amplifier in the| | ||||
| +========================+===========+=========================================+ | ||||
| | ``type_variety``       | (string)  | a unique name to ID the amplifier in the| | ||||
| |                        |           | JSON/Excel template topology input file | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `out_voa_auto`       | (boolean) | auto_design feature to optimize the     | | ||||
| +------------------------+-----------+-----------------------------------------+ | ||||
| | ``out_voa_auto``       | (boolean) | auto_design feature to optimize the     | | ||||
| |                        |           | amplifier output VOA. If true, output   | | ||||
| |                        |           | VOA is present and will be used to push | | ||||
| |                        |           | amplifier gain to its maximum, within   | | ||||
| |                        |           | EOL power margins.                      | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `allowed_for_design` | (boolean) | If false, the amplifier will not be     | | ||||
| +------------------------+-----------+-----------------------------------------+ | ||||
| | ``allowed_for_design`` | (boolean) | If false, the amplifier will not be     | | ||||
| |                        |           | picked by auto-design but it can still  | | ||||
| |                        |           | be used as a manual input (from JSON or | | ||||
| |                        |           | Excel template topology files.)         | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| +------------------------+-----------+-----------------------------------------+ | ||||
|  | ||||
| The fiber library currently describes SSMF and NZDF but additional fiber types can be entered by the user following the same model: | ||||
|  | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | field                | type      | description                             | | ||||
| +======================+===========+=========================================+ | ||||
| | `type_variety`       | (string)  | a unique name to ID the fiber in the    | | ||||
| | ``type_variety``     | (string)  | a unique name to ID the fiber in the    | | ||||
| |                      |           | JSON or Excel template topology input   | | ||||
| |                      |           | file                                    | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `dispersion`         | (number)  | (s.m-1.m-1)                             | | ||||
| | ``dispersion``       | (number)  | (s.m-1.m-1)                             | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `gamma`              | (number)  | 2pi.n2/(lambda*Aeff) (w-2.m-1)          | | ||||
| | ``gamma``            | (number)  | 2pi.n2/(lambda*Aeff) (w-2.m-1)          | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
|  | ||||
| The transceiver equipment library is a list of supported transceivers. New | ||||
| transceivers can be added and existing ones removed at will by the user. It is | ||||
| used to determine the service list path feasibility when running the | ||||
| path_request_run.py routine. | ||||
| `path_request_run.py routine <examples/path_request_run.py>`_. | ||||
|  | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | field                | type      | description                             | | ||||
| +======================+===========+=========================================+ | ||||
| |  `type_variety`      | (string)  | a unique name to ID the transceiver in  | | ||||
| | ``type_variety``     | (string)  | A unique name to ID the transceiver in  | | ||||
| |                      |           | the JSON or Excel template topology     | | ||||
| |                      |           | input file                              | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| |  `frequency`         | (number)  | Min/max as below.                       | | ||||
| | ``frequency``        | (number)  | Min/max as below.                       | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| |  `mode`              | (number)  | a list of modes supported by the        | | ||||
| | ``mode``             | (number)  | A list of modes supported by the        | | ||||
| |                      |           | transponder. New modes can be added at  | | ||||
| |                      |           | will by the user. The modes are specific| | ||||
| |                      |           | to each transponder type_variety.       | | ||||
| @@ -257,61 +267,61 @@ The modes are defined as follows: | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | field                | type      | description                             | | ||||
| +======================+===========+=========================================+ | ||||
| | `format`             | (string)  | a unique name to ID the mode.           | | ||||
| | ``format``           | (string)  | a unique name to ID the mode            | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `baud_rate`          | (number)  | in Hz                                   | | ||||
| | ``baud_rate``        | (number)  | in Hz                                   | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `OSNR`               | (number)  | min required OSNR in 0.1nm (dB)         | | ||||
| | ``OSNR``             | (number)  | min required OSNR in 0.1nm (dB)         | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `bit_rate`           | (number)  | in bit/s                                | | ||||
| | ``bit_rate``         | (number)  | in bit/s                                | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `roll_off`           | (number)  | Not used.                               | | ||||
| | ``roll_off``         | (number)  | Not used.                               | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `tx_osnr`            | (number)  | In dB. OSNR out from transponder.       | | ||||
| | ``tx_osnr``          | (number)  | In dB. OSNR out from transponder.       | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
| | `cost`               | (number)  | Arbitrary unit                          | | ||||
| | ``cost``             | (number)  | Arbitrary unit                          | | ||||
| +----------------------+-----------+-----------------------------------------+ | ||||
|  | ||||
| Simulation parameters are defined as follows. | ||||
|  | ||||
| Auto-design automatically creates EDFA amplifier network elements when they are | ||||
| missing, after a fiber, or between a ROADM and a fiber. This auto-design | ||||
| functionality can be manually and locally deactivated by introducing a `Fused` | ||||
| network element after a `Fiber` or a `Roadm` that doesn't need amplification. | ||||
| functionality can be manually and locally deactivated by introducing a ``Fused`` | ||||
| network element after a ``Fiber`` or a ``Roadm`` that doesn't need amplification. | ||||
| The amplifier is chosen in the EDFA list of the equipment library based on | ||||
| gain, power, and NF criteria. Only the EDFA that are marked | ||||
| `'allowed_for_design': true` are considered. | ||||
| ``'allowed_for_design': true`` are considered. | ||||
|  | ||||
| For amplifiers defined in the topology JSON input but whose gain = 0 | ||||
| (placeholder), auto-design will set its gain automatically: see `power_mode` in | ||||
| the `Spans` library to find out how the gain is calculated. | ||||
| For amplifiers defined in the topology JSON input but whose ``gain = 0`` | ||||
| (placeholder), auto-design will set its gain automatically: see ``power_mode`` in | ||||
| the ``Spans`` library to find out how the gain is calculated. | ||||
|  | ||||
| Span configuration is performed as follows. It is not a list (which may change | ||||
| in later releases) and the user can only modify the value of existing | ||||
| parameters: | ||||
|  | ||||
| +------------------------+-----------+---------------------------------------------+ | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
| | field                               | type      | description                                 | | ||||
| +========================+===========+=============================================+ | ||||
| | `power_mode`           | (boolean) | If false, gain mode. Auto-design sets       | | ||||
| +=====================================+===========+=============================================+ | ||||
| | ``power_mode``                      | (boolean) | If false, gain mode. Auto-design sets       | | ||||
| |                                     |           | amplifier gain = preceding span loss,       | | ||||
| |                                     |           | unless the amplifier exists and its         | | ||||
| |                        |           | gain > 0 in the topology input json.        | | ||||
| |                                     |           | gain > 0 in the topology input JSON.        | | ||||
| |                                     |           | If true, power mode (recommended for        | | ||||
| |                                     |           | auto-design and power sweep.)               | | ||||
| |                                     |           | Auto-design sets amplifier power            | | ||||
| |                                     |           | according to delta_power_range. If the      | | ||||
| |                                     |           | amplifier exists with gain > 0 in the       | | ||||
| |                        |           | topology json input, then its gain is       | | ||||
| |                                     |           | topology JSON input, then its gain is       | | ||||
| |                                     |           | translated into a power target/channel.     | | ||||
| |                                     |           | Moreover, when performing a power sweep     | | ||||
| |                        |           | (see power_range_db in the SI               | | ||||
| |                                     |           | (see ``power_range_db`` in the SI           | | ||||
| |                                     |           | configuration library) the power sweep      | | ||||
| |                                     |           | is performed w/r/t this power target,       | | ||||
| |                                     |           | regardless of preceding amplifiers          | | ||||
| |                                     |           | power saturation/limitations.               | | ||||
| +------------------------+-----------+---------------------------------------------+ | ||||
| | `delta_power_range_db` | (number)  | Auto-design only, power-mode                | | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
| | ``delta_power_range_db``            | (number)  | Auto-design only, power-mode                | | ||||
| |                                     |           | only. Specifies the [min, max, step]        | | ||||
| |                                     |           | power excursion/span. It is a relative      | | ||||
| |                                     |           | power excursion w/r/t the                   | | ||||
| @@ -344,23 +354,25 @@ parameters: | ||||
| |                                     |           | power = power_dbm + power_range_db and      | | ||||
| |                                     |           | a 23 dB span to                             | | ||||
| |                                     |           | power = power_dbm + power_range_db + 1      | | ||||
| +------------------------+-----------+---------------------------------------------+ | ||||
| | `max_length`           | (number)  | Split fiber lengths > max_length.           | | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
| | ``max_fiber_lineic_loss_for_raman`` | (number)  | Maximum linear fiber loss for Raman         | | ||||
| |                                     |           | amplification use.                          | | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
| | ``max_length``                      | (number)  | Split fiber lengths > max_length.           | | ||||
| |                                     |           | Interest to support high level              | | ||||
| |                                     |           | topologies that do not specify in line      | | ||||
| |                                     |           | amplification sites. For example the        | | ||||
| |                                     |           | CORONET_Global_Topology.xls defines         | | ||||
| |                                     |           | links > 1000km between 2 sites: it          | | ||||
| |                                     |           | couldn't be simulated if these links        | | ||||
| |                        |           | were not splitted in shorter span           | | ||||
| |                        |           | lengths.                                    | | ||||
| +------------------------+-----------+---------------------------------------------+ | ||||
| | `length_unit`          | "m"/"km"  | Unit for max_length.                        | | ||||
| +------------------------+-----------+---------------------------------------------+ | ||||
| | `max_loss`             | (number)  | Not used in the current code                | | ||||
| |                                     |           | were not split in shorter span lengths.     | | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
| | ``length_unit``                     | "m"/"km"  | Unit for ``max_length``.                    | | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
| | ``max_loss``                        | (number)  | Not used in the current code                | | ||||
| |                                     |           | implementation.                             | | ||||
| +------------------------+-----------+---------------------------------------------+ | ||||
| | `padding`              | (number)  | In dB. Min span loss before putting an      | | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
| | ``padding``                         | (number)  | In dB. Min span loss before putting an      | | ||||
| |                                     |           | attenuator before fiber. Attenuator         | | ||||
| |                                     |           | value                                       | | ||||
| |                                     |           | Fiber.att_in = max(0, padding - span_loss). | | ||||
| @@ -374,25 +386,23 @@ parameters: | ||||
| |                                     |           | completed to have span_loss = padding.      | | ||||
| |                                     |           | Therefore it is not possible to set         | | ||||
| |                                     |           | span_loss < padding.                        | | ||||
| +------------------------+-----------+---------------------------------------------+ | ||||
| | `EOL`                  | (number)  | All fiber span loss ageing. The value       | | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
| | ``EOL``                             | (number)  | All fiber span loss ageing. The value       | | ||||
| |                                     |           | is added to the con_out (fiber output       | | ||||
| |                                     |           | connector). So the design and the path      | | ||||
| |                                     |           | feasibility are performed with              | | ||||
| |                                     |           | span_loss + EOL. EOL cannot be set          | | ||||
| |                                     |           | manually for a given fiber span             | | ||||
| |                        |           | (workaround is to specify higher con_out    | | ||||
| |                        |           | loss for this fiber).                       | | ||||
| +------------------------+-----------+---------------------------------------------+ | ||||
| | `con_in`, `con_out`    | (number)  | Default values if Fiber/params/con_in/out   | | ||||
| |                        |           | is None in the topology input               | | ||||
| |                                     |           | (workaround is to specify higher            | | ||||
| |                                     |           | ``con_out`` loss for this fiber).           | | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
| | ``con_in``,                         | (number)  | Default values if Fiber/params/con_in/out   | | ||||
| | ``con_out``                         |           | is None in the topology input               | | ||||
| |                                     |           | description. This default value is          | | ||||
| |                                     |           | ignored if a Fiber/params/con_in/out        | | ||||
| |                                     |           | value is input in the topology for a        | | ||||
| |                                     |           | given Fiber.                                | | ||||
| +------------------------+-----------+---------------------------------------------+ | ||||
|  | ||||
| **[1]** | ||||
| +-------------------------------------+-----------+---------------------------------------------+ | ||||
|  | ||||
| .. code-block:: json | ||||
|  | ||||
| @@ -415,33 +425,36 @@ parameters: | ||||
| ROADMs can be configured as follows. The user can only modify the value of | ||||
| existing parameters: | ||||
|  | ||||
| +-------------------------+-----------+---------------------------------------------+ | ||||
| +--------------------------+-----------+---------------------------------------------+ | ||||
| | field                    |   type    | description                                 | | ||||
| +=========================+===========+=============================================+ | ||||
| |`gain_mode_default_loss` | (number)  | Default value if Roadm/params/loss is       | | ||||
| |                         |           | None in the topology input description.     | | ||||
| +==========================+===========+=============================================+ | ||||
| | ``target_pch_out_db``    | (number)  | Auto-design sets the ROADM egress channel   | | ||||
| |                          |           | power. This reflects typical control loop   | | ||||
| |                          |           | algorithms that adjust ROADM losses to      | | ||||
| |                          |           | equalize channels (eg coming from different | | ||||
| |                          |           | ingress direction or add ports)             | | ||||
| |                          |           | This is the default value                   | | ||||
| |                          |           | Roadm/params/target_pch_out_db if no value  | | ||||
| |                          |           | is given in the ``Roadm`` element in the    | | ||||
| |                          |           | topology input description.                 | | ||||
| |                          |           | This default value is ignored if a          | | ||||
| |                         |           | params/loss value is input in the           | | ||||
| |                         |           | topology for a given ROADM.                 | | ||||
| +-------------------------+-----------+---------------------------------------------+ | ||||
| |`power_mode_pref`        | (number)  | Power mode only. Auto-design sets the       | | ||||
| |                         |           | power of ROADM ingress amplifiers to        | | ||||
| |                         |           | power_dbm + power_range_db,                 | | ||||
| |                         |           | regardless of existing gain settings        | | ||||
| |                         |           | from the topology JSON input.               | | ||||
| |                         |           | Auto-design sets the Roadm loss so that     | | ||||
| |                         |           | its egress channel power = power_mode_pref, | | ||||
| |                         |           | regardless of existing loss settings        | | ||||
| |                         |           | from the topology JSON input. It means      | | ||||
| |                         |           | that the output power from a ROADM (and      | | ||||
| |                         |           | therefore its OSNR contribution) is Cte     | | ||||
| |                         |           | and not depending from power_dbm and        | | ||||
| |                         |           | power_range_db sweep settings. This         | | ||||
| |                         |           | choice is meant to reflect some typical     | | ||||
| |                         |           | control loop algorithms.                    | | ||||
| +-------------------------+-----------+---------------------------------------------+ | ||||
| |                          |           | params/target_pch_out_db value is input in  | | ||||
| |                          |           | the topology for a given ROADM.             | | ||||
| +--------------------------+-----------+---------------------------------------------+ | ||||
| | ``add_drop_osnr``        | (number)  | OSNR contribution from the add/drop ports   | | ||||
| +--------------------------+-----------+---------------------------------------------+ | ||||
| | ``restrictions``         | (dict of  | If non-empty, keys ``preamp_variety_list``  | | ||||
| |                          |  strings) | and ``booster_variety_list`` represent      | | ||||
| |                          |           | list of ``type_variety`` amplifiers which   | | ||||
| |                          |           | are allowed for auto-design within ROADM's  | | ||||
| |                          |           | line degrees.                               | | ||||
| |                          |           |                                             | | ||||
| |                          |           | If no booster should be placed on a degree, | | ||||
| |                          |           | insert a ``Fused`` node on the degree       | | ||||
| |                          |           | output.                                     | | ||||
| +--------------------------+-----------+---------------------------------------------+ | ||||
|  | ||||
| The `SpectralInformation` object can be configured as follows. The user can | ||||
| The ``SpectralInformation`` object can be configured as follows. The user can | ||||
| only modify the value of existing parameters. It defines a spectrum of N | ||||
| identical carriers. While the code libraries allow for different carriers and | ||||
| power levels, the current user parametrization only allows one carrier type and | ||||
| @@ -450,21 +463,18 @@ one power/channel definition. | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | field                |   type    | description                               | | ||||
| +======================+===========+===========================================+ | ||||
| | `f_min/max`          | (number)  | In Hz. Carrier min max excursion          | | ||||
| | ``f_min``,           | (number)  | In Hz. Carrier min max excursion.         | | ||||
| | ``f_max``            |           |                                           | | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | `baud_rate`          | (number)  | In Hz. Simulated baud rate.               | | ||||
| | ``baud_rate``        | (number)  | In Hz. Simulated baud rate.               | | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | `spacing`            | (number)  | In Hz. Carrier spacing.                   | | ||||
| | ``spacing``          | (number)  | In Hz. Carrier spacing.                   | | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | `roll_off`           | (number)  | Not used.                                 | | ||||
| | ``roll_off``         | (number)  | Not used.                                 | | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | `OSNR`               | (number)  | Not used.                                 | | ||||
| | ``tx_osnr``          | (number)  | In dB. OSNR out from transponder.         | | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | `bit_rate`           | (number)  | Not used.                                 | | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | `tx_osnr`            | (number)  | In dB. OSNR out from transponder.         | | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | `power_dbm`          | (number)  | Reference channel power. In gain mode     | | ||||
| | ``power_dbm``        | (number)  | Reference channel power. In gain mode     | | ||||
| |                      |           | (see spans/power_mode = false), all gain  | | ||||
| |                      |           | settings are offset w/r/t this reference  | | ||||
| |                      |           | power. In power mode, it is the           | | ||||
| @@ -477,17 +487,30 @@ one power/channel definition. | ||||
| |                      |           | power sweep is defined (see after) the    | | ||||
| |                      |           | design is not repeated.                   | | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | `power_range_db`     | (number)  | Power sweep excursion around power_dbm.   | | ||||
| | ``power_range_db``   | (number)  | Power sweep excursion around power_dbm.   | | ||||
| |                      |           | It is not the min and max channel power   | | ||||
| |                      |           | values! The reference power becomes:      | | ||||
| |                      |           | power_range_db + power_dbm.               | | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
| | ``sys_margins``      | (number)  | In dB. Added margin on min required       | | ||||
| |                      |           | transceiver OSNR.                         |          | ||||
| +----------------------+-----------+-------------------------------------------+ | ||||
|  | ||||
| The `transmission_main_example.py <examples/transmission_main_example.py>`_ | ||||
| script propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing and 0 | ||||
| dBm/channel. These are not yet parametrized but can be modified directly in the | ||||
| script (via the SpectralInformation structure) to accommodate any baud rate, | ||||
| spacing, power or channel count demand. | ||||
| The `transmission_main_example.py <examples/transmission_main_example.py>`_ script propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing and 0 dBm/channel.  | ||||
| Launch power can be overridden by using the ``--power`` argument. | ||||
| Spectrum information is not yet parametrized but can be modified directly in the ``eqpt_config.json`` (via the ``SpectralInformation`` -SI- structure) to accommodate any baud rate or spacing. | ||||
| The number of channel is computed based on ``spacing`` and ``f_min``, ``f_max`` values. | ||||
|  | ||||
| An experimental support for Raman amplification is available: | ||||
|  | ||||
| .. code-block:: shell | ||||
|  | ||||
|      $ ./examples/transmission_main_example.py \ | ||||
|        examples/raman_edfa_example_network.json \ | ||||
|        --sim examples/sim_params.json --show-channels | ||||
|  | ||||
| Configuration of Raman pumps (their frequencies, power and pumping direction) is done via the `RamanFiber element in the network topology <examples/raman_edfa_example_network.json>`_. | ||||
| General numeric parameters for simulaiton control are provided in the `examples/sim_params.json <examples/sim_params.json>`_. | ||||
|  | ||||
| Use `examples/path_requests_run.py <examples/path_requests_run.py>`_ to run multiple optimizations as follows: | ||||
|  | ||||
| @@ -496,7 +519,7 @@ Use `examples/path_requests_run.py <examples/path_requests_run.py>`_ to run mult | ||||
|      $ python path_requests_run.py -h | ||||
|      Usage: path_requests_run.py [-h] [-v] [-o OUTPUT] [network_filename] [service_filename] [eqpt_filename] | ||||
|  | ||||
| The `network_filename` and `service_filename` can be an XLS or JSON file. The `eqpt_filename` must be a JSON file. | ||||
| The ``network_filename`` and ``service_filename`` can be an XLS or JSON file. The ``eqpt_filename`` must be a JSON file. | ||||
|  | ||||
| To see an example of it, run: | ||||
|  | ||||
| @@ -507,10 +530,10 @@ To see an example of it, run: | ||||
|  | ||||
| This program requires a list of connections to be estimated and the equipment | ||||
| library. The program computes performances for the list of services (accepts | ||||
| json or excel format) using the same spectrum propagation modules as | ||||
| transmission_main_example.py. Explanation on the Excel template is provided in | ||||
| JSON or Excel format) using the same spectrum propagation modules as | ||||
| ``transmission_main_example.py``. Explanation on the Excel template is provided in | ||||
| the `Excel_userguide.rst <Excel_userguide.rst#service-sheet>`_. Template for | ||||
| the json format can be found here: `service-template.json | ||||
| the JSON format can be found here: `service-template.json | ||||
| <service-template.json>`_. | ||||
|  | ||||
| Contributing | ||||
| @@ -519,8 +542,8 @@ Contributing | ||||
| ``gnpy`` is looking for additional contributors, especially those with experience | ||||
| planning and maintaining large-scale, real-world mesh optical networks. | ||||
|  | ||||
| To get involved, please contact James Powell | ||||
| <james.powell@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>. | ||||
| To get involved, please contact Jan Kundrát | ||||
| <jan.kundrat@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>. | ||||
|  | ||||
| ``gnpy`` contributions are currently limited to members of `TIP | ||||
| <http://telecominfraproject.com>`_. Membership is free and open to all. | ||||
| @@ -578,6 +601,11 @@ implementations. | ||||
|   :alt: Build Status | ||||
|   :scale: 100% | ||||
|  | ||||
| .. |doi| image:: https://zenodo.org/badge/96894149.svg | ||||
|   :target: https://zenodo.org/badge/latestdoi/96894149 | ||||
|   :alt: DOI | ||||
|   :scale: 100% | ||||
|  | ||||
| TIP OOPT/PSE & PSE WG Charter | ||||
| ----------------------------- | ||||
|  | ||||
|   | ||||
| @@ -874,7 +874,7 @@ month={Sept},} | ||||
|   number = {7}, | ||||
|   journal = {Optics Express}, | ||||
|   urlyear = {2017-11-14}, | ||||
|   year = {2012-03-26}, | ||||
|   date = {2012-03-26}, | ||||
|   year = {2012}, | ||||
|   pages = {7777}, | ||||
|   author = {Bononi, A. and Serena, P. and Rossi, N. and Grellier, E. and Vacondio, F.} | ||||
| @@ -1114,7 +1114,7 @@ month={Sept},} | ||||
|   number = {26}, | ||||
|   journal = {Optics Express}, | ||||
|   urlyear = {2017-11-16}, | ||||
|   year = {2013-12-30}, | ||||
|   date = {2013-12-30}, | ||||
|   year = {2013}, | ||||
|   pages = {32254}, | ||||
|   author = {Bononi, Alberto and Beucher, Ottmar and Serena, Paolo} | ||||
|   | ||||
| @@ -173,5 +173,4 @@ texinfo_documents = [ | ||||
|      'Miscellaneous'), | ||||
| ] | ||||
|  | ||||
|  | ||||
|  | ||||
| autodoc_default_flags = ['members', 'undoc-members', 'private-members', 'show-inheritance'] | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								docs/images/GNPy-banner.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								docs/images/GNPy-banner.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 518 KiB | 
| @@ -51,6 +51,8 @@ Contributors in alphabetical order | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | David    | Boertjes   | Ciena                 | dboertje@ciena.com                   | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Diego    | Landa      | Facebook              | dlanda@fb.com                        | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Esther   | Le Rouzic  | Orange                | esther.lerouzic@orange.com           | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Gabriele | Galimberti | Cisco                 | ggalimbe@cisco.com                   | | ||||
| @@ -61,20 +63,28 @@ Contributors in alphabetical order | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | James    | Powell     | Telecom Infra Project | james.powell@telecominfraproject.com | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Jeanluc  | Auge       | Orange                | jeanluc.auge@orange.com              | | ||||
| | Jan      | Kundrát    | Telecom Infra Project | jan.kundrat@telecominfraproject.com  | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Jonas    | Martensson | RISE Research Sweden  | jonas.martensson@ri.se               | | ||||
| | Jeanluc  | Augé       | Orange                | jeanluc.auge@orange.com              | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Jonas    | Mårtensson | RISE Research Sweden  | jonas.martensson@ri.se               | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Mattia   | Cantono    | Politecnico di Torino | mattia.cantono@polito.it             | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Miguel   | Garrich    | University Catalunya  | miquel.garrich@upct.es               | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Stefan   | Melin      | Telia Company         | Stefan.Melin@teliacompany.com        | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Raj      | Nagarajan  | Lumentum              | raj.nagarajan@lumentum.com           | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Roberts  | Miculens   | Lattelecom            | roberts.miculens@lattelecom.lv       | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Shengxiang | Zhu      | University of Arizona | szhu@email.arizona.edu               | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Stefan   | Melin      | Telia Company         | Stefan.Melin@teliacompany.com        | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Vittorio | Curri      | Politecnico di Torino | vittorio.curri@polito.it             | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
| | Xufeng   | Liu        | Jabil                 | xufeng_liu@jabil.com                 | | ||||
| +----------+------------+-----------------------+--------------------------------------+ | ||||
|  | ||||
| -------------- | ||||
|  | ||||
|   | ||||
| @@ -4,10 +4,39 @@ gnpy\.core package | ||||
| Submodules | ||||
| ---------- | ||||
|  | ||||
| gnpy\.core\.ansi_escapes module | ||||
| ------------------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.ansi_escapes | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| gnpy\.core\.convert module | ||||
| -------------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.convert | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| gnpy\.core\.elements module | ||||
| --------------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.elements | ||||
|  | ||||
| gnpy\.core\.equipment module | ||||
| ---------------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.equipment | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| gnpy\.core\.exceptions module | ||||
| ----------------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.exceptions | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
| @@ -16,30 +45,34 @@ gnpy\.core\.execute module | ||||
| -------------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.execute | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| gnpy\.core\.info module | ||||
| ----------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.info | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| gnpy\.core\.network module | ||||
| -------------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.network | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| gnpy\.core\.node module | ||||
| ----------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.node | ||||
|  | ||||
| gnpy\.core\.request module | ||||
| -------------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.request | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| gnpy\.core\.service_sheet module | ||||
| -------------------------------- | ||||
|  | ||||
| .. automodule:: gnpy.core.service_sheet | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
| @@ -48,23 +81,14 @@ gnpy\.core\.units module | ||||
| ------------------------ | ||||
|  | ||||
| .. automodule:: gnpy.core.units | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| gnpy\.core\.utils module | ||||
| ------------------------ | ||||
|  | ||||
| .. automodule:: gnpy.core.utils | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
|  | ||||
| Module contents | ||||
| --------------- | ||||
|  | ||||
| .. automodule:: gnpy.core | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|   | ||||
| @@ -12,6 +12,3 @@ Module contents | ||||
| --------------- | ||||
|  | ||||
| .. automodule:: gnpy | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|   | ||||
							
								
								
									
										124
									
								
								examples/2019-demo-equipment.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								examples/2019-demo-equipment.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| {     "Edfa":[ | ||||
|  | ||||
|             { | ||||
|             "type_variety": "fixed27", | ||||
|             "type_def": "fixed_gain", | ||||
|             "gain_flatmax": 27, | ||||
|             "gain_min": 27, | ||||
|             "p_max": 21, | ||||
|             "nf0": 5.5, | ||||
|             "allowed_for_design": false | ||||
|             }, | ||||
|  | ||||
|             { | ||||
|             "type_variety": "fixed22", | ||||
|             "type_def": "fixed_gain", | ||||
|             "gain_flatmax": 22, | ||||
|             "gain_min": 22, | ||||
|             "p_max": 21, | ||||
|             "nf0": 5.5, | ||||
|             "allowed_for_design": false | ||||
|             } | ||||
|       ], | ||||
|       "Fiber":[{ | ||||
|             "type_variety": "SSMF", | ||||
|             "dispersion": 1.67e-05, | ||||
|             "gamma": 0.00127 | ||||
|             }, | ||||
|             { | ||||
|             "type_variety": "NZDF", | ||||
|             "dispersion": 0.5e-05, | ||||
|             "gamma": 0.00146 | ||||
|             }, | ||||
|             { | ||||
|             "type_variety": "LOF", | ||||
|             "dispersion": 2.2e-05, | ||||
|             "gamma": 0.000843 | ||||
|             } | ||||
|       ], | ||||
|       "Span":[{ | ||||
|             "power_mode": false, | ||||
|             "delta_power_range_db": [-2,3,0.5], | ||||
|             "max_fiber_lineic_loss_for_raman": 0.25, | ||||
|             "target_extended_gain": 2.5, | ||||
|             "max_length": 150, | ||||
|             "length_units": "km", | ||||
|             "max_loss": 28, | ||||
|             "padding": 10, | ||||
|             "EOL": 0, | ||||
|             "con_in": 0, | ||||
|             "con_out": 0 | ||||
|             } | ||||
|       ], | ||||
|       "Roadm":[{ | ||||
|             "target_pch_out_db": -25, | ||||
|             "add_drop_osnr": 30.00, | ||||
|             "restrictions": { | ||||
|                             "preamp_variety_list":[], | ||||
|                             "booster_variety_list":[] | ||||
|                             }             | ||||
|             }], | ||||
|       "SI":[{ | ||||
|             "f_min": 191.6e12, | ||||
|             "baud_rate": 32e9, | ||||
|             "f_max":195.1e12, | ||||
|             "spacing": 50e9, | ||||
|             "power_dbm": 0, | ||||
|             "power_range_db": [0,0,1], | ||||
|             "roll_off": 0.15, | ||||
|             "tx_osnr": 40, | ||||
|             "sys_margins": 2 | ||||
|             }], | ||||
|       "Transceiver":[ | ||||
|             { | ||||
|             "type_variety": "Cassini", | ||||
|             "frequency":{ | ||||
|                         "min": 191.35e12, | ||||
|                         "max": 196.1e12 | ||||
|                         }, | ||||
|             "mode":[ | ||||
|                        { | ||||
|  | ||||
|                        "format": "dp-qpsk", | ||||
|                        "baud_rate": 32e9, | ||||
|                        "OSNR": 11, | ||||
|                        "bit_rate": 100e9, | ||||
|                        "roll_off": 0.15, | ||||
|                        "tx_osnr": 40, | ||||
|                        "min_spacing": 37.5e9, | ||||
|                        "cost":1 | ||||
|                        }, | ||||
|                        { | ||||
|                        "format": "16-qam", | ||||
|                        "baud_rate": 66e9, | ||||
|                        "OSNR": 15, | ||||
|                        "bit_rate": 200e9, | ||||
|                        "roll_off": 0.15, | ||||
|                        "tx_osnr": 40, | ||||
|                        "min_spacing": 75e9, | ||||
|                        "cost":1 | ||||
|                        } | ||||
|                    ] | ||||
|             }, | ||||
|             { | ||||
|             "type_variety": "Voyager", | ||||
|             "frequency":{ | ||||
|                         "min": 191.35e12, | ||||
|                         "max": 196.1e12 | ||||
|                         }, | ||||
|             "mode":[ | ||||
|                        { | ||||
|                        "format": "mode 1", | ||||
|                        "baud_rate": 32e9, | ||||
|                        "OSNR": 12, | ||||
|                        "bit_rate": 100e9, | ||||
|                        "roll_off": 0.15, | ||||
|                        "tx_osnr": 40, | ||||
|                        "min_spacing": 37.5e9, | ||||
|                        "cost":1 | ||||
|                        } | ||||
|                    ] | ||||
|             } | ||||
|       ] | ||||
|  | ||||
| } | ||||
							
								
								
									
										67
									
								
								examples/2019-demo-services.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								examples/2019-demo-services.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| { | ||||
|   "path-request": [ | ||||
|     { | ||||
|       "request-id": "first", | ||||
|       "source": "netconf:10.0.254.93:830", | ||||
|       "destination": "netconf:10.0.254.94:830", | ||||
|       "src-tp-id": "trx-Amsterdam", | ||||
|       "dst-tp-id": "trx-Bremen", | ||||
|       "bidirectional": true, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Cassini", | ||||
|           "trx_mode": null, | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 100000000000.0 | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "second", | ||||
|       "source": "netconf:10.0.254.93:830", | ||||
|       "destination": "netconf:10.0.254.94:830", | ||||
|       "src-tp-id": "trx-Amsterdam", | ||||
|       "dst-tp-id": "trx-Bremen", | ||||
|       "bidirectional": true, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Cassini", | ||||
|           "trx_mode": null, | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 100000000000.0 | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "synchronization": [ | ||||
|     { | ||||
|       "synchronization-id": "some redundancy please", | ||||
|       "svec": { | ||||
|         "relaxable": "false", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "first", | ||||
|           "second" | ||||
|         ] | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										1263
									
								
								examples/2019-demo-topology.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1263
									
								
								examples/2019-demo-topology.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										179
									
								
								examples/2019-generate-tip-demo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								examples/2019-generate-tip-demo.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| # How many nodes in the ring topology? Up to eight is supported, then I ran out of cities.. | ||||
| HOW_MANY = 3 | ||||
|  | ||||
| # city names | ||||
| ALL_CITIES = [ | ||||
|     'Amsterdam', | ||||
|     'Bremen', | ||||
|     'Cologne', | ||||
|     'Dueseldorf', | ||||
|     'Eindhoven', | ||||
|     'Frankfurt', | ||||
|     'Ghent', | ||||
|     'Hague', | ||||
| ] | ||||
| # end of configurable parameters | ||||
|  | ||||
|  | ||||
| J = { | ||||
|     "elements": [], | ||||
|     "connections": [], | ||||
| } | ||||
|  | ||||
| def unidir_join(a, b): | ||||
|     global J | ||||
|     J["connections"].append( | ||||
|         {"from_node": a, "to_node": b} | ||||
|     ) | ||||
|  | ||||
| def mk_edfa(name, gain, voa=0.0): | ||||
|     global J | ||||
|     J["elements"].append( | ||||
|         {"uid": name, "type": "Edfa", "type_variety": f"fixed{gain}", "operational": {"gain_target": gain, "out_voa": voa}} | ||||
|     ) | ||||
|  | ||||
| def add_att(a, b, att): | ||||
|     global J | ||||
|     if att > 0: | ||||
|         uid = f"att-({a})-({b})" | ||||
|     else: | ||||
|         uid = f"splice-({a})-({b})" | ||||
|     J["elements"].append( | ||||
|         {"uid": uid, "type": "Fused", "params": {"loss": att}}, | ||||
|     ) | ||||
|     unidir_join(a, uid) | ||||
|     unidir_join(uid, b) | ||||
|     return uid | ||||
|  | ||||
| def build_fiber(city1, city2): | ||||
|     global J | ||||
|     J["elements"].append( | ||||
|         { | ||||
|             "uid": f"fiber-{city1}-{city2}", | ||||
|             "type": "Fiber", | ||||
|             "type_variety": "SSMF", | ||||
|             "params": { | ||||
|                 "length": 50, | ||||
|                 "length_units": "km", | ||||
|                 "loss_coef": 0.2, | ||||
|                 "con_in": 1.5, | ||||
|                 "con_out": 1.5, | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
|  | ||||
| def unidir_patch(a, b): | ||||
|     global J | ||||
|     uid = f"patch-({a})-({b})" | ||||
|     J["elements"].append( | ||||
|         { | ||||
|             "uid": uid, | ||||
|             "type": "Fiber", | ||||
|             "type_variety": "SSMF", | ||||
|             "params": { | ||||
|                 "length": 0, | ||||
|                 "length_units": "km", | ||||
|                 "loss_coef": 0.2, | ||||
|                 "con_in": 0.5, | ||||
|                 "con_out": 0.5, | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
|     add_att(a, uid, 0.0) | ||||
|     add_att(uid, b, 0.0) | ||||
|  | ||||
| for CITY in (ALL_CITIES[x] for x in range(0, HOW_MANY)): | ||||
|     J["elements"].append( | ||||
|         {"uid": f"trx-{CITY}", "type": "Transceiver"} | ||||
|     ) | ||||
|     target_pwr = [ | ||||
|         {"to_node": f"trx-{CITY}", "target_pch_out_db": -25}, | ||||
|         {"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L1))", "target_pch_out_db": -12}, | ||||
|         {"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L2))", "target_pch_out_db": -12}, | ||||
|     ] | ||||
|     J["elements"].append( | ||||
|         {"uid": f"roadm-{CITY}-AD", "type": "Roadm", "params": {"target_pch_out_db": -2.0, "per_degree_target_pch_out_db": target_pwr}} | ||||
|     ) | ||||
|     unidir_join(f"trx-{CITY}", f"roadm-{CITY}-AD") | ||||
|     unidir_join(f"roadm-{CITY}-AD", f"trx-{CITY}") | ||||
|  | ||||
|     for n in (1,2): | ||||
|         target_pwr = [ | ||||
|             {"to_node": f"roadm-{CITY}-L{n}-booster", "target_pch_out_db": -23}, | ||||
|             {"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-AD))", "target_pch_out_db": -12}, | ||||
|         ] | ||||
|         for m in (1,2): | ||||
|             if m == n: | ||||
|                 continue | ||||
|             target_pwr.append( | ||||
|               {"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-L{m}))", "target_pch_out_db": -12}, | ||||
|             ) | ||||
|         J["elements"].append( | ||||
|             {"uid": f"roadm-{CITY}-L{n}", "type": "Roadm", "params": {"target_pch_out_db": -23.0, "per_degree_target_pch_out_db": target_pwr}} | ||||
|         ) | ||||
|         mk_edfa(f"roadm-{CITY}-L{n}-booster", 22) | ||||
|         mk_edfa(f"roadm-{CITY}-L{n}-preamp", 27) | ||||
|         unidir_join(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{n}-booster") | ||||
|         unidir_join(f"roadm-{CITY}-L{n}-preamp", f"roadm-{CITY}-L{n}") | ||||
|  | ||||
|         unidir_patch(f"roadm-{CITY}-AD", f"roadm-{CITY}-L{n}") | ||||
|         unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-AD") | ||||
|         for m in (1,2): | ||||
|             if m == n: | ||||
|                 continue | ||||
|             #add_att(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}", 22) | ||||
|             unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}") | ||||
|  | ||||
| for city1, city2 in ((ALL_CITIES[i], ALL_CITIES[i + 1] if i < HOW_MANY - 1 else ALL_CITIES[0]) for i in range(0, HOW_MANY)): | ||||
|     build_fiber(city1, city2) | ||||
|     unidir_join(f"roadm-{city1}-L1-booster", f"fiber-{city1}-{city2}") | ||||
|     unidir_join(f"fiber-{city1}-{city2}", f"roadm-{city2}-L2-preamp") | ||||
|     build_fiber(city2, city1) | ||||
|     unidir_join(f"roadm-{city2}-L2-booster", f"fiber-{city2}-{city1}") | ||||
|     unidir_join(f"fiber-{city2}-{city1}", f"roadm-{city1}-L1-preamp") | ||||
|  | ||||
|  | ||||
| for _, E in enumerate(J["elements"]): | ||||
|     uid = E["uid"] | ||||
|     if uid.startswith("roadm-") and (uid.endswith("-L1-booster") or uid.endswith("-L2-booster")): | ||||
|         E["operational"]["out_voa"] = 12.0 | ||||
|     #if uid.endswith("-AD-add"): | ||||
|     #    E["operational"]["out_voa"] = 21 | ||||
|  | ||||
| translate = { | ||||
|     #"trx-Amsterdam": "10.0.254.93", | ||||
|     #"trx-Bremen": "10.0.254.94", | ||||
|     "trx-Amsterdam": "10.0.254.76", | ||||
|     "trx-Bremen": "10.0.254.77", | ||||
|  | ||||
|     # Amsterdam A/D: coherent-v9u | ||||
|     "roadm-Amsterdam-AD": "10.0.254.107", | ||||
|     # Bremen A/D: -spi | ||||
|     "roadm-Bremen-AD": "10.0.254.225", | ||||
|  | ||||
|     # Amsterdam -> Bremen ...QR79 | ||||
|     "roadm-Amsterdam-L1": "10.0.254.78", | ||||
|     # Bremen -> Amsterdam ...QCP9 | ||||
|     "roadm-Bremen-L2": "10.0.254.102", | ||||
|  | ||||
|     # Bremen -> Cologne ...WKP | ||||
|     "roadm-Bremen-L1": "10.0.254.100", | ||||
|     # Cologne -> Bremen ...QLK6 | ||||
|     "roadm-Cologne-L2": "10.0.254.104", | ||||
|  | ||||
|     # Cologne -> Amsterdam ...TQQ | ||||
|     "roadm-Cologne-L1": "10.0.254.99", | ||||
|     # Amsterdam -> Cologne ...Q7JS | ||||
|     "roadm-Amsterdam-L2": "10.0.254.79", | ||||
|  | ||||
|     # spare Line/Degree ...QC8B | ||||
|     "spare-line-degree": "10.0.254.101", | ||||
|     # spare Add/Drop: ...NNN | ||||
|     "spare-add-drop": "10.0.254.228", | ||||
| } | ||||
|  | ||||
| import json | ||||
| s = json.dumps(J, indent=2) | ||||
| for (old, new) in translate.items(): | ||||
|     s = s.replace(f'"{old}"', f'"netconf:{new}:830"') | ||||
| print(s) | ||||
							
								
								
									
										160
									
								
								examples/Juniper-BoosterHG.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								examples/Juniper-BoosterHG.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,160 @@ | ||||
| { | ||||
|       "nf_fit_coeff": [ | ||||
|         0.0008, | ||||
|         0.0272, | ||||
|         -0.2249, | ||||
|         6.4902 | ||||
|        ], | ||||
|       "f_min": 191.35e12, | ||||
|       "f_max": 196.1e12, | ||||
|        "nf_ripple": [ | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0, | ||||
|         0.0 | ||||
|     ], | ||||
|     "gain_ripple": [ | ||||
|         0.15017064489112, | ||||
|         0.14157768006701, | ||||
|         0.00223094639866, | ||||
|         -0.06701528475711, | ||||
|         -0.05982935510889, | ||||
|         -0.01028161641541, | ||||
|         0.02740682579566, | ||||
|         0.02795958961474, | ||||
|         0.00107516750419, | ||||
|         -0.02199015912898, | ||||
|         -0.00877407872698, | ||||
|         0.0453465242881, | ||||
|         0.1204721524288, | ||||
|         0.18936662479061, | ||||
|         0.23826109715241, | ||||
|         0.26956762981574, | ||||
|         0.27836159966498, | ||||
|         0.26941687604691, | ||||
|         0.23579878559464, | ||||
|         0.18147717755444, | ||||
|         0.1191656197655, | ||||
|         0.05921587102177, | ||||
|         0.01509526800668, | ||||
|         -0.01053287269681, | ||||
|         -0.02475397822447, | ||||
|         -0.01847257118928, | ||||
|         -0.00420121440538, | ||||
|         0.01584903685091, | ||||
|         0.0399193886097, | ||||
|         0.04494451423784, | ||||
|         0.04961788107202, | ||||
|         0.03378873534338, | ||||
|         0.01027114740367, | ||||
|         -0.01319618927973, | ||||
|         -0.04962835008375, | ||||
|         -0.0765630234506, | ||||
|         -0.10606051088777, | ||||
|         -0.13550774706866, | ||||
|         -0.15460322445561, | ||||
|         -0.17113588777219, | ||||
|         -0.18053287269681, | ||||
|         -0.18324644053602, | ||||
|         -0.19440221943049, | ||||
|         -0.20897508375209, | ||||
|         -0.23575900335007, | ||||
|         -0.25188965661642, | ||||
|         -0.22244242043552, | ||||
|         -0.15656302345061 | ||||
|     ], | ||||
|     "dgt": [ | ||||
|         2.4553191172498, | ||||
|         2.44342862248888, | ||||
|         2.41879254989742, | ||||
|         2.38192717604575, | ||||
|         2.33147727493671, | ||||
|         2.26678136721453, | ||||
|         2.19013043016015, | ||||
|         2.10336369905543, | ||||
|         2.01414465424155, | ||||
|         1.92915262384742, | ||||
|         1.85543800978691, | ||||
|         1.79748596476494, | ||||
|         1.75428006928365, | ||||
|         1.72461030013125, | ||||
|         1.70379790088896, | ||||
|         1.68845480656382, | ||||
|         1.6761448370895, | ||||
|         1.66286684904577, | ||||
|         1.64799163036252, | ||||
|         1.63068023161292, | ||||
|         1.61073904908309, | ||||
|         1.58973304612691, | ||||
|         1.56750088631614, | ||||
|         1.54578500307573, | ||||
|         1.5242627235492, | ||||
|         1.50335352244996, | ||||
|         1.48420288841848, | ||||
|         1.46637521309853, | ||||
|         1.44977369463316, | ||||
|         1.43476940680732, | ||||
|         1.42089447397912, | ||||
|         1.40864903907609, | ||||
|         1.3966294751726, | ||||
|         1.38430337205545, | ||||
|         1.3710092503689, | ||||
|         1.35690844654118, | ||||
|         1.3405812000038, | ||||
|         1.32210817897091, | ||||
|         1.30069883494415, | ||||
|         1.27657903892303, | ||||
|         1.24931318255134, | ||||
|         1.21911100318577, | ||||
|         1.18632744096844, | ||||
|         1.15209185089701, | ||||
|         1.11575888725852, | ||||
|         1.07773189112355, | ||||
|         1.03941448941778, | ||||
|         1.0 | ||||
|     ] | ||||
| } | ||||
| @@ -11,64 +11,72 @@ If not present in the "Nodes" sheet, the "Type" column will be implicitly | ||||
| determined based on the topology. | ||||
| """ | ||||
|  | ||||
| from sys import exit | ||||
| try: | ||||
|     from xlrd import open_workbook | ||||
| except ModuleNotFoundError: | ||||
|     exit('Required: `pip install xlrd`') | ||||
| from argparse import ArgumentParser | ||||
| from collections import namedtuple, defaultdict | ||||
|  | ||||
| PARSER = ArgumentParser() | ||||
| PARSER.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls', | ||||
|                     help='create the mandatory columns in Eqpt sheet') | ||||
| ALL_ROWS = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) | ||||
|  | ||||
| Shortlink = namedtuple('Link', 'src dest') | ||||
| class Node: | ||||
|     """ Node element contains uid, list of connected nodes and eqpt type | ||||
|     """ | ||||
|     def __init__(self, uid, to_node): | ||||
|         self.uid = uid | ||||
|         self.to_node = to_node | ||||
|         self.eqpt = None | ||||
|  | ||||
| Shortnode = namedtuple('Node', 'nodename eqt') | ||||
|     def __repr__(self): | ||||
|         return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n' | ||||
|  | ||||
| parser = ArgumentParser() | ||||
| parser.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls', | ||||
|     help = 'create the mandatory columns in Eqpt sheet  ') | ||||
| all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) | ||||
|     def __str__(self): | ||||
|         return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n' | ||||
|  | ||||
| def read_excel(input_filename): | ||||
|     with open_workbook(input_filename) as wb: | ||||
|     """ read excel Nodes and Links sheets and create a dict of nodes with | ||||
|     their to_nodes and type of eqpt | ||||
|     """ | ||||
|     with open_workbook(input_filename) as wobo: | ||||
|         # reading Links sheet | ||||
|         links_sheet = wb.sheet_by_name('Links') | ||||
|         links = [] | ||||
|         nodeoccuranceinlinks = [] | ||||
|         links_by_src = defaultdict(list) | ||||
|         links_by_dest = defaultdict(list) | ||||
|         for row in all_rows(links_sheet, start=5): | ||||
|             links.append(Shortlink(row[0].value,row[1].value)) | ||||
|             links_by_src[row[0].value].append(Shortnode(row[1].value,'')) | ||||
|             links_by_dest[row[1].value].append(Shortnode(row[0].value,'')) | ||||
|             #print(f'source {links[len(links)-1].src} dest {links[len(links)-1].dest}') | ||||
|             nodeoccuranceinlinks.append(row[0].value) | ||||
|             nodeoccuranceinlinks.append(row[1].value) | ||||
|         links_sheet = wobo.sheet_by_name('Links') | ||||
|         nodes = {} | ||||
|         for row in ALL_ROWS(links_sheet, start=5): | ||||
|             try: | ||||
|                 nodes[row[0].value].to_node.append(row[1].value) | ||||
|             except KeyError: | ||||
|                 nodes[row[0].value] = Node(row[0].value, [row[1].value]) | ||||
|             try: | ||||
|                 nodes[row[1].value].to_node.append(row[0].value) | ||||
|             except KeyError: | ||||
|                 nodes[row[1].value] = Node(row[1].value, [row[0].value]) | ||||
|  | ||||
|         # reading Nodes sheet | ||||
|         nodes_sheet = wb.sheet_by_name('Nodes') | ||||
|         nodes = [] | ||||
|         node_degree = [] | ||||
|         for row in all_rows(nodes_sheet, start=5) : | ||||
|         nodes_sheet = wobo.sheet_by_name('Nodes') | ||||
|         for row in ALL_ROWS(nodes_sheet, start=5): | ||||
|             node = row[0].value | ||||
|             eqpt = row[6].value | ||||
|             try: | ||||
|                 if eqpt == 'ILA' and len(nodes[node].to_node) != 2: | ||||
|                     print(f'Inconsistancy ILA node with degree > 2: {node} ') | ||||
|                     exit() | ||||
|                 if eqpt == '' and len(nodes[node].to_node) == 2: | ||||
|                     nodes[node].eqpt = 'ILA' | ||||
|                 elif eqpt == '' and len(nodes[node].to_node) != 2: | ||||
|                     nodes[node].eqpt = 'ROADM' | ||||
|                 else: | ||||
|                     nodes[node].eqpt = eqpt | ||||
|             except KeyError: | ||||
|                 print(f'inconsistancy between nodes and links sheet: {node} is not listed in links') | ||||
|                 exit() | ||||
|         return nodes | ||||
|  | ||||
|             temp_eqt = row[6].value | ||||
|             # verify node degree to confirm eqt type | ||||
|             node_degree.append(nodeoccuranceinlinks.count(row[0].value)) | ||||
|             if temp_eqt.lower() == 'ila' and nodeoccuranceinlinks.count(row[0].value) !=2 : | ||||
|                 print(f'Inconsistancy: node {nodes[len(nodes)-1]} has degree \ | ||||
|                     {node_degree[len(nodes)-1]} and can not be an ILA ... replaced by ROADM') | ||||
|                 temp_eqt = 'ROADM' | ||||
|             if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) == 2 : | ||||
|                 temp_eqt = 'ILA' | ||||
|             if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) != 2 : | ||||
|                 temp_eqt = 'ROADM' | ||||
|             # print(f'node {nodes[len(nodes)-1]} eqt {temp_eqt}') | ||||
|             nodes.append(Shortnode(row[0].value,temp_eqt)) | ||||
|             # print(len(nodes)-1) | ||||
|             print(f'reading: node {nodes[len(nodes)-1].nodename} eqpt {temp_eqt}') | ||||
|         return links,nodes, links_by_src , links_by_dest | ||||
|  | ||||
| def create_eqt_template(links,nodes, links_by_src , links_by_dest, input_filename): | ||||
| def create_eqt_template(nodes, input_filename): | ||||
|     """ writes list of node A node Z corresponding to Nodes and Links sheets in order | ||||
|     to help user populating Eqpt | ||||
|     """ | ||||
|     output_filename = f'{input_filename[:-4]}_eqpt_sheet.txt' | ||||
|     with open(output_filename, 'w', encoding='utf-8') as my_file: | ||||
|         # print header similar to excel | ||||
| @@ -77,27 +85,17 @@ def create_eqt_template(links,nodes, links_by_src , links_by_dest, input_filenam | ||||
|            \nNode A \tNode Z \tamp type \tatt_in \tamp gain \ttilt \tatt_out\ | ||||
|            amp type   \tatt_in \tamp gain   \ttilt   \tatt_out\n') | ||||
|  | ||||
|         tab = [] | ||||
|         temp = [] | ||||
|         i = 0 | ||||
|         for lk in links: | ||||
|             if [e for n,e in nodes if n==lk.src][0] != 'FUSED' : | ||||
|                 temp = [lk.src , lk.dest] | ||||
|                 tab.append(temp) | ||||
|                 my_file.write(f'{temp[0]}\t{temp[1]}\n') | ||||
|         for n in nodes : | ||||
|             if n.eqt.lower() == 'roadm' : | ||||
|                 for src in  links_by_dest[n.nodename] : | ||||
|                     temp = [n.nodename , src.nodename] | ||||
|                     tab.append(temp) | ||||
|                     # print(temp) | ||||
|                     my_file.write(f'{temp[0]}\t{temp[1]}\n') | ||||
|             i = i + 1 | ||||
|  | ||||
|         for node in nodes.values(): | ||||
|             if node.eqpt == 'ILA': | ||||
|                 my_file.write(f'{node.uid}\t{node.to_node[0]}\n') | ||||
|             if node.eqpt == 'ROADM': | ||||
|                 for to_node in node.to_node: | ||||
|                     my_file.write(f'{node.uid}\t{to_node}\n') | ||||
|  | ||||
|         print(f'File {output_filename} successfully created with Node A - Node Z ' + | ||||
|               ' entries for Eqpt sheet in excel file.') | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     args = parser.parse_args() | ||||
|     input_filename = args.workbook | ||||
|     links,nodes,links_by_src, links_by_dest = read_excel(input_filename) | ||||
|     create_eqt_template(links,nodes, links_by_src , links_by_dest , input_filename) | ||||
|     ARGS = PARSER.parse_args() | ||||
|     create_eqt_template(read_excel(ARGS.workbook), ARGS.workbook) | ||||
|   | ||||
							
								
								
									
										1033
									
								
								examples/demo.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1033
									
								
								examples/demo.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,5 +1,6 @@ | ||||
| { | ||||
|     "nf_ripple": "NFR0_96.txt",  | ||||
|     "gain_ripple": "DFG0_96.txt", | ||||
|     "dgt": "DGT_96.txt"     | ||||
|     "nf_ripple": "NFR_96.txt",  | ||||
|     "gain_ripple": "DFG_96.txt", | ||||
|     "dgt": "DGT_96.txt", | ||||
|     "nf_fit_coeff": "pNFfit3.txt" | ||||
| } | ||||
| @@ -1,8 +0,0 @@ | ||||
|   -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01 | ||||
|   -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02 | ||||
|   -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01  -2.0000000000000000e+01 | ||||
|   -2.0500000000000000e+01  -2.0489473680000000e+01  -2.0478947370000000e+01  -2.0468421050000000e+01  -2.0457894740000000e+01  -2.0447368420000000e+01  -2.0436842110000001e+01  -2.0426315790000000e+01  -2.0415789470000000e+01  -2.0405263160000001e+01  -2.0394736840000000e+01  -2.0384210530000001e+01  -2.0373684210000000e+01  -2.0363157890000000e+01  -2.0352631580000001e+01  -2.0342105260000000e+01  -2.0331578950000001e+01  -2.0321052630000001e+01  -2.0310526320000001e+01  -2.0300000000000001e+01  -2.0289473680000000e+01  -2.0278947370000001e+01  -2.0268421050000001e+01  -2.0257894740000001e+01  -2.0247368420000001e+01  -2.0236842110000001e+01  -2.0226315790000001e+01  -2.0215789470000001e+01  -2.0205263160000001e+01  -2.0194736840000001e+01  -2.0184210530000001e+01  -2.0173684210000001e+01  -2.0163157890000001e+01  -2.0152631580000001e+01  -2.0142105260000001e+01  -2.0131578950000002e+01  -2.0121052630000001e+01  -2.0110526320000002e+01  -2.0100000000000001e+01  -2.0089473680000001e+01  -2.0078947370000002e+01  -2.0068421050000001e+01  -2.0057894739999998e+01  -2.0047368420000002e+01  -2.0036842109999998e+01  -2.0026315790000002e+01  -2.0015789470000001e+01  -2.0005263159999998e+01  -1.9994736840000002e+01  -1.9984210529999999e+01  -1.9973684209999998e+01  -1.9963157890000002e+01  -1.9952631579999998e+01  -1.9942105260000002e+01  -1.9931578949999999e+01  -1.9921052629999998e+01  -1.9910526319999999e+01  -1.9899999999999999e+01  -1.9889473679999998e+01  -1.9878947369999999e+01  -1.9868421049999998e+01  -1.9857894739999999e+01  -1.9847368419999999e+01  -1.9836842109999999e+01  -1.9826315789999999e+01  -1.9815789469999999e+01  -1.9805263159999999e+01  -1.9794736839999999e+01  -1.9784210529999999e+01  -1.9773684209999999e+01  -1.9763157889999999e+01  -1.9752631579999999e+01  -1.9742105259999999e+01  -1.9731578949999999e+01  -1.9721052629999999e+01  -1.9710526320000000e+01  -1.9699999999999999e+01  -1.9689473679999999e+01  -1.9678947369999999e+01  -1.9668421049999999e+01  -1.9657894740000000e+01  -1.9647368419999999e+01  -1.9636842110000000e+01  -1.9626315790000000e+01  -1.9615789469999999e+01  -1.9605263160000000e+01  -1.9594736839999999e+01  -1.9584210530000000e+01  -1.9573684210000000e+01  -1.9563157889999999e+01  -1.9552631580000000e+01  -1.9542105260000000e+01  -1.9531578950000000e+01  -1.9521052630000000e+01  -1.9510526320000000e+01  -1.9500000000000000e+01 | ||||
|   -2.0500000000000000e+01  -2.0489473680000000e+01  -2.0478947370000000e+01  -2.0468421050000000e+01  -2.0457894740000000e+01  -2.0447368420000000e+01  -2.0436842110000001e+01  -2.0426315790000000e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.9573684210000000e+01  -1.9563157889999999e+01  -1.9552631580000000e+01  -1.9542105260000000e+01  -1.9531578950000000e+01  -1.9521052630000000e+01  -1.9510526320000000e+01  -1.9500000000000000e+01 | ||||
|   -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.4460000000000001e+01 | ||||
|   -1.4460000000000001e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01  -1.4460000000000001e+01 | ||||
|   -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.4460000000000001e+01  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02  -1.0000000000000000e+02 | ||||
| @@ -1,8 +0,0 @@ | ||||
|    7.0000000000000000e+01	   1.1700000000000000e+02	   1.0800000000000000e+02	   1.0800000000000000e+02	   3.2000000000000000e+01	   7.0000000000000000e+01	   1.0800000000000000e+02	   9.7000000000000000e+01	   1.1600000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	 | ||||
|    7.9000000000000000e+01	   1.1000000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	   7.1000000000000000e+01	   1.1400000000000000e+02	   1.1100000000000000e+02	   1.1700000000000000e+02	   1.1200000000000000e+02	   3.2000000000000000e+01	   6.6000000000000000e+01	   1.0800000000000000e+02	   1.1700000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	 | ||||
|    7.9000000000000000e+01	   1.1000000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	   7.1000000000000000e+01	   1.1400000000000000e+02	   1.1100000000000000e+02	   1.1700000000000000e+02	   1.1200000000000000e+02	   3.2000000000000000e+01	   8.2000000000000000e+01	   1.0100000000000000e+02	   1.0000000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	 | ||||
|    7.0000000000000000e+01	   1.1700000000000000e+02	   1.0800000000000000e+02	   1.0800000000000000e+02	   3.2000000000000000e+01	   1.1900000000000000e+02	   3.2000000000000000e+01	   8.3000000000000000e+01	   8.2000000000000000e+01	   8.3000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	 | ||||
|    6.6000000000000000e+01	   1.1100000000000000e+02	   1.1600000000000000e+02	   1.0400000000000000e+02	   3.2000000000000000e+01	   6.9000000000000000e+01	   1.1000000000000000e+02	   1.0000000000000000e+02	   1.1500000000000000e+02	   3.2000000000000000e+01	   1.1900000000000000e+02	   3.2000000000000000e+01	   8.3000000000000000e+01	   8.2000000000000000e+01	   8.3000000000000000e+01	 | ||||
|    1.0400000000000000e+02	   1.0100000000000000e+02	   9.7000000000000000e+01	   1.1800000000000000e+02	   1.2100000000000000e+02	   3.2000000000000000e+01	   9.8000000000000000e+01	   1.0800000000000000e+02	   1.1700000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	 | ||||
|    1.0400000000000000e+02	   1.0100000000000000e+02	   9.7000000000000000e+01	   1.1800000000000000e+02	   1.2100000000000000e+02	   3.2000000000000000e+01	   1.1400000000000000e+02	   1.0100000000000000e+02	   1.0000000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	 | ||||
|    1.1900000000000000e+02	   1.1100000000000000e+02	   1.1400000000000000e+02	   1.1500000000000000e+02	   1.1600000000000000e+02	   3.2000000000000000e+01	   9.9000000000000000e+01	   9.7000000000000000e+01	   1.1500000000000000e+02	   1.0100000000000000e+02	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	   3.2000000000000000e+01	 | ||||
| @@ -1,301 +0,0 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
| """ | ||||
| Created on Mon Nov 27 12:32:04 2017 | ||||
|  | ||||
| @author: briantaylor | ||||
| """ | ||||
| import numpy as np | ||||
| from numpy import polyfit, polyval, mean | ||||
| from utilities import lin2db, db2lin, itufs, freq2wavelength | ||||
| import matplotlib.pyplot as plt | ||||
| from scipy.constants import h | ||||
|  | ||||
|  | ||||
| def noise_profile(nf, gain, ffs, df): | ||||
|     """ noise_profile(nf, gain, ffs, df) computes amplifier ase | ||||
|  | ||||
|     :param nf: Noise figure in dB | ||||
|     :param gain: Actual gain calculated for the EDFA in dB units | ||||
|     :param ffs: A numpy array of frequencies | ||||
|     :param df: the reference bw in THz | ||||
|     :type nf: numpy.ndarray | ||||
|     :type gain: numpy.ndarray | ||||
|     :type ffs: numpy.ndarray | ||||
|     :type df: float | ||||
|     :return: the asepower in dBm | ||||
|     :rtype: numpy.ndarray | ||||
|  | ||||
|     ASE POWER USING PER CHANNEL GAIN PROFILE | ||||
|     INPUTS: | ||||
|     NF_dB - Noise figure in dB, vector of length number of channels or | ||||
|             spectral slices | ||||
|     G_dB  - Actual gain calculated for the EDFA, vector of length number of | ||||
|             channels or spectral slices | ||||
|     ffs     - Center frequency grid of the channels or spectral slices in THz, | ||||
|             vector of length number of channels or spectral slices | ||||
|     dF    - width of each channel or spectral slice in THz, | ||||
|             vector of length number of channels or spectral slices | ||||
|     OUTPUT: | ||||
|         ase_dBm - ase in dBm per channel or spectral slice | ||||
|     NOTE: the output is the total ASE in the channel or spectral slice. For | ||||
|     50GHz channels the ASE BW is effectively 0.4nm. To get to noise power in | ||||
|     0.1nm, subtract 6dB. | ||||
|  | ||||
|     ONSR is usually quoted as channel power divided by | ||||
|     the ASE power in 0.1nm RBW, regardless of the width of the actual | ||||
|     channel.  This is a historical convention from the days when optical | ||||
|     signals were much smaller (155Mbps, 2.5Gbps, ... 10Gbps) than the | ||||
|     resolution of the OSAs that were used to measure spectral power which | ||||
|     were set to 0.1nm resolution for convenience.  Moving forward into | ||||
|     flexible grid and high baud rate signals, it may be convenient to begin | ||||
|     quoting power spectral density in the same BW for both signal and ASE, | ||||
|     e.g. 12.5GHz.""" | ||||
|  | ||||
|     h_mWThz = 1e-3 * h * (1e14)**2 | ||||
|     nf_lin = db2lin(nf) | ||||
|     g_lin = db2lin(gain) | ||||
|     ase = h_mWThz * df * ffs * (nf_lin * g_lin - 1) | ||||
|     asedb = lin2db(ase) | ||||
|  | ||||
|     return asedb | ||||
|  | ||||
|  | ||||
| def gain_profile(dfg, dgt, Pin, gp, gtp): | ||||
|     """ | ||||
|     :param dfg: design flat gain | ||||
|     :param dgt: design gain tilt | ||||
|     :param Pin: channing input power profile | ||||
|     :param gp: Average gain setpoint in dB units | ||||
|     :param gtp: gain tilt setting | ||||
|     :type dfg: numpy.ndarray | ||||
|     :type dgt: numpy.ndarray | ||||
|     :type Pin: numpy.ndarray | ||||
|     :type gp: float | ||||
|     :type gtp: float | ||||
|     :return: gain profile in dBm | ||||
|     :rtype: numpy.ndarray | ||||
|  | ||||
|     AMPLIFICATION USING INPUT PROFILE | ||||
|     INPUTS: | ||||
|         DFG - vector of length number of channels or spectral slices | ||||
|         DGT - vector of length number of channels or spectral slices | ||||
|         Pin - input powers vector of length number of channels or | ||||
|         spectral slices | ||||
|         Gp  - provisioned gain length 1 | ||||
|         GTp - provisioned tilt length 1 | ||||
|  | ||||
|     OUTPUT: | ||||
|         amp gain per channel or spectral slice | ||||
|     NOTE: there is no checking done for violations of the total output power | ||||
|         capability of the amp. | ||||
|         Ported from Matlab version written by David Boerges at Ciena. | ||||
|     Based on: | ||||
|         R. di Muro, "The Er3+ fiber gain coefficient derived from a dynamic | ||||
|         gain | ||||
|         tilt technique", Journal of Lightwave Technology, Vol. 18, Iss. 3, | ||||
|         Pp. 343-347, 2000. | ||||
|     """ | ||||
|     err_tolerance = 1.0e-11 | ||||
|     simple_opt = True | ||||
|  | ||||
|     # TODO make all values linear unit and convert to dB units as needed within | ||||
|     # this function. | ||||
|     nchan = list(range(len(Pin))) | ||||
|  | ||||
|     # TODO find a way to use these or lose them.  Primarily we should have a | ||||
|     # way to determine if exceeding the gain or output power of the amp | ||||
|     tot_in_power_db = lin2db(np.sum(db2lin(Pin))) | ||||
|     avg_gain_db = lin2db(mean(db2lin(dfg))) | ||||
|  | ||||
|     # Linear fit to get the | ||||
|     p = polyfit(nchan, dgt, 1) | ||||
|     dgt_slope = p[0] | ||||
|  | ||||
|     # Calculate the target slope-  Currently assumes equal spaced channels | ||||
|     # TODO make it so that supports arbitrary channel spacing. | ||||
|     targ_slope = gtp / (len(nchan) - 1) | ||||
|  | ||||
|     # 1st estimate of DGT scaling | ||||
|     dgts1 = targ_slope / dgt_slope | ||||
|  | ||||
|     # when simple_opt is true code makes 2 attempts to compute gain and | ||||
|     # the internal voa value.  This is currently here to provide direct | ||||
|     # comparison with original Matlab code.  Will be removed. | ||||
|     # TODO replace with loop | ||||
|  | ||||
|     if simple_opt: | ||||
|  | ||||
|         # 1st estimate of Er gain & voa loss | ||||
|         g1st = dfg + dgt * dgts1 | ||||
|         voa = lin2db(mean(db2lin(g1st))) - gp | ||||
|  | ||||
|         # 2nd estimate of Amp ch gain using the channel input profile | ||||
|         g2nd = g1st - voa | ||||
|         pout_db = lin2db(np.sum(db2lin(Pin + g2nd))) | ||||
|         dgts2 = gp - (pout_db - tot_in_power_db) | ||||
|  | ||||
|         # Center estimate of amp ch gain | ||||
|         xcent = dgts2 | ||||
|         gcent = g1st - voa + dgt * xcent | ||||
|         pout_db = lin2db(np.sum(db2lin(Pin + gcent))) | ||||
|         gavg_cent = pout_db - tot_in_power_db | ||||
|  | ||||
|         # Lower estimate of Amp ch gain | ||||
|         deltax = np.max(g1st) - np.min(g1st) | ||||
|         xlow = dgts2 - deltax | ||||
|         glow = g1st - voa + xlow * dgt | ||||
|         pout_db = lin2db(np.sum(db2lin(Pin + glow))) | ||||
|         gavg_low = pout_db - tot_in_power_db | ||||
|  | ||||
|         # Upper gain estimate | ||||
|         xhigh = dgts2 + deltax | ||||
|         ghigh = g1st - voa + xhigh * dgt | ||||
|         pout_db = lin2db(np.sum(db2lin(Pin + ghigh))) | ||||
|         gavg_high = pout_db - tot_in_power_db | ||||
|  | ||||
|         # compute slope | ||||
|         slope1 = (gavg_low - gavg_cent) / (xlow - xcent) | ||||
|         slope2 = (gavg_cent - gavg_high) / (xcent - xhigh) | ||||
|  | ||||
|         if np.abs(gp - gavg_cent) <= err_tolerance: | ||||
|             dgts3 = xcent | ||||
|         elif gp < gavg_cent: | ||||
|             dgts3 = xcent - (gavg_cent - gp) / slope1 | ||||
|         else: | ||||
|             dgts3 = xcent + (-gavg_cent + gp) / slope2 | ||||
|  | ||||
|         gprofile = g1st - voa + dgt * dgts3 | ||||
|     else: | ||||
|         gprofile = None | ||||
|  | ||||
|     return gprofile | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|  | ||||
|     plt.close('all') | ||||
|     fc = itufs(0.05) | ||||
|     lc = freq2wavelength(fc) / 1000 | ||||
|     nchan = list(range(len(lc))) | ||||
|     df = np.array([0.05] * (nchan[-1] + 1)) | ||||
|     # TODO remove path dependence | ||||
|     path = '' | ||||
|  | ||||
|     """ | ||||
|     DFG_96:  Design flat gain at each wavelength in the 96 channel 50GHz ITU | ||||
|     grid in dB.  This can be experimentally determined by measuring the gain | ||||
|     at each wavelength using a full, flat channel (or ASE) load at the input. | ||||
|     The amplifier should be set to its maximum flat gain (tilt = 0dB).  This | ||||
|     measurement captures the ripple of the amplifier.  If the amplifier was | ||||
|     designed to be mimimum ripple at some other tilt value, then the ripple | ||||
|     reflected in this measurement will not be that minimum.  However, when | ||||
|     the DGT gets applied through the provisioning of tilt, the model should | ||||
|     accurately reproduce the expected ripple at that tilt value.  One could | ||||
|     also do the measurement at some expected tilt value and back-calculate | ||||
|     this vector using the DGT method.  Alternatively, one could re-write the | ||||
|     algorithm to accept a nominal tilt and a tiled version of this vector. | ||||
|     """ | ||||
|  | ||||
|     dfg_96 = np.loadtxt(path + 'DFG_96.txt') | ||||
|  | ||||
|     """maximum gain for flat operation - the amp in the data file was designed | ||||
|     for 25dB gain and has an internal VOA for setting the external gain | ||||
|     """ | ||||
|  | ||||
|     avg_dfg = dfg_96.mean() | ||||
|  | ||||
|     """ | ||||
|     DGT_96:  This is the so-called Dynamic Gain Tilt of the EDFA in dB/dB. It | ||||
|     is the change in gain at each wavelength corresponding to a 1dB change at | ||||
|     the longest wavelength supported.  The value can be obtained | ||||
|     experimentally or through analysis of the cross sections or Giles | ||||
|     parameters of the Er fibre.  This is experimentally measured by changing | ||||
|     the gain of the amplifier above the maximum flat gain while not changing | ||||
|     the internal VOA (i.e. the mid-stage VOA is set to minimum and does not | ||||
|     change during the measurement). Note that the measurement can change the | ||||
|     gain by an arbitrary amount and divide by the gain change (in dB) which | ||||
|     is measured at the reference wavelength (the red end of the band). | ||||
|     """ | ||||
|  | ||||
|     dgt_96 = np.loadtxt(path + 'DGT_96.txt') | ||||
|  | ||||
|     """ | ||||
|     pNFfit3:  Cubic polynomial fit coefficients to noise figure in dB | ||||
|     averaged across wavelength as a function of gain change from design flat: | ||||
|  | ||||
|         NFavg = pNFfit3(1)*dG^3 + pNFfit3(2)*dG^2 pNFfit3(3)*dG + pNFfit3(4) | ||||
|     where | ||||
|         dG = GainTarget - average(DFG_96) | ||||
|     note that dG will normally be a negative value. | ||||
|     """ | ||||
|  | ||||
|     nf_fitco = np.loadtxt(path + 'pNFfit3.txt') | ||||
|  | ||||
|     """NFR_96:  Noise figure ripple in dB away from the average noise figure | ||||
|     across the band.  This captures the wavelength dependence of the NF.  To | ||||
|     calculate the NF across channels, one uses the cubic fit coefficients | ||||
|     with the external gain target to get the average nosie figure, NFavg and | ||||
|     then adds this to NFR_96: | ||||
|     NF_96 = NFR_96 + NFavg | ||||
|     """ | ||||
|  | ||||
|     nf_ripple = np.loadtxt(path + 'NFR_96.txt') | ||||
|  | ||||
|     # This is an example to set the provisionable gain and gain-tilt values | ||||
|     # Tilt is in units of dB/THz | ||||
|     gain_target = 20.0 | ||||
|     tilt_target = -0.7 | ||||
|  | ||||
|     # calculate the NF for the EDFA at this gain setting | ||||
|     dg = gain_target - avg_dfg | ||||
|     nf_avg = polyval(nf_fitco, dg) | ||||
|     nf_96 = nf_ripple + nf_avg | ||||
|  | ||||
|     # get the input power profiles to show | ||||
|     pch2d = np.loadtxt(path + 'Pchan2D.txt') | ||||
|  | ||||
|     # Load legend and assemble legend text | ||||
|     pch2d_legend_data = np.loadtxt(path + 'Pchan2DLegend.txt') | ||||
|     pch2d_legend = [] | ||||
|     for ea in pch2d_legend_data: | ||||
|         s = ''.join([chr(xx) for xx in ea.astype(dtype=int)]).strip() | ||||
|         pch2d_legend.append(s) | ||||
|  | ||||
|     # assemble plot | ||||
|     axis_font = {'fontname': 'Arial', 'size': '16', 'fontweight': 'bold'} | ||||
|     title_font = {'fontname': 'Arial', 'size': '17', 'fontweight': 'bold'} | ||||
|     tic_font = {'fontname': 'Arial', 'size': '12'} | ||||
|  | ||||
|     plt.rcParams["font.family"] = "Arial" | ||||
|     plt.figure() | ||||
|     plt.plot(nchan, pch2d.T, '.-', lw=2) | ||||
|     plt.xlabel('Channel Number', **axis_font) | ||||
|     plt.ylabel('Channel Power [dBm]', **axis_font) | ||||
|     plt.title('Input Power Profiles for Different Channel Loading', | ||||
|               **title_font) | ||||
|     plt.legend(pch2d_legend, loc=5) | ||||
|     plt.grid() | ||||
|     plt.ylim((-100, -10)) | ||||
|     plt.xlim((0, 110)) | ||||
|     plt.xticks(np.arange(0, 100, 10), **tic_font) | ||||
|     plt.yticks(np.arange(-110, -10, 10), **tic_font) | ||||
|  | ||||
|     plt.figure() | ||||
|     ea = pch2d[1, :] | ||||
|     for ea in pch2d: | ||||
|         chgain = gain_profile(dfg_96, dgt_96, ea, gain_target, tilt_target) | ||||
|         pase = noise_profile(nf_96, chgain, fc, df) | ||||
|         pout = lin2db(db2lin(ea + chgain) + db2lin(pase)) | ||||
|         plt.plot(nchan, pout, '.-', lw=2) | ||||
|     plt.title('Output Power with ASE for Different Channel Loading', | ||||
|               **title_font) | ||||
|     plt.xlabel('Channel Number', **axis_font) | ||||
|     plt.ylabel('Channel Power [dBm]', **axis_font) | ||||
|     plt.grid() | ||||
|     plt.ylim((-50, 10)) | ||||
|     plt.xlim((0, 100)) | ||||
|     plt.xticks(np.arange(0, 100, 10), **tic_font) | ||||
|     plt.yticks(np.arange(-50, 10, 10), **tic_font) | ||||
|     plt.legend(pch2d_legend, loc=5) | ||||
|     plt.show() | ||||
							
								
								
									
										300
									
								
								examples/edfa_model/amplifier_models_description.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								examples/edfa_model/amplifier_models_description.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,300 @@ | ||||
| ********************************************* | ||||
| Amplifier models and configuration | ||||
| ********************************************* | ||||
|  | ||||
|  | ||||
| 1. Equipment configuration description | ||||
| ####################################### | ||||
|  | ||||
| Equipment description defines equipment types and parameters. | ||||
| It takes place in the default **eqpt_config.json** file.  | ||||
| By default **transmission_main_example.py** uses **eqpt_config.json** file and that | ||||
| can be changed with **-e** or **--equipment** command line parameter. | ||||
|  | ||||
| 2. Amplifier parameters and subtypes | ||||
| ####################################### | ||||
|  | ||||
| Several amplifiers can be used by GNpy, so they are defined as an array of equipment parameters in **eqpt_config.json** file. | ||||
|  | ||||
| - *"type_variety"*: | ||||
|     Each amplifier is identified by its unique *"type_variety"*, which is used in the topology files input to reference a specific amplifier. It is a user free defined id. | ||||
|      | ||||
|     For each amplifier *type_variety*, specific parameters are describing its attributes and performance: | ||||
|  | ||||
| - *"type_def"*: | ||||
|     Sets the amplifier model that the simulation will use to calculate the ase noise contribution. 5 models are defined with reserved words: | ||||
|  | ||||
|     - *"advanced_model"* | ||||
|     - *"variable_gain"* | ||||
|     - *"fixed_gain"* | ||||
|     - *"dual_stage"* | ||||
|     - *"openroadm"* | ||||
|         *see next section for a full description of these models* | ||||
|  | ||||
| - *"advanced_config_from_json"*: | ||||
|     **This parameter is only applicable to the _"advanced_model"_ model** | ||||
|      | ||||
|     json file name describing: | ||||
|  | ||||
|     - nf_fit_coeff | ||||
|     - f_min/max | ||||
|     - gain_ripple | ||||
|     - nf_ripple  | ||||
|     - dgt | ||||
|      | ||||
|     *see next section for a full description* | ||||
|  | ||||
| - *"gain_flatmax"*:  | ||||
|     amplifier maximum gain in dB before its extended gain range: flat or nominal tilt output.  | ||||
|      | ||||
|     If gain > gain_flatmax, the amplifier will tilt, based on its dgt function | ||||
|  | ||||
|     If gain > gain_flatmax + target_extended_gain, the amplifier output power is reduced to  not exceed the extended gain range. | ||||
|  | ||||
| - *"gain_min"*:  | ||||
|     amplifier minimum gain in dB. | ||||
|  | ||||
|     If gain < gain_min, the amplifier input is automatically padded, which results in | ||||
|  | ||||
|     NF += gain_min - gain  | ||||
|  | ||||
| - *"p_max"*:  | ||||
|     amplifier max output power, full load | ||||
|  | ||||
|     Total signal output power will not be allowed beyond this value | ||||
|  | ||||
| - *"nf_min/max"*: | ||||
|     **These parameters are only applicable to the _"variable_gain"_ model** | ||||
|  | ||||
|     min & max NF values in dB | ||||
|  | ||||
|     NF_min is the amplifier NF @ gain_max   | ||||
|  | ||||
|     NF_max is the amplifier NF @ gain_min   | ||||
|  | ||||
| - *"nf_coef"*:  | ||||
|     **This parameter is only applicable to the *"openroadm"* model** | ||||
|  | ||||
|     [a, b, c, d] 3rd order polynomial coefficients list to define the incremental OSNR vs Pin | ||||
|      | ||||
|     Incremental OSNR is the amplifier OSNR contribution | ||||
|      | ||||
|     Pin is the amplifier channel input power defined in a 50GHz bandwidth | ||||
|      | ||||
|     Incremental OSNR = a*Pin³ + b*Pin² + c*Pin + d | ||||
|  | ||||
| - *"preamp_variety"*:  | ||||
|     **This parameter is only applicable to the _"dual_stage"_ model** | ||||
|  | ||||
|     1st stage type_variety | ||||
|  | ||||
| - *"booster_variety"*:  | ||||
|     **This parameter is only applicable to the *"dual_stage"* model** | ||||
|  | ||||
|     2nd stage type_variety | ||||
|  | ||||
| - *"out_voa_auto"*: true/false | ||||
|     **power_mode only** | ||||
|  | ||||
|     **This parameter is only applicable to the *"advanced_model"* and *"variable_gain"* models** | ||||
|  | ||||
|     If "out_voa_auto": true, auto_design will chose the output_VOA value that maximizes the amplifier gain within its power capability and therefore minimizes its NF. | ||||
|  | ||||
| - *"allowed_for_design"*: true/false | ||||
|     **auto_design only** | ||||
|  | ||||
|     Tells auto_design if this amplifier can be picked for the design (deactivates unwanted amplifiers) | ||||
|  | ||||
|     It does not prevent the use of an amplifier if it is placed in the topology input. | ||||
|  | ||||
|     .. code-block:: json | ||||
|  | ||||
|         {"Edfa": [{ | ||||
|                     "type_variety": "std_medium_gain", | ||||
|                     "type_def": "variable_gain", | ||||
|                     "gain_flatmax": 26, | ||||
|                     "gain_min": 15, | ||||
|                     "p_max": 23, | ||||
|                     "nf_min": 6, | ||||
|                     "nf_max": 10, | ||||
|                     "out_voa_auto": false, | ||||
|                     "allowed_for_design": true | ||||
|                     }, | ||||
|                     { | ||||
|                     "type_variety": "std_low_gain", | ||||
|                     "type_def": "variable_gain", | ||||
|                     "gain_flatmax": 16, | ||||
|                     "gain_min": 8, | ||||
|                     "p_max": 23, | ||||
|                     "nf_min": 6.5, | ||||
|                     "nf_max": 11, | ||||
|                     "out_voa_auto": false, | ||||
|                     "allowed_for_design": true | ||||
|                     } | ||||
|             ]} | ||||
|  | ||||
|  | ||||
| 3. Amplifier models | ||||
| ####################################### | ||||
|  | ||||
| In an opensource and multi-vendor environnement, it is needed to support different use cases and context. Therefore several models are supported for amplifiers. | ||||
|  | ||||
| 5 types of EDFA definition are possible and referenced by the *"type_def"* parameter with the following reserved words: | ||||
|  | ||||
| -  *"advanced_model"*  | ||||
|     This model is refered as a whitebox model because of the detailed level of knowledge that is required. The amplifier NF model and ripple definition are described by a json file referenced with *"advanced_config_from_json"*: json filename. This json file contains: | ||||
|  | ||||
|     - nf_fit_coeff: [a,b,c,d] | ||||
|           | ||||
|         3rd order polynomial NF = f(-dg) coeficients list | ||||
|  | ||||
|         dg = gain - gain_max | ||||
|  | ||||
|     - f_min/max: amplifier frequency range in Hz | ||||
|     - gain_ripple : [...] | ||||
|  | ||||
|         amplifier gain ripple excursion comb list in dB across the frequency range. | ||||
|     - nf_ripple : [...] | ||||
|          | ||||
|         amplifier nf ripple excursion comb list in dB across the frequency range.  | ||||
|     - dgt : [...] | ||||
|         amplifier dynamic gain tilt comb list across the frequency range. | ||||
|              | ||||
|         *See next section for the generation of this json file* | ||||
|  | ||||
|     .. code-block:: json-object | ||||
|  | ||||
|         "Edfa":[{ | ||||
|                 "type_variety": "high_detail_model_example", | ||||
|                 "type_def": "advanced_model", | ||||
|                 "gain_flatmax": 25, | ||||
|                 "gain_min": 15, | ||||
|                 "p_max": 21, | ||||
|                 "advanced_config_from_json": "std_medium_gain_advanced_config.json", | ||||
|                 "out_voa_auto": false, | ||||
|                 "allowed_for_design": false | ||||
|                 } | ||||
|             ] | ||||
|  | ||||
| - *"variable_gain"*  | ||||
|     This model is refered as an operator model because a lower level of knowledge is required. A full polynomial description of the NF cross the gain range is not required. Instead, NF_min and NF_max values are required and used by the code to model a dual stage amplifier with an internal mid stage VOA. NF_min and NF_max values are typically available from equipment suppliers data-sheet. | ||||
|  | ||||
|     There is a default JSON file ”default_edfa_config.json”* to enforce 0 tilt and ripple values because GNpy core algorithm is a multi-carrier propogation. | ||||
|     - gain_ripple =[0,...,0] | ||||
|     - nf_ripple = [0,...,0] | ||||
|     - dgt = [...] generic dgt comb | ||||
|  | ||||
|     .. code-block:: json-object | ||||
|  | ||||
|         "Edfa":[{ | ||||
|                 "type_variety": "std_medium_gain", | ||||
|                 "type_def": "variable_gain", | ||||
|                 "gain_flatmax": 26, | ||||
|                 "gain_min": 15, | ||||
|                 "p_max": 23, | ||||
|                 "nf_min": 6, | ||||
|                 "nf_max": 10, | ||||
|                 "out_voa_auto": false, | ||||
|                 "allowed_for_design": true | ||||
|                 } | ||||
|             ] | ||||
|  | ||||
| -  *"fixed_gain"*  | ||||
|     This model is also an operator model with a single NF value that emulates basic single coil amplifiers without internal VOA. | ||||
|  | ||||
|     if gain_min < gain < gain_max, NF == nf0 | ||||
|      | ||||
|     if gain < gain_min, the amplifier input is automatically padded, which results in  | ||||
|  | ||||
|     NF += gain_min - gain | ||||
|  | ||||
|     .. code-block:: json-object | ||||
|  | ||||
|         "Edfa":[{ | ||||
|                 "type_variety": "std_fixed_gain", | ||||
|                 "type_def": "fixed_gain", | ||||
|                 "gain_flatmax": 21, | ||||
|                 "gain_min": 20, | ||||
|                 "p_max": 21, | ||||
|                 "nf0": 5.5, | ||||
|                 "allowed_for_design": false | ||||
|                 } | ||||
|             ] | ||||
|  | ||||
| - *"openroadm"*  | ||||
|     This model is a black box model replicating OpenRoadm MSA spec for ILA. | ||||
|  | ||||
|     .. code-block:: json-object | ||||
|  | ||||
|         "Edfa":[{ | ||||
|                 "type_variety": "low_noise", | ||||
|                 "type_def": "openroadm", | ||||
|                 "gain_flatmax": 27, | ||||
|                 "gain_min": 12, | ||||
|                 "p_max": 22, | ||||
|                 "nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62], | ||||
|                 "allowed_for_design": false | ||||
|                 } | ||||
|             ] | ||||
|  | ||||
| - *"dual_stage"*  | ||||
|     This model allows the cascade (pre-defined combination) of any 2 amplifiers already described in the eqpt_config.json library. | ||||
|      | ||||
|     - preamp_variety defines the 1st stge type variety | ||||
|      | ||||
|     - booster variety defines the 2nd stage type variety | ||||
|      | ||||
|     Both preamp and booster variety must exist in the eqpt libray | ||||
|     The resulting NF is the sum of the 2 amplifiers  | ||||
|     The preamp is operated to its maximum gain | ||||
|      | ||||
|     - gain_min indicates to auto_design when this dual_stage should be used | ||||
|      | ||||
|     But unlike other models the 1st stage input will not be padded: it is always operated to its maximu gain and min NF. Therefore if gain adaptation and padding is needed it will be performed by the 2nd stage. | ||||
|  | ||||
|     .. code-block:: json | ||||
|  | ||||
|                 { | ||||
|                 "type_variety": "medium+low_gain", | ||||
|                 "type_def": "dual_stage", | ||||
|                 "gain_min": 25, | ||||
|                 "preamp_variety": "std_medium_gain", | ||||
|                 "booster_variety": "std_low_gain", | ||||
|                 "allowed_for_design": true | ||||
|                 } | ||||
|  | ||||
| 4. advanced_config_from_json  | ||||
| ####################################### | ||||
|  | ||||
| The build_oa_json.py library in gnpy/examples/edfa_model can be used to build the json file required for the amplifier advanced_model type_def: | ||||
|  | ||||
| Update an existing json file with all the 96ch txt files for a given amplifier type | ||||
| amplifier type 'OA_type1' is hard coded but can be modified and other types added | ||||
| returns an updated amplifier json file: output_json_file_name = 'edfa_config.json' | ||||
| amplifier file names | ||||
|  | ||||
| Convert a set of amplifier files + input json definiton file into a valid edfa_json_file: | ||||
|  | ||||
| nf_fit_coeff: NF 3rd order polynomial coefficients txt file | ||||
|  | ||||
| nf = f(dg) with dg = gain_operational - gain_max | ||||
|  | ||||
| nf_ripple: NF ripple excursion txt file | ||||
|  | ||||
| gain_ripple: gain ripple txt file | ||||
|  | ||||
| dgt: dynamic gain txt file | ||||
|  | ||||
| input json file in argument (defult = 'OA.json') | ||||
|  | ||||
| the json input file should have the following fields: | ||||
|  | ||||
|     .. code-block:: json | ||||
|  | ||||
|         { | ||||
|             "nf_fit_coeff": "nf_filename.txt", | ||||
|             "nf_ripple": "nf_ripple_filename.txt",  | ||||
|             "gain_ripple": "DFG_filename.txt", | ||||
|             "dgt": "DGT_filename.txt" | ||||
|         } | ||||
|  | ||||
| @@ -4,7 +4,6 @@ | ||||
| Created on Tue Jan 30 12:32:00 2018 | ||||
|  | ||||
| @author: jeanluc-auge | ||||
| @comments about amplifier input files from Brian Taylor & Dave Boertjes | ||||
|  | ||||
| update an existing json file with all the 96ch txt files for a given amplifier type | ||||
| amplifier type 'OA_type1' is hard coded but can be modified and other types added | ||||
| @@ -18,46 +17,29 @@ from gnpy.core.utils import lin2db, db2lin | ||||
|  | ||||
| """amplifier file names | ||||
| convert a set of amplifier files + input json definiton file into a valid edfa_json_file: | ||||
| nf_fit_coeff: NF polynomial coefficients txt file (optional) | ||||
| nf_fit_coeff: NF 3rd order polynomial coefficients txt file | ||||
|     nf = f(dg) | ||||
|     with dg = gain_operational - gain_max | ||||
| nf_ripple: NF ripple excursion txt file | ||||
| dfg: gain txt file | ||||
| gain_ripple: gain ripple txt file | ||||
| dgt: dynamic gain txt file | ||||
| input json file in argument (defult = 'OA.json') | ||||
|  | ||||
| the json input file should have the following fields: | ||||
| { | ||||
|     "gain_flatmax": 25, | ||||
|     "gain_min": 15, | ||||
|     "p_max": 21, | ||||
|     "nf_fit_coeff": "pNFfit3.txt", | ||||
|     "nf_ripple": "NFR_96.txt",  | ||||
|     "dfg": "DFG_96.txt", | ||||
|     "dgt": "DGT_96.txt", | ||||
|     "nf_model":  | ||||
|         { | ||||
|         "enabled": true, | ||||
|         "nf_min": 5.8, | ||||
|         "nf_max": 10 | ||||
|         } | ||||
|     "nf_fit_coeff": "nf_filename.txt", | ||||
|     "nf_ripple": "nf_ripple_filename.txt",  | ||||
|     "gain_ripple": "DFG_filename.txt", | ||||
|     "dgt": "DGT_filename.txt", | ||||
| } | ||||
| gain_flat = max flat gain (dB) | ||||
| gain_min = min gain (dB) : will consider an input VOA if below (TBD vs throwing an exception) | ||||
| p_max = max power (dBm) | ||||
| nf_fit = boolean (True, False) :  | ||||
|         if False nf_fit_coeff are ignored and nf_model fields are used | ||||
|  | ||||
| """ | ||||
|  | ||||
| input_json_file_name = "OA.json" #default path | ||||
| output_json_file_name = "default_edfa_config.json" | ||||
| param_field  ="params" | ||||
| gain_min_field = "gain_min" | ||||
| gain_max_field = "gain_flatmax" | ||||
| gain_ripple_field = "dfg" | ||||
| gain_ripple_field = "gain_ripple" | ||||
| nf_ripple_field = "nf_ripple" | ||||
| nf_fit_coeff = "nf_fit_coeff" | ||||
| nf_model_field = "nf_model" | ||||
| nf_model_enabled_field = "enabled" | ||||
| nf_min_field  ="nf_min" | ||||
| nf_max_field = "nf_max" | ||||
|  | ||||
| def read_file(field, file_name): | ||||
|     """read and format the 96 channels txt files describing the amplifier NF and ripple | ||||
| @@ -76,6 +58,7 @@ def read_file(field, file_name): | ||||
|         #consider ripple excursion only to avoid redundant information | ||||
|         #because the max flat_gain is already given by the 'gain_flat' field in json | ||||
|         #remove the mean component | ||||
|         print(file_name, ', mean value =', data.mean(), ' is substracted') | ||||
|         data = data - data.mean() | ||||
|     data = data.tolist() | ||||
|     return data | ||||
|   | ||||
| @@ -1,313 +0,0 @@ | ||||
| { | ||||
|     "params": { | ||||
|         "gain_flatmax": 25, | ||||
|         "gain_min": 15, | ||||
|         "p_max": 21, | ||||
|         "nf_fit_coeff": [ | ||||
|             0.000168241, | ||||
|             0.0469961, | ||||
|             0.0359549, | ||||
|             5.82851 | ||||
|         ], | ||||
|         "nf_ripple": [ | ||||
|             -0.3110761646066259, | ||||
|             -0.3110761646066259, | ||||
|             -0.31110274831665313, | ||||
|             -0.31419329378173544, | ||||
|             -0.3172854168606314, | ||||
|             -0.32037911876162584, | ||||
|             -0.3233255190215882, | ||||
|             -0.31624321721895354, | ||||
|             -0.30915729645781326, | ||||
|             -0.30206775396360075, | ||||
|             -0.2949045115165272, | ||||
|             -0.26632156113294336, | ||||
|             -0.23772399031437283, | ||||
|             -0.20911178784023846, | ||||
|             -0.18048410390821285, | ||||
|             -0.14379944379052215, | ||||
|             -0.10709599992470213, | ||||
|             -0.07037375788020579, | ||||
|             -0.03372858157230583, | ||||
|             -0.015660302006048, | ||||
|             0.0024172385953583004, | ||||
|             0.020504047353947653, | ||||
|             0.03860013139908377, | ||||
|             0.05670549786742816, | ||||
|             0.07482015390297145, | ||||
|             0.0838762040768461, | ||||
|             0.09284481475528361, | ||||
|             0.1018180306253394, | ||||
|             0.11079585523492333, | ||||
|             0.1020395478432815, | ||||
|             0.09310160456603413, | ||||
|             0.08415906712621996, | ||||
|             0.07521193198077789, | ||||
|             0.0676340601339394, | ||||
|             0.06005437964543287, | ||||
|             0.052470799141237305, | ||||
|             0.044883315610536455, | ||||
|             0.037679759069084225, | ||||
|             0.03047647598902483, | ||||
|             0.02326948274513522, | ||||
|             0.01605877647020772, | ||||
|             0.021248462316134083, | ||||
|             0.02657315875107553, | ||||
|             0.03190060058247842, | ||||
|             0.03723078993416436, | ||||
|             0.04256372893215024, | ||||
|             0.047899419704645264, | ||||
|             0.03915515813685565, | ||||
|             0.030289222542492025, | ||||
|             0.021418708618354456, | ||||
|             0.012573926129294415, | ||||
|             0.006240488799898697, | ||||
|             -9.622162373026585e-05, | ||||
|             -0.006436207679519103, | ||||
|             -0.012779471908040341, | ||||
|             -0.02038153550619876, | ||||
|             -0.027999803010447587, | ||||
|             -0.035622012697103154, | ||||
|             -0.043236398934156144, | ||||
|             -0.04493583574805963, | ||||
|             -0.04663615264317309, | ||||
|             -0.048337350303318156, | ||||
|             -0.050039429413028365, | ||||
|             -0.051742390657545205, | ||||
|             -0.05342028484370278, | ||||
|             -0.05254242298580185, | ||||
|             -0.05166410580536087, | ||||
|             -0.05078533294804249, | ||||
|             -0.04990610405914272, | ||||
|             -0.05409792133358102, | ||||
|             -0.05832916277634124, | ||||
|             -0.06256260169582961, | ||||
|             -0.06660356886269536, | ||||
|             -0.04779792991567815, | ||||
|             -0.028982516728038848, | ||||
|             -0.010157321677553965, | ||||
|             0.00861320615127981, | ||||
|             0.01913736978785662, | ||||
|             0.029667009055877668, | ||||
|             0.04020212822983975, | ||||
|             0.050742731588695494, | ||||
|             0.061288823415841555, | ||||
|             0.07184040799914815, | ||||
|             0.1043252636301016, | ||||
|             0.13687829834471027, | ||||
|             0.1694483010211072, | ||||
|             0.202035284929368, | ||||
|             0.23624619427167134, | ||||
|             0.27048596623174515, | ||||
|             0.30474360397422756, | ||||
|             0.3390191214858807, | ||||
|             0.36358851509924695, | ||||
|             0.38814205928193013, | ||||
|             0.41270842850729195, | ||||
|             0.4372876328262819, | ||||
|             0.4372876328262819 | ||||
|         ], | ||||
|         "dgt": [ | ||||
|             2.714526681131686, | ||||
|             2.705443819238505, | ||||
|             2.6947834587664494, | ||||
|             2.6841217449620203, | ||||
|             2.6681935771243177, | ||||
|             2.6521732021128046, | ||||
|             2.630396440815385, | ||||
|             2.602860350286428, | ||||
|             2.5696460593920065, | ||||
|             2.5364027376452056, | ||||
|             2.499446286796604, | ||||
|             2.4587748041127506, | ||||
|             2.414398437185221, | ||||
|             2.3699990328716107, | ||||
|             2.322373696229342, | ||||
|             2.271520771371253, | ||||
|             2.2174389328192197, | ||||
|             2.16337565384239, | ||||
|             2.1183028432496016, | ||||
|             2.082225099873648, | ||||
|             2.055100772005235, | ||||
|             2.0279625371819305, | ||||
|             2.0008103857988204, | ||||
|             1.9736443063300082, | ||||
|             1.9482128147680253, | ||||
|             1.9245345552113182, | ||||
|             1.9026104247588487, | ||||
|             1.8806927939516411, | ||||
|             1.862235672444246, | ||||
|             1.847275503201129, | ||||
|             1.835814081380705, | ||||
|             1.824381436842932, | ||||
|             1.8139629377087627, | ||||
|             1.8045606557581335, | ||||
|             1.7961751115773796, | ||||
|             1.7877868031023945, | ||||
|             1.7793941781790852, | ||||
|             1.7709972329654864, | ||||
|             1.7625959636196327, | ||||
|             1.7541903672600494, | ||||
|             1.7459181197626403, | ||||
|             1.737780757913635, | ||||
|             1.7297783508684146, | ||||
|             1.7217732861435076, | ||||
|             1.7137640932265894, | ||||
|             1.7057507692361864, | ||||
|             1.6918150918099673, | ||||
|             1.6719047669939942, | ||||
|             1.6460167077689267, | ||||
|             1.6201194134191075, | ||||
|             1.5986915141218316, | ||||
|             1.5817353179379183, | ||||
|             1.569199764184379, | ||||
|             1.5566577309558969, | ||||
|             1.545374152761467, | ||||
|             1.5353620432989845, | ||||
|             1.5266220576235803, | ||||
|             1.5178910621476225, | ||||
|             1.5097346239790443, | ||||
|             1.502153039909686, | ||||
|             1.495145456062699, | ||||
|             1.488134243479226, | ||||
|             1.48111939735681, | ||||
|             1.474100442252211, | ||||
|             1.4670307626366115, | ||||
|             1.4599103316162523, | ||||
|             1.45273959485914, | ||||
|             1.445565137158368, | ||||
|             1.4340878115214444, | ||||
|             1.418273806730323, | ||||
|             1.3981208704326855, | ||||
|             1.3779439775587023, | ||||
|             1.3598972673004606, | ||||
|             1.3439818461440451, | ||||
|             1.3301807335621048, | ||||
|             1.316383926863083, | ||||
|             1.3040618749785347, | ||||
|             1.2932153453410835, | ||||
|             1.2838336236692311, | ||||
|             1.2744470198196236, | ||||
|             1.2650555289898042, | ||||
|             1.2556591482982988, | ||||
|             1.2428104897182262, | ||||
|             1.2264996957264114, | ||||
|             1.2067249615595257, | ||||
|             1.1869318618366975, | ||||
|             1.1672278304018044, | ||||
|             1.1476135933863398, | ||||
|             1.1280891949729075, | ||||
|             1.108555289615659, | ||||
|             1.0895983485572227, | ||||
|             1.0712204022764056, | ||||
|             1.0534217504465226, | ||||
|             1.0356155337864215, | ||||
|             1.017807767853702, | ||||
|             1.0 | ||||
|         ], | ||||
|         "nf_model": { | ||||
|             "enabled": true, | ||||
|             "nf1": 5.727887800964238, | ||||
|             "nf2": 7.727887800964238, | ||||
|             "delta_p": 5.238350271545567 | ||||
|         }, | ||||
|         "gain_ripple": [ | ||||
|             0.1359703369791596, | ||||
|             0.11822862697916037, | ||||
|             0.09542181697916163, | ||||
|             0.06245819697916133, | ||||
|             0.02602813697916062, | ||||
|             -0.0036199830208403228, | ||||
|             -0.018326963020840026, | ||||
|             -0.0246928330208398, | ||||
|             -0.016792253020838643, | ||||
|             -0.0028138630208403015, | ||||
|             0.017572956979162058, | ||||
|             0.038328296979159404, | ||||
|             0.054956336979159914, | ||||
|             0.0670723869791594, | ||||
|             0.07091459697916136, | ||||
|             0.07094413697916124, | ||||
|             0.07114372697916238, | ||||
|             0.07533675697916209, | ||||
|             0.08731066697916035, | ||||
|             0.10313984697916112, | ||||
|             0.12276252697916235, | ||||
|             0.14239527697916188, | ||||
|             0.15945681697916214, | ||||
|             0.1739275269791598, | ||||
|             0.1767381569791624, | ||||
|             0.17037189697916233, | ||||
|             0.15216302697916007, | ||||
|             0.13114358697916018, | ||||
|             0.10802383697916085, | ||||
|             0.08548825697916129, | ||||
|             0.06916723697916183, | ||||
|             0.05848224697916038, | ||||
|             0.05447361697916264, | ||||
|             0.05154489697916276, | ||||
|             0.04946107697915991, | ||||
|             0.04717897697916129, | ||||
|             0.04551704697916037, | ||||
|             0.04467697697916151, | ||||
|             0.04072968697916224, | ||||
|             0.03285456697916089, | ||||
|             0.023488786979161347, | ||||
|             0.01659282697915998, | ||||
|             0.013321846979160057, | ||||
|             0.011234826979162449, | ||||
|             0.01030063697916006, | ||||
|             0.00936596697916059, | ||||
|             0.00874012697916271, | ||||
|             0.00842583697916055, | ||||
|             0.006965146979162284, | ||||
|             0.0040435869791615175, | ||||
|             0.0007104669791608842, | ||||
|             -0.0015763130208377163, | ||||
|             -0.006936193020838033, | ||||
|             -0.016475303020840215, | ||||
|             -0.028748483020837767, | ||||
|             -0.039618433020837784, | ||||
|             -0.051112303020840244, | ||||
|             -0.06468462302083822, | ||||
|             -0.07868024302083754, | ||||
|             -0.09101254302083817, | ||||
|             -0.10103437302083762, | ||||
|             -0.11041488302083735, | ||||
|             -0.11916081302083725, | ||||
|             -0.12789859302083784, | ||||
|             -0.1353792530208402, | ||||
|             -0.14160178302083892, | ||||
|             -0.1455411330208385, | ||||
|             -0.1484450830208388, | ||||
|             -0.14823350302084037, | ||||
|             -0.14591937302083835, | ||||
|             -0.1409032730208395, | ||||
|             -0.13525493302083902, | ||||
|             -0.1279646530208396, | ||||
|             -0.11963431302083904, | ||||
|             -0.11089282302084058, | ||||
|             -0.1027863830208382, | ||||
|             -0.09717347302083823, | ||||
|             -0.09343261302083761, | ||||
|             -0.0913487130208388, | ||||
|             -0.08906007302083907, | ||||
|             -0.0865687230208394, | ||||
|             -0.08407607302083875, | ||||
|             -0.07844600302084004, | ||||
|             -0.06968090302083851, | ||||
|             -0.05947139302083926, | ||||
|             -0.05095282302083959, | ||||
|             -0.042428283020839785, | ||||
|             -0.03218106302083967, | ||||
|             -0.01819858302084043, | ||||
|             -0.0021726530208390216, | ||||
|             0.01393231697916164, | ||||
|             0.028098946979159933, | ||||
|             0.040326236979161934, | ||||
|             0.05257029697916238, | ||||
|             0.06479749697916048, | ||||
|             0.07704745697916238 | ||||
|         ] | ||||
|     } | ||||
| } | ||||
| @@ -1,11 +1,21 @@ | ||||
| {     "Edfa":[{ | ||||
|             "type_variety": "high_detail_model_example", | ||||
|             "type_def": "advanced_model", | ||||
|             "gain_flatmax": 25, | ||||
|             "gain_min": 15, | ||||
|             "p_max": 21, | ||||
|             "advanced_config_from_json": "std_medium_gain_advanced_config.json", | ||||
|             "out_voa_auto": false, | ||||
|             "allowed_for_design": false | ||||
|             },                  { | ||||
|             "type_variety": "Juniper_BoosterHG", | ||||
|             "type_def": "advanced_model", | ||||
|             "gain_flatmax": 25, | ||||
|             "gain_min": 10, | ||||
|             "p_max": 21, | ||||
|             "advanced_config_from_json": "Juniper-BoosterHG.json", | ||||
|             "out_voa_auto": false, | ||||
|             "allowed_for_design": false | ||||
|             },  | ||||
|             { | ||||
|             "type_variety": "operator_model_example", | ||||
| @@ -37,6 +47,17 @@ | ||||
|             "allowed_for_design": false | ||||
|             }, | ||||
|             { | ||||
|             "type_variety": "std_high_gain", | ||||
|             "type_def": "variable_gain", | ||||
|             "gain_flatmax": 35, | ||||
|             "gain_min": 25, | ||||
|             "p_max": 21, | ||||
|             "nf_min": 5.5, | ||||
|             "nf_max": 7, | ||||
|             "out_voa_auto": false, | ||||
|             "allowed_for_design": true | ||||
|             },             | ||||
|             { | ||||
|             "type_variety": "std_medium_gain", | ||||
|             "type_def": "variable_gain", | ||||
|             "gain_flatmax": 26, | ||||
| @@ -59,6 +80,17 @@ | ||||
|             "allowed_for_design": true | ||||
|             }, | ||||
|             { | ||||
|             "type_variety": "high_power", | ||||
|             "type_def": "variable_gain", | ||||
|             "gain_flatmax": 16, | ||||
|             "gain_min": 8, | ||||
|             "p_max": 25, | ||||
|             "nf_min": 9, | ||||
|             "nf_max": 15, | ||||
|             "out_voa_auto": false, | ||||
|             "allowed_for_design": false | ||||
|             },             | ||||
|             { | ||||
|             "type_variety": "std_fixed_gain", | ||||
|             "type_def": "fixed_gain", | ||||
|             "gain_flatmax": 21, | ||||
| @@ -66,6 +98,49 @@ | ||||
|             "p_max": 21, | ||||
|             "nf0": 5.5, | ||||
|             "allowed_for_design": false | ||||
|             }, | ||||
|             { | ||||
|             "type_variety": "4pumps_raman", | ||||
|             "type_def": "fixed_gain", | ||||
|             "gain_flatmax": 12, | ||||
|             "gain_min": 12, | ||||
|             "p_max": 21, | ||||
|             "nf0": -1, | ||||
|             "allowed_for_design": false | ||||
|             },    | ||||
|             { | ||||
|             "type_variety": "hybrid_4pumps_lowgain", | ||||
|             "type_def": "dual_stage", | ||||
|             "raman": true, | ||||
|             "gain_min": 25, | ||||
|             "preamp_variety": "4pumps_raman", | ||||
|             "booster_variety": "std_low_gain", | ||||
|             "allowed_for_design": true | ||||
|             }, | ||||
|             { | ||||
|             "type_variety": "hybrid_4pumps_mediumgain", | ||||
|             "type_def": "dual_stage", | ||||
|             "raman": true, | ||||
|             "gain_min": 25, | ||||
|             "preamp_variety": "4pumps_raman", | ||||
|             "booster_variety": "std_medium_gain", | ||||
|             "allowed_for_design": true | ||||
|             },             | ||||
|             { | ||||
|             "type_variety": "medium+low_gain", | ||||
|             "type_def": "dual_stage", | ||||
|             "gain_min": 25, | ||||
|             "preamp_variety": "std_medium_gain", | ||||
|             "booster_variety": "std_low_gain", | ||||
|             "allowed_for_design": true | ||||
|             }, | ||||
|             { | ||||
|             "type_variety": "medium+high_power", | ||||
|             "type_def": "dual_stage", | ||||
|             "gain_min": 25, | ||||
|             "preamp_variety": "std_medium_gain", | ||||
|             "booster_variety": "high_power", | ||||
|             "allowed_for_design": false | ||||
|             }                                   | ||||
|       ], | ||||
|       "Fiber":[{ | ||||
| @@ -84,9 +159,41 @@ | ||||
|             "gamma": 0.000843 | ||||
|             } | ||||
|       ], | ||||
|       "Spans":[{ | ||||
|       "RamanFiber":[{ | ||||
|             "type_variety": "SSMF", | ||||
|             "dispersion": 1.67e-05, | ||||
|             "gamma": 0.00127, | ||||
|             "raman_efficiency": { | ||||
|               "cr":[ | ||||
|                   0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119, | ||||
|                   0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469, | ||||
|                   0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826, | ||||
|                   0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05, | ||||
|                   7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05, | ||||
|                   2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05, | ||||
|                   2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05, | ||||
|                   1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05, | ||||
|                   1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07, | ||||
|                   2E-07, 1E-07 | ||||
|               ], | ||||
|               "frequency_offset":[ | ||||
|                 0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12, | ||||
|                 7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12, | ||||
|                 13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12, | ||||
|                 17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12, | ||||
|                 22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12, | ||||
|                 28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12, | ||||
|                 35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12, | ||||
|                 41.5e12, 42e12 | ||||
|               ] | ||||
|               } | ||||
|             } | ||||
|       ], | ||||
|       "Span":[{ | ||||
|             "power_mode":true, | ||||
|             "delta_power_range_db": [0,0,0.5], | ||||
|             "delta_power_range_db": [-2,3,0.5], | ||||
|             "max_fiber_lineic_loss_for_raman": 0.25, | ||||
|             "target_extended_gain": 2.5, | ||||
|             "max_length": 150, | ||||
|             "length_units": "km", | ||||
|             "max_loss": 28, | ||||
| @@ -96,10 +203,13 @@ | ||||
|             "con_out": 0 | ||||
|             } | ||||
|       ], | ||||
|       "Roadms":[{ | ||||
|             "gain_mode_default_loss": 20, | ||||
|             "power_mode_pout_target": -20, | ||||
|             "add_drop_osnr": 38 | ||||
|       "Roadm":[{ | ||||
|             "target_pch_out_db": -20, | ||||
|             "add_drop_osnr": 38, | ||||
|             "restrictions": { | ||||
|                             "preamp_variety_list":[], | ||||
|                             "booster_variety_list":[] | ||||
|                             }             | ||||
|             }], | ||||
|       "SI":[{ | ||||
|             "f_min": 191.3e12, | ||||
| @@ -107,10 +217,10 @@ | ||||
|             "f_max":195.1e12, | ||||
|             "spacing": 50e9, | ||||
|             "power_dbm": 0, | ||||
|             "power_range_db": [0,0,0.5], | ||||
|             "power_range_db": [0,0,1], | ||||
|             "roll_off": 0.15, | ||||
|             "tx_osnr": 40, | ||||
|             "sys_margins": 0 | ||||
|             "sys_margins": 2 | ||||
|             }], | ||||
|       "Transceiver":[ | ||||
|             { | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| @@ -623,31 +623,469 @@ | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lannion_CAS to Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Corlay to Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Corlay", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Loudeac to Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Loudeac", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lannion_CAS to Stbrieuc", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Stbrieuc to Rennes_STA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Stbrieuc", | ||||
|           "region": "RLD", | ||||
|           "latitude": 1.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lannion_CAS to Morlaix", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lorient_KMA to Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Vannes_KBE to Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Vannes_KBE", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 4.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Rennes_STA to Stbrieuc", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Rennes_STA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Brest_KLA to Morlaix", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Brest_KLA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 4.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Lannion_CAS to Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Corlay to Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Corlay", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Loudeac to Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Loudeac", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Lorient_KMA to Vannes_KBE", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Lannion_CAS to Stbrieuc", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Stbrieuc to Rennes_STA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Stbrieuc", | ||||
|           "region": "RLD", | ||||
|           "latitude": 1.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Lannion_CAS to Morlaix", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Lorient_KMA to Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Vannes_KBE to Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Vannes_KBE", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 4.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Rennes_STA to Stbrieuc", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Rennes_STA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west edfa in Brest_KLA to Morlaix", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Brest_KLA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 4.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": null, | ||||
|         "delta_p": 1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lorient_KMA to Vannes_KBE", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused", | ||||
|       "params": { | ||||
|         "loss": 0 | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "connections": [ | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "east edfa in Lannion_CAS to Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lannion_CAS to Corlay", | ||||
|       "to_node": "fiber (Lannion_CAS → Corlay)-F061" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Corlay → Lannion_CAS)-F061", | ||||
|       "to_node": "west edfa in Lannion_CAS to Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west edfa in Lannion_CAS to Corlay", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "east edfa in Lannion_CAS to Stbrieuc" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lannion_CAS to Stbrieuc", | ||||
|       "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Stbrieuc → Lannion_CAS)-F056", | ||||
|       "to_node": "west edfa in Lannion_CAS to Stbrieuc" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west edfa in Lannion_CAS to Stbrieuc", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "east edfa in Lannion_CAS to Morlaix" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lannion_CAS to Morlaix", | ||||
|       "to_node": "fiber (Lannion_CAS → Morlaix)-F059" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Morlaix → Lannion_CAS)-F059", | ||||
|       "to_node": "west edfa in Lannion_CAS to Morlaix" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west edfa in Lannion_CAS to Morlaix", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
| @@ -684,18 +1122,34 @@ | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "east edfa in Lorient_KMA to Loudeac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lorient_KMA to Loudeac", | ||||
|       "to_node": "fiber (Lorient_KMA → Loudeac)-F054" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Loudeac → Lorient_KMA)-F054", | ||||
|       "to_node": "west edfa in Lorient_KMA to Loudeac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west edfa in Lorient_KMA to Loudeac", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "east edfa in Lorient_KMA to Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lorient_KMA to Vannes_KBE", | ||||
|       "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055", | ||||
|       "to_node": "west edfa in Lorient_KMA to Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west edfa in Lorient_KMA to Vannes_KBE", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
| @@ -708,10 +1162,18 @@ | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Vannes_KBE", | ||||
|       "to_node": "east edfa in Vannes_KBE to Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Vannes_KBE to Lorient_KMA", | ||||
|       "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055", | ||||
|       "to_node": "west edfa in Vannes_KBE to Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west edfa in Vannes_KBE to Lorient_KMA", | ||||
|       "to_node": "roadm Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
| @@ -724,18 +1186,34 @@ | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056", | ||||
|       "to_node": "east edfa in Stbrieuc to Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Stbrieuc to Rennes_STA", | ||||
|       "to_node": "fiber (Stbrieuc → Rennes_STA)-F057" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Rennes_STA → Stbrieuc)-F057", | ||||
|       "to_node": "west edfa in Stbrieuc to Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west edfa in Stbrieuc to Rennes_STA", | ||||
|       "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Rennes_STA", | ||||
|       "to_node": "east edfa in Rennes_STA to Stbrieuc" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Rennes_STA to Stbrieuc", | ||||
|       "to_node": "fiber (Rennes_STA → Stbrieuc)-F057" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Stbrieuc → Rennes_STA)-F057", | ||||
|       "to_node": "west edfa in Rennes_STA to Stbrieuc" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west edfa in Rennes_STA to Stbrieuc", | ||||
|       "to_node": "roadm Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
| @@ -764,10 +1242,18 @@ | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Brest_KLA", | ||||
|       "to_node": "east edfa in Brest_KLA to Morlaix" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Brest_KLA to Morlaix", | ||||
|       "to_node": "fiber (Brest_KLA → Morlaix)-F060" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Morlaix → Brest_KLA)-F060", | ||||
|       "to_node": "west edfa in Brest_KLA to Morlaix" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west edfa in Brest_KLA to Morlaix", | ||||
|       "to_node": "roadm Brest_KLA" | ||||
|     }, | ||||
|     { | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| @@ -2,10 +2,11 @@ | ||||
|   "path-request": [ | ||||
|     { | ||||
|       "request-id": "0", | ||||
|       "source": "Lorient_KMA", | ||||
|       "destination": "Vannes_KBE", | ||||
|       "source": "trx Lorient_KMA", | ||||
|       "destination": "trx Vannes_KBE", | ||||
|       "src-tp-id": "trx Lorient_KMA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -13,8 +14,8 @@ | ||||
|           "trx_mode": null, | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
| @@ -22,17 +23,15 @@ | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 100000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "1", | ||||
|       "source": "Brest_KLA", | ||||
|       "destination": "Vannes_KBE", | ||||
|       "source": "trx Brest_KLA", | ||||
|       "destination": "trx Vannes_KBE", | ||||
|       "src-tp-id": "trx Brest_KLA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -40,8 +39,8 @@ | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
| @@ -50,66 +49,42 @@ | ||||
|           "path_bandwidth": 200000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm Brest_KLA", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 1, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm Lannion_CAS", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 2, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm Lorient_KMA", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 3, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm Vannes_KBE", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
| @@ -117,10 +92,11 @@ | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "3", | ||||
|       "source": "Lannion_CAS", | ||||
|       "destination": "Rennes_STA", | ||||
|       "source": "trx Lannion_CAS", | ||||
|       "destination": "trx Rennes_STA", | ||||
|       "src-tp-id": "trx Lannion_CAS", | ||||
|       "dst-tp-id": "trx Rennes_STA", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -128,8 +104,8 @@ | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
| @@ -137,17 +113,15 @@ | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 60000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "4", | ||||
|       "source": "Rennes_STA", | ||||
|       "destination": "Lannion_CAS", | ||||
|       "source": "trx Rennes_STA", | ||||
|       "destination": "trx Lannion_CAS", | ||||
|       "src-tp-id": "trx Rennes_STA", | ||||
|       "dst-tp-id": "trx Lannion_CAS", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -155,8 +129,8 @@ | ||||
|           "trx_mode": null, | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 75000000000.0, | ||||
| @@ -164,17 +138,15 @@ | ||||
|           "output-power": 0.0019952623149688794, | ||||
|           "path_bandwidth": 150000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "5", | ||||
|       "source": "Rennes_STA", | ||||
|       "destination": "Lannion_CAS", | ||||
|       "source": "trx Rennes_STA", | ||||
|       "destination": "trx Lannion_CAS", | ||||
|       "src-tp-id": "trx Rennes_STA", | ||||
|       "dst-tp-id": "trx Lannion_CAS", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -182,8 +154,8 @@ | ||||
|           "trx_mode": "mode 2", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 75000000000.0, | ||||
| @@ -191,17 +163,15 @@ | ||||
|           "output-power": 0.0019952623149688794, | ||||
|           "path_bandwidth": 20000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "6", | ||||
|       "source": "Lannion_CAS", | ||||
|       "destination": "Lorient_KMA", | ||||
|       "source": "trx Lannion_CAS", | ||||
|       "destination": "trx Lorient_KMA", | ||||
|       "src-tp-id": "trx Lannion_CAS", | ||||
|       "dst-tp-id": "trx Lorient_KMA", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -209,8 +179,8 @@ | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
| @@ -218,17 +188,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "7", | ||||
|       "source": "Lannion_CAS", | ||||
|       "destination": "Lorient_KMA", | ||||
|       "source": "trx Lannion_CAS", | ||||
|       "destination": "trx Lorient_KMA", | ||||
|       "src-tp-id": "trx Lannion_CAS", | ||||
|       "dst-tp-id": "trx Lorient_KMA", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -236,8 +204,8 @@ | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
| @@ -245,17 +213,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 400000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "7b", | ||||
|       "source": "Lannion_CAS", | ||||
|       "destination": "Lorient_KMA", | ||||
|       "source": "trx Lannion_CAS", | ||||
|       "destination": "trx Lorient_KMA", | ||||
|       "src-tp-id": "trx Lannion_CAS", | ||||
|       "dst-tp-id": "trx Lorient_KMA", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -263,8 +229,8 @@ | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 75000000000.0, | ||||
| @@ -272,9 +238,6 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 400000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
| @@ -282,9 +245,8 @@ | ||||
|     { | ||||
|       "synchronization-id": "3", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "relaxable": "false", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "3", | ||||
|           "1" | ||||
| @@ -294,9 +256,8 @@ | ||||
|     { | ||||
|       "synchronization-id": "4", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "relaxable": "false", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "4", | ||||
|           "5" | ||||
|   | ||||
| @@ -18,77 +18,107 @@ from pathlib import Path | ||||
| from collections import namedtuple | ||||
| from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO | ||||
| from json import dumps, loads | ||||
| from networkx import (draw_networkx_nodes, draw_networkx_edges, | ||||
|                       draw_networkx_labels) | ||||
| from numpy import mean | ||||
| from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element | ||||
| from gnpy.core.utils import load_json | ||||
| from gnpy.core.network import load_network, build_network, set_roadm_loss, save_network | ||||
| from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch, automatic_spacing | ||||
| from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused, Fiber | ||||
| from gnpy.core.network import load_network, build_network, save_network, network_from_json | ||||
| from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | ||||
| from gnpy.core.elements import Transceiver, Roadm | ||||
| from gnpy.core.utils import db2lin, lin2db | ||||
| from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, | ||||
|                               propagate, jsontocsv, Disjunction, compute_path_dsjctn, requests_aggregation, | ||||
|                               propagate_and_optimize_mode) | ||||
| from gnpy.core.request import (Path_request, Result_element, | ||||
|                                propagate, jsontocsv, Disjunction, compute_path_dsjctn, | ||||
|                                requests_aggregation, propagate_and_optimize_mode, | ||||
|                                BLOCKING_NOPATH, BLOCKING_NOMODE, | ||||
|                                find_reversed_path) | ||||
| from gnpy.core.exceptions import (ConfigurationError, EquipmentConfigError, NetworkTopologyError, | ||||
|                                   ServiceError, DisjunctionError) | ||||
| import gnpy.core.ansi_escapes as ansi_escapes | ||||
| from gnpy.core.spectrum_assignment import (build_oms_list, pth_assign_spectrum) | ||||
| from copy import copy, deepcopy | ||||
| from textwrap import dedent | ||||
| from math import ceil | ||||
| import time | ||||
|  | ||||
| from flask import Flask, jsonify, make_response, request | ||||
| from flask_restful import Api, Resource, reqparse, fields | ||||
|  | ||||
| #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | ||||
|  | ||||
| logger = getLogger(__name__) | ||||
| LOGGER = getLogger(__name__) | ||||
|  | ||||
| parser = ArgumentParser(description = 'A function that computes performances for a list of services provided in a json file or an excel sheet.') | ||||
| parser.add_argument('network_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') | ||||
| parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') | ||||
| parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') | ||||
| parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') | ||||
| parser.add_argument('-o', '--output', type = Path) | ||||
| PARSER = ArgumentParser(description='A function that computes performances for a list of ' + | ||||
|                         'services provided in a json file or an excel sheet.') | ||||
| PARSER.add_argument('network_filename', nargs='?', type=Path,\ | ||||
|                     default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\ | ||||
|                     help='input topology file in xls or json') | ||||
| PARSER.add_argument('service_filename', nargs='?', type=Path,\ | ||||
|                     default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\ | ||||
|                     help='input service file in xls or json') | ||||
| PARSER.add_argument('eqpt_filename', nargs='?', type=Path,\ | ||||
|                     default=Path(__file__).parent / 'eqpt_config.json',\ | ||||
|                     help='input equipment library in json. Default is eqpt_config.json') | ||||
| PARSER.add_argument('-bi', '--bidir', action='store_true',\ | ||||
|                     help='considers that all demands are bidir') | ||||
| PARSER.add_argument('-v', '--verbose', action='count', default=0,\ | ||||
|                     help='increases verbosity for each occurence') | ||||
| PARSER.add_argument('-o', '--output', type=Path) | ||||
| PARSER.add_argument('-r', '--rest', action='count', default=0, help='use the REST API') | ||||
|  | ||||
| NETWORK_FILENAME = 'topoDemov1.json' #'disagregatedTopoDemov1.json' # | ||||
|  | ||||
| def requests_from_json(json_data,equipment): | ||||
| APP = Flask(__name__, static_url_path="") | ||||
| API = Api(APP) | ||||
|  | ||||
| def requests_from_json(json_data, equipment): | ||||
|     """ converts the json data into a list of requests elements | ||||
|     """ | ||||
|     requests_list = [] | ||||
|  | ||||
|     for req in json_data['path-request']: | ||||
|         # init all params from request | ||||
|         params = {} | ||||
|         params['request_id'] = req['request-id'] | ||||
|         params['source'] = req['src-tp-id'] | ||||
|         params['destination'] = req['dst-tp-id'] | ||||
|         params['source'] = req['source'] | ||||
|         params['bidir'] = req['bidirectional'] | ||||
|         params['destination'] = req['destination'] | ||||
|         params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type'] | ||||
|         params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode'] | ||||
|         params['format'] = params['trx_mode'] | ||||
|         nd_list = req['optimizations']['explicit-route-include-objects'] | ||||
|         params['nodes_list'] = [n['unnumbered-hop']['node-id'] for n in nd_list] | ||||
|         params['loose_list'] = [n['unnumbered-hop']['hop-type'] for n in nd_list] | ||||
|         params['spacing'] = req['path-constraints']['te-bandwidth']['spacing'] | ||||
|  | ||||
|         try: | ||||
|             nd_list = req['explicit-route-objects']['route-object-include-exclude'] | ||||
|         except KeyError: | ||||
|             nd_list = [] | ||||
|         params['nodes_list'] = [n['num-unnum-hop']['node-id'] for n in nd_list] | ||||
|         params['loose_list'] = [n['num-unnum-hop']['hop-type'] for n in nd_list] | ||||
|         # recover trx physical param (baudrate, ...) from type and mode | ||||
|         # in trx_mode_params optical power is read from equipment['SI']['default'] and | ||||
|         # nb_channel is computed based on min max frequency and spacing | ||||
|         trx_params = trx_mode_params(equipment,params['trx_type'],params['trx_mode'],True) | ||||
|         trx_params = trx_mode_params(equipment, params['trx_type'], params['trx_mode'], True) | ||||
|         params.update(trx_params) | ||||
|         # print(trx_params['min_spacing']) | ||||
|         # optical power might be set differently in the request. if it is indicated then the | ||||
|         # params['power'] is updated | ||||
|         try: | ||||
|             if req['path-constraints']['te-bandwidth']['output-power']: | ||||
|                 params['power'] = req['path-constraints']['te-bandwidth']['output-power'] | ||||
|  | ||||
|         except KeyError: | ||||
|             pass | ||||
|         # same process for nb-channel | ||||
|         f_min = params['f_min'] | ||||
|         f_max_from_si = params['f_max'] | ||||
|         if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None : | ||||
|         try: | ||||
|             if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None: | ||||
|                 nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] | ||||
|                 params['nb_channel'] = nch | ||||
|                 spacing = params['spacing'] | ||||
|                 params['f_max'] = f_min + nch*spacing | ||||
|         else : | ||||
|             params['nb_channel'] = automatic_nch(f_min,f_max_from_si,params['spacing']) | ||||
|  | ||||
|             else: | ||||
|                 params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing']) | ||||
|         except KeyError: | ||||
|             params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing']) | ||||
|         consistency_check(params, f_max_from_si) | ||||
|  | ||||
|         try : | ||||
|         try: | ||||
|             params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] | ||||
|         except KeyError: | ||||
|             pass | ||||
| @@ -96,304 +126,444 @@ def requests_from_json(json_data,equipment): | ||||
|     return requests_list | ||||
|  | ||||
| def consistency_check(params, f_max_from_si): | ||||
|     """ checks that the requested parameters are consistant (spacing vs nb channel, | ||||
|         vs transponder mode...) | ||||
|     """ | ||||
|     f_min = params['f_min'] | ||||
|     f_max = params['f_max'] | ||||
|     max_recommanded_nb_channels = automatic_nch(f_min,f_max, | ||||
|                 params['spacing']) | ||||
|     max_recommanded_nb_channels = automatic_nch(f_min, f_max, params['spacing']) | ||||
|     if params['baud_rate'] is not None: | ||||
|         #implicitely means that a mode is defined with min_spacing | ||||
|         if params['min_spacing']>params['spacing'] :  | ||||
|             msg = f'Request {params["request_id"]} has spacing below transponder {params["trx_type"]}'+\ | ||||
|                 f' {params["trx_mode"]} min spacing value {params["min_spacing"]*1e-9}GHz.\n'+\ | ||||
|                 'Computation stopped' | ||||
|         if params['min_spacing'] > params['spacing']: | ||||
|             msg = f'Request {params["request_id"]} has spacing below transponder ' +\ | ||||
|                   f'{params["trx_type"]} {params["trx_mode"]} min spacing value ' +\ | ||||
|                   f'{params["min_spacing"]*1e-9}GHz.\nComputation stopped' | ||||
|             print(msg) | ||||
|             logger.critical(msg) | ||||
|             exit() | ||||
|         if f_max>f_max_from_si: | ||||
|             LOGGER.critical(msg) | ||||
|             raise ServiceError(msg) | ||||
|         if f_max > f_max_from_si: | ||||
|             msg = dedent(f''' | ||||
|             Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz  | ||||
|             is not consistent with frequency range {f_min*1e-12} THz, {f_max*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz. | ||||
|             max recommanded nb of channels is {max_recommanded_nb_channels} | ||||
|             Computation stopped.''') | ||||
|             logger.critical(msg) | ||||
|             exit()     | ||||
|             LOGGER.critical(msg) | ||||
|             raise ServiceError(msg) | ||||
|  | ||||
|  | ||||
| def disjunctions_from_json(json_data): | ||||
|     """ reads the disjunction requests from the json dict and create the list | ||||
|         of requested disjunctions for this set of requests | ||||
|     """ | ||||
|     disjunctions_list = [] | ||||
|  | ||||
|     try: | ||||
|         temp_test = json_data['synchronization'] | ||||
|     except KeyError: | ||||
|         temp_test = [] | ||||
|     if temp_test: | ||||
|         for snc in json_data['synchronization']: | ||||
|             params = {} | ||||
|             params['disjunction_id'] = snc['synchronization-id'] | ||||
|             params['relaxable'] = snc['svec']['relaxable'] | ||||
|         params['link_diverse'] = snc['svec']['link-diverse'] | ||||
|         params['node_diverse'] = snc['svec']['node-diverse'] | ||||
|             params['link_diverse'] = 'link' in snc['svec']['disjointness'] | ||||
|             params['node_diverse'] = 'node' in snc['svec']['disjointness'] | ||||
|             params['disjunctions_req'] = snc['svec']['request-id-number'] | ||||
|             disjunctions_list.append(Disjunction(**params)) | ||||
|  | ||||
|     return disjunctions_list | ||||
|  | ||||
|  | ||||
| def load_requests(filename,eqpt_filename): | ||||
| def load_requests(filename, eqpt_filename, bidir): | ||||
|     """ loads the requests from a json or an excel file into a data string | ||||
|     """ | ||||
|     if filename.suffix.lower() == '.xls': | ||||
|         logger.info('Automatically converting requests from XLS to JSON') | ||||
|         json_data = convert_service_sheet(filename,eqpt_filename) | ||||
|         LOGGER.info('Automatically converting requests from XLS to JSON') | ||||
|         try: | ||||
|             json_data = convert_service_sheet(filename, eqpt_filename, bidir=bidir) | ||||
|         except ServiceError as this_e: | ||||
|             print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||
|             exit(1) | ||||
|     else: | ||||
|         with open(filename, encoding='utf-8') as f: | ||||
|             json_data = loads(f.read()) | ||||
|         with open(filename, encoding='utf-8') as my_f: | ||||
|             json_data = loads(my_f.read()) | ||||
|     return json_data | ||||
|  | ||||
| def compute_path(network, equipment, pathreqlist): | ||||
|  | ||||
|     # This function is obsolete and not relevant with respect to network building: suggest either to correct | ||||
|     # or to suppress it | ||||
|      | ||||
|     path_res_list = [] | ||||
|  | ||||
|     for pathreq in pathreqlist: | ||||
|         #need to rebuid the network for each path because the total power | ||||
|         #can be different and the choice of amplifiers in autodesign is power dependant | ||||
|         #but the design is the same if the total power is the same | ||||
|         #TODO parametrize the total spectrum power so the same design can be shared | ||||
|         p_db = lin2db(pathreq.power*1e3) | ||||
|         p_total_db = p_db + lin2db(pathreq.nb_channel) | ||||
|         build_network(network, equipment, p_db, p_total_db) | ||||
|         pathreq.nodes_list.append(pathreq.destination) | ||||
|         #we assume that the destination is a strict constraint | ||||
|         pathreq.loose_list.append('strict') | ||||
|         print(f'Computing path from {pathreq.source} to {pathreq.destination}') | ||||
|         print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output | ||||
|         total_path = compute_constrained_path(network, pathreq) | ||||
|         print(f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}\n') | ||||
|  | ||||
|         if total_path : | ||||
|             total_path = propagate(total_path,pathreq,equipment, show=False) | ||||
|         else: | ||||
|             total_path = [] | ||||
|         # we record the last tranceiver object in order to have th whole | ||||
|         # information about spectrum. Important Note: since transceivers | ||||
|         # attached to roadms are actually logical elements to simulate | ||||
|         # performance, several demands having the same destination may use | ||||
|         # the same transponder for the performance simaulation. This is why | ||||
|         # we use deepcopy: to ensure each propagation is recorded and not | ||||
|         # overwritten | ||||
|  | ||||
|         path_res_list.append(deepcopy(total_path)) | ||||
|     return path_res_list | ||||
|  | ||||
| def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): | ||||
|      | ||||
|     # use a list but a dictionnary might be helpful to find path bathsed on request_id | ||||
|     # TODO change all these req, dsjct, res lists into dict ! | ||||
|     """ use a list but a dictionnary might be helpful to find path based on request_id | ||||
|         TODO change all these req, dsjct, res lists into dict ! | ||||
|     """ | ||||
|     path_res_list = [] | ||||
|     reversed_path_res_list = [] | ||||
|     propagated_reversed_path_res_list = [] | ||||
|  | ||||
|     for i,pathreq in enumerate(pathreqlist): | ||||
|     for i, pathreq in enumerate(pathreqlist): | ||||
|  | ||||
|         # use the power specified in requests but might be different from the one specified for design | ||||
|         # the power is an optional parameter for requests definition | ||||
|         # if optional, use the one defines in eqt_config.json | ||||
|         # use the power specified in requests but might be different from the one | ||||
|         # specified for design the power is an optional parameter for requests | ||||
|         # definition if optional, use the one defines in eqt_config.json | ||||
|         p_db = lin2db(pathreq.power*1e3) | ||||
|         p_total_db = p_db + lin2db(pathreq.nb_channel) | ||||
|         print(f'request {pathreq.request_id}') | ||||
|         print(f'Computing path from {pathreq.source} to {pathreq.destination}') | ||||
|         print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output | ||||
|         # adding first node to be clearer on the output | ||||
|         print(f'with path constraint: {[pathreq.source] + pathreq.nodes_list}') | ||||
|  | ||||
|         total_path = pathlist[i] | ||||
|         print(f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}\n') | ||||
|         # pathlist[i] contains the whole path information for request i | ||||
|         # last element is a transciver and where the result of the propagation is | ||||
|         # recorded. | ||||
|         # Important Note: since transceivers attached to roadms are actually logical | ||||
|         # elements to simulate performance, several demands having the same destination | ||||
|         # may use the same transponder for the performance simulation. This is why | ||||
|         # we use deepcopy: to ensure that each propagation is recorded and not overwritten | ||||
|         total_path = deepcopy(pathlist[i]) | ||||
|         print(f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}') | ||||
|         # for debug | ||||
|         # print(f'{pathreq.baud_rate}   {pathreq.power}   {pathreq.spacing}   {pathreq.nb_channel}') | ||||
|         if total_path : | ||||
|         if total_path: | ||||
|             if pathreq.baud_rate is not None: | ||||
|                 total_path = propagate(total_path,pathreq,equipment, show=False) | ||||
|                 temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))),2) | ||||
|                 if temp_snr01nm < pathreq.OSNR : | ||||
|                     msg = f'\tWarning! Request {pathreq.request_id} computed path from {pathreq.source} to {pathreq.destination} does not pass with {pathreq.tsp_mode}\n' +\ | ||||
|                     f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}\n' | ||||
|                 # means that at this point the mode was entered/forced by user and thus a | ||||
|                 # baud_rate was defined | ||||
|                 total_path = propagate(total_path, pathreq, equipment) | ||||
|                 temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2) | ||||
|                 if temp_snr01nm < pathreq.OSNR: | ||||
|                     msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\ | ||||
|                           f' {pathreq.source} to {pathreq.destination} does not pass with' +\ | ||||
|                           f' {pathreq.tsp_mode}\n\tcomputedSNR in 0.1nm = {temp_snr01nm} ' +\ | ||||
|                           f'- required osnr {pathreq.OSNR}' | ||||
|                     print(msg) | ||||
|                     logger.warning(msg) | ||||
|                     total_path = [] | ||||
|                     LOGGER.warning(msg) | ||||
|                     pathreq.blocking_reason = 'MODE_NOT_FEASIBLE' | ||||
|             else: | ||||
|                 total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment) | ||||
|                 # if no baudrate satisfies spacing, no mode is returned and an empty path is returned | ||||
|                 total_path, mode = propagate_and_optimize_mode(total_path, pathreq, equipment) | ||||
|                 # if no baudrate satisfies spacing, no mode is returned and the last explored mode | ||||
|                 # a warning is shown in the propagate_and_optimize_mode | ||||
|                 if mode is not None : | ||||
|                 # propagate_and_optimize_mode function returns the mode with the highest bitrate | ||||
|                     # that passes. if no mode passes, then it returns an empty path | ||||
|                 # that passes. if no mode passes, then a attribute blocking_reason is added on | ||||
|                 # pathreq that contains the reason for blocking: 'NO_PATH', 'NO_FEASIBLE_MODE', ... | ||||
|                 try: | ||||
|                     if pathreq.blocking_reason in BLOCKING_NOPATH: | ||||
|                         total_path = [] | ||||
|                     elif pathreq.blocking_reason in BLOCKING_NOMODE: | ||||
|                         pathreq.baud_rate = mode['baud_rate'] | ||||
|                         pathreq.tsp_mode = mode['format'] | ||||
|                         pathreq.format = mode['format'] | ||||
|                         pathreq.OSNR = mode['OSNR'] | ||||
|                         pathreq.tx_osnr = mode['tx_osnr'] | ||||
|                         pathreq.bit_rate = mode['bit_rate'] | ||||
|                     # other blocking reason should not appear at this point | ||||
|                 except AttributeError: | ||||
|                     pathreq.baud_rate = mode['baud_rate'] | ||||
|                     pathreq.tsp_mode = mode['format'] | ||||
|                     pathreq.format = mode['format'] | ||||
|                     pathreq.OSNR = mode['OSNR'] | ||||
|                     pathreq.tx_osnr = mode['tx_osnr'] | ||||
|                     pathreq.bit_rate = mode['bit_rate'] | ||||
|                 else : | ||||
|                     total_path = [] | ||||
|         # we record the last tranceiver object in order to have th whole  | ||||
|         # information about spectrum. Important Note: since transceivers  | ||||
|         # attached to roadms are actually logical elements to simulate | ||||
|         # performance, several demands having the same destination may use  | ||||
|         # the same transponder for the performance simaulation. This is why  | ||||
|         # we use deepcopy: to ensure each propagation is recorded and not  | ||||
|         # overwritten  | ||||
|  | ||||
|         path_res_list.append(deepcopy(total_path)) | ||||
|     return path_res_list | ||||
|             # reversed path is needed for correct spectrum assignment | ||||
|             reversed_path = find_reversed_path(pathlist[i]) | ||||
|             if pathreq.bidir: | ||||
|                 # only propagate if bidir is true, but needs the reversed path anyway for | ||||
|                 # correct spectrum assignment | ||||
|                 rev_p = deepcopy(reversed_path) | ||||
|  | ||||
|                 print(f'\n\tPropagating Z to A direction {pathreq.destination} to {pathreq.source}') | ||||
|                 print(f'\tPath (roadsm) {[r.uid for r in rev_p if isinstance(r,Roadm)]}\n') | ||||
|                 propagated_reversed_path = propagate(rev_p, pathreq, equipment) | ||||
|                 temp_snr01nm = round(mean(propagated_reversed_path[-1].snr +\ | ||||
|                                           lin2db(pathreq.baud_rate/(12.5e9))), 2) | ||||
|                 if temp_snr01nm < pathreq.OSNR: | ||||
|                     msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\ | ||||
|                           f' {pathreq.source} to {pathreq.destination} does not pass with' +\ | ||||
|                           f' {pathreq.tsp_mode}\n' +\ | ||||
|                           f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}' | ||||
|                     print(msg) | ||||
|                     LOGGER.warning(msg) | ||||
|                     # TODO selection of mode should also be on reversed direction !! | ||||
|                     pathreq.blocking_reason = 'MODE_NOT_FEASIBLE' | ||||
|             else: | ||||
|                 propagated_reversed_path = [] | ||||
|         else: | ||||
|             msg = 'Total path is empty. No propagation' | ||||
|             print(msg) | ||||
|             LOGGER.info(msg) | ||||
|             reversed_path = [] | ||||
|             propagated_reversed_path = [] | ||||
|  | ||||
|         path_res_list.append(total_path) | ||||
|         reversed_path_res_list.append(reversed_path) | ||||
|         propagated_reversed_path_res_list.append(propagated_reversed_path) | ||||
|         # print to have a nice output | ||||
|         print('') | ||||
|     return path_res_list, reversed_path_res_list, propagated_reversed_path_res_list | ||||
|  | ||||
| def correct_route_list(network, pathreqlist): | ||||
|     # prepares the format of route list of nodes to be consistant | ||||
|     # remove wrong names, remove endpoints | ||||
|     # also correct source and destination | ||||
|     anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver) and not isinstance(n, Fiber)] | ||||
|     # TODO there is a problem of identification of fibers in case of parallel fibers bitween two adjacent roadms | ||||
|     # so fiber constraint is not supported | ||||
|     """ prepares the format of route list of nodes to be consistant | ||||
|         remove wrong names, remove endpoints | ||||
|         also correct source and destination | ||||
|     """ | ||||
|     anytype = [n.uid for n in network.nodes()] | ||||
|     # TODO there is a problem of identification of fibers in case of parallel fibers | ||||
|     # between two adjacent roadms so fiber constraint is not supported | ||||
|     transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)] | ||||
|     for pathreq in pathreqlist: | ||||
|         for i,n_id in enumerate(pathreq.nodes_list): | ||||
|         for i, n_id in enumerate(pathreq.nodes_list): | ||||
|             # replace possibly wrong name with a formated roadm name | ||||
|             # print(n_id) | ||||
|             if n_id not in anytype : | ||||
|             if n_id not in anytype: | ||||
|                 # find nodes name that include constraint among all possible names except | ||||
|                 # transponders (not yet supported as constraints). | ||||
|                 nodes_suggestion = [uid for uid in anytype \ | ||||
|                     if n_id.lower() in uid.lower()] | ||||
|                 if pathreq.loose_list[i] == 'loose': | ||||
|                     if len(nodes_suggestion)>0 : | ||||
|                     if n_id.lower() in uid.lower() and uid not in transponders] | ||||
|                 if pathreq.loose_list[i] == 'LOOSE': | ||||
|                     if len(nodes_suggestion) > 0: | ||||
|                         new_n = nodes_suggestion[0] | ||||
|                         print(f'invalid route node specified:\ | ||||
|                         \n\'{n_id}\', replaced with \'{new_n}\'') | ||||
|                         pathreq.nodes_list[i] = new_n | ||||
|                     else: | ||||
|                         print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!'+'\x1b[0m') | ||||
|                         print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\',' +\ | ||||
|                               f' could not use it as constraint, skipped!'+'\x1b[0m') | ||||
|                         pathreq.nodes_list.remove(n_id) | ||||
|                         pathreq.loose_list.pop(i) | ||||
|                 else: | ||||
|                     msg = f'\x1b[1;33;40m'+f'could not find node : {n_id} in network topology. Strict constraint can not be applied.'+'\x1b[0m' | ||||
|                     logger.critical(msg) | ||||
|                     msg = f'\x1b[1;33;40m'+f'could not find node: {n_id} in network topology.' +\ | ||||
|                           f' Strict constraint can not be applied.' + '\x1b[0m' | ||||
|                     LOGGER.critical(msg) | ||||
|                     raise ValueError(msg) | ||||
|         if pathreq.source not in transponders: | ||||
|             msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.'+'\x1b[0m' | ||||
|             logger.critical(msg) | ||||
|             msg = f'\x1b[1;31;40m' + f'Request: {pathreq.request_id}: could not find' +\ | ||||
|                   f' transponder source: {pathreq.source}.'+'\x1b[0m' | ||||
|             LOGGER.critical(msg) | ||||
|             print(f'{msg}\nComputation stopped.') | ||||
|             exit() | ||||
|             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) | ||||
|             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.') | ||||
|             exit() | ||||
|             raise ServiceError(msg) | ||||
|  | ||||
|         # TODO remove endpoints from this list in case they were added by the user in the xls or json files | ||||
|         # TODO remove endpoints from this list in case they were added by the user | ||||
|         # in the xls or json files | ||||
|     return pathreqlist | ||||
|  | ||||
| def correct_disjn(disjn): | ||||
|     """ clean disjunctions to remove possible repetition | ||||
|     """ | ||||
|     local_disjn = disjn.copy() | ||||
|     for el in local_disjn: | ||||
|         for d in local_disjn: | ||||
|             if set(el.disjunctions_req) == set(d.disjunctions_req) and\ | ||||
|              el.disjunction_id != d.disjunction_id: | ||||
|                 local_disjn.remove(d) | ||||
|     for elem in local_disjn: | ||||
|         for dis_elem in local_disjn: | ||||
|             if set(elem.disjunctions_req) == set(dis_elem.disjunctions_req) and\ | ||||
|              elem.disjunction_id != dis_elem.disjunction_id: | ||||
|                 local_disjn.remove(dis_elem) | ||||
|     return local_disjn | ||||
|  | ||||
|  | ||||
| def path_result_json(pathresult): | ||||
|     """ create the response dictionnary | ||||
|     """ | ||||
|     data = { | ||||
|         'path': [n.json for n in pathresult] | ||||
|         'response': [n.json for n in pathresult] | ||||
|     } | ||||
|     return data | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     start = time.time() | ||||
|     args = parser.parse_args() | ||||
|     basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, DEBUG)) | ||||
|     logger.info(f'Computing path requests {args.service_filename} into JSON format') | ||||
|     print('\x1b[1;34;40m'+f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m') | ||||
|     # for debug | ||||
|     # print( args.eqpt_filename) | ||||
|     data = load_requests(args.service_filename,args.eqpt_filename) | ||||
|     equipment = load_equipment(args.eqpt_filename) | ||||
|     network = load_network(args.network_filename,equipment) | ||||
|  | ||||
| def compute_requests(network, data, equipment): | ||||
|     """ Main program calling functions | ||||
|     """ | ||||
|     # Build the network once using the default power defined in SI in eqpt config | ||||
|     # TODO power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||
|     # TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||
|     # spacing, f_min and f_max | ||||
|     p_db = equipment['SI']['default'].power_dbm | ||||
|  | ||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||
|     build_network(network, equipment, p_db, p_total_db) | ||||
|     save_network(args.network_filename, network) | ||||
|     save_network(ARGS.network_filename, network) | ||||
|  | ||||
|     oms_list = build_oms_list(network, equipment) | ||||
|  | ||||
|     try: | ||||
|         rqs = requests_from_json(data, equipment) | ||||
|  | ||||
|     except ServiceError as this_e: | ||||
|         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||
|         raise this_e | ||||
|     # check that request ids are unique. Non unique ids, may | ||||
|     # mess the computation : better to stop the computation | ||||
|     # mess the computation: better to stop the computation | ||||
|     all_ids = [r.request_id for r in rqs] | ||||
|     if len(all_ids) != len(set(all_ids)): | ||||
|         for a in list(set(all_ids)): | ||||
|             all_ids.remove(a) | ||||
|         for item in list(set(all_ids)): | ||||
|             all_ids.remove(item) | ||||
|         msg = f'Requests id {all_ids} are not unique' | ||||
|         logger.critical(msg) | ||||
|         exit() | ||||
|         LOGGER.critical(msg) | ||||
|         raise ServiceError(msg) | ||||
|     try: | ||||
|         rqs = correct_route_list(network, rqs) | ||||
|  | ||||
|     except ServiceError as this_e: | ||||
|         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||
|         raise this_e | ||||
|         #exit(1) | ||||
|     # pths = compute_path(network, equipment, rqs) | ||||
|     dsjn = disjunctions_from_json(data) | ||||
|  | ||||
|     print('\x1b[1;34;40m'+f'List of disjunctions'+ '\x1b[0m') | ||||
|     print('\x1b[1;34;40m' + f'List of disjunctions' + '\x1b[0m') | ||||
|     print(dsjn) | ||||
|     # need to warn or correct in case of wrong disjunction form | ||||
|     # disjunction must not be repeated with same or different ids | ||||
|     dsjn = correct_disjn(dsjn) | ||||
|  | ||||
|     # Aggregate demands with same exact constraints | ||||
|     print('\x1b[1;34;40m'+f'Aggregating similar requests'+ '\x1b[0m') | ||||
|     print('\x1b[1;34;40m' + f'Aggregating similar requests' + '\x1b[0m') | ||||
|  | ||||
|     rqs,dsjn = requests_aggregation(rqs,dsjn) | ||||
|     rqs, dsjn = requests_aggregation(rqs, dsjn) | ||||
|     # TODO export novel set of aggregated demands in a json file | ||||
|  | ||||
|     print('\x1b[1;34;40m'+'The following services have been requested:'+ '\x1b[0m') | ||||
|     print('\x1b[1;34;40m' + 'The following services have been requested:' + '\x1b[0m') | ||||
|     print(rqs) | ||||
|  | ||||
|     print('\x1b[1;34;40m'+f'Computing all paths with constraints'+ '\x1b[0m') | ||||
|     print('\x1b[1;34;40m' + f'Computing all paths with constraints' + '\x1b[0m') | ||||
|     try: | ||||
|         pths = compute_path_dsjctn(network, equipment, rqs, dsjn) | ||||
|     except DisjunctionError as this_e: | ||||
|         print(f'{ansi_escapes.red}Disjunction error:{ansi_escapes.reset} {this_e}') | ||||
|         raise this_e | ||||
|  | ||||
|     print('\x1b[1;34;40m'+f'Propagating on selected path'+ '\x1b[0m') | ||||
|     propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) | ||||
|     print('\x1b[1;34;40m' + f'Propagating on selected path' + '\x1b[0m') | ||||
|     propagatedpths, reversed_pths, reversed_propagatedpths = \ | ||||
|         compute_path_with_disjunction(network, equipment, rqs, pths) | ||||
|     # Note that deepcopy used in compute_path_with_disjunction returns | ||||
|     # a list of nodes which are not belonging to network (they are copies of the node objects). | ||||
|     # so there can not be propagation on these nodes. | ||||
|  | ||||
|     pth_assign_spectrum(pths, rqs, oms_list, reversed_pths) | ||||
|  | ||||
|     end = time.time() | ||||
|     print(f'computation time {end-start}') | ||||
|     print('\x1b[1;34;40m'+f'Result summary'+ '\x1b[0m') | ||||
|      | ||||
|     header = ['req id', '  demand','  snr@bandwidth','  snr@0.1nm','  Receiver minOSNR', '  mode', '  Gbit/s' , '  nb of tsp pairs'] | ||||
|     header = ['req id', '  demand', '  snr@bandwidth A-Z (Z-A)', '  snr@0.1nm A-Z (Z-A)',\ | ||||
|               '  Receiver minOSNR', '  mode', '  Gbit/s', '  nb of tsp pairs',\ | ||||
|               'N,M or blocking reason'] | ||||
|     data = [] | ||||
|     data.append(header) | ||||
|     for i, p in enumerate(propagatedpths): | ||||
|         if p: | ||||
|             line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ | ||||
|                 f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ | ||||
|                 f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}' , f'{round(rqs[i].path_bandwidth * 1e-9,2)}' , f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }'] | ||||
|     for i, this_p in enumerate(propagatedpths): | ||||
|         rev_pth = reversed_propagatedpths[i] | ||||
|         if rev_pth and this_p: | ||||
|             psnrb = f'{round(mean(this_p[-1].snr),2)} ({round(mean(rev_pth[-1].snr),2)})' | ||||
|             psnr = f'{round(mean(this_p[-1].snr_01nm), 2)}' +\ | ||||
|                    f' ({round(mean(rev_pth[-1].snr_01nm),2)})' | ||||
|         elif this_p: | ||||
|             psnrb = f'{round(mean(this_p[-1].snr),2)}' | ||||
|             psnr = f'{round(mean(this_p[-1].snr_01nm),2)}' | ||||
|  | ||||
|         try : | ||||
|             if rqs[i].blocking_reason in  BLOCKING_NOPATH: | ||||
|                 line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} :',\ | ||||
|                         f'-', f'-', f'-', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9,2)}',\ | ||||
|                         f'-', f'{rqs[i].blocking_reason}'] | ||||
|             else: | ||||
|             line = [f'{rqs[i].request_id}',f' {rqs[i].source} to {rqs[i].destination} : not feasible '] | ||||
|                 line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', psnrb,\ | ||||
|                         psnr, f'-', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9, 2)}',\ | ||||
|                         f'-', f'{rqs[i].blocking_reason}'] | ||||
|         except AttributeError: | ||||
|             line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', psnrb,\ | ||||
|                     psnr, f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9,2)}',\ | ||||
|                     f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }', f'({rqs[i].N},{rqs[i].M})'] | ||||
|         data.append(line) | ||||
|  | ||||
|     col_width = max(len(word) for row in data for word in row[2:])   # padding | ||||
|     firstcol_width = max(len(row[0]) for row in data )   # padding | ||||
|     secondcol_width = max(len(row[1]) for row in data )   # padding | ||||
|     firstcol_width = max(len(row[0]) for row in data)   # padding | ||||
|     secondcol_width = max(len(row[1]) for row in data)   # padding | ||||
|     for row in data: | ||||
|         firstcol = ''.join(row[0].ljust(firstcol_width)) | ||||
|         secondcol = ''.join(row[1].ljust(secondcol_width)) | ||||
|         remainingcols = ''.join(word.center(col_width,' ') for word in row[2:]) | ||||
|         remainingcols = ''.join(word.center(col_width, ' ') for word in row[2:]) | ||||
|         print(f'{firstcol} {secondcol} {remainingcols}') | ||||
|     print('\x1b[1;33;40m'+f'Result summary shows mean SNR and OSNR (average over all channels)' +\ | ||||
|           '\x1b[0m') | ||||
|  | ||||
|     return propagatedpths, reversed_propagatedpths, rqs | ||||
|  | ||||
|  | ||||
|     if args.output : | ||||
| def launch_cli(network, data, equipment): | ||||
|     """ Compute requests using network, data and equipment with client line interface | ||||
|     """ | ||||
|     propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment) | ||||
|     #Generate the output | ||||
|     if ARGS.output : | ||||
|         result = [] | ||||
|         # assumes that list of rqs and list of propgatedpths have same order | ||||
|         for i,p in enumerate(propagatedpths): | ||||
|             result.append(Result_element(rqs[i],p)) | ||||
|         for i, pth in enumerate(propagatedpths): | ||||
|             result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) | ||||
|         temp = path_result_json(result) | ||||
|         fnamecsv = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.csv' | ||||
|         fnamejson = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.json' | ||||
|         with open(fnamejson, 'w', encoding='utf-8') as f: | ||||
|             f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) | ||||
|             with open(fnamecsv,"w", encoding='utf-8') as fcsv : | ||||
|                 jsontocsv(temp,equipment,fcsv) | ||||
|                 print('\x1b[1;34;40m'+f'saving in {args.output} and {fnamecsv}'+ '\x1b[0m') | ||||
|         fnamecsv = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.csv' | ||||
|         fnamejson = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.json' | ||||
|         with open(fnamejson, 'w', encoding='utf-8') as fjson: | ||||
|             fjson.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) | ||||
|             with open(fnamecsv, "w", encoding='utf-8') as fcsv: | ||||
|                 jsontocsv(temp, equipment, fcsv) | ||||
|                 print('\x1b[1;34;40m'+f'saving in {ARGS.output} and {fnamecsv}'+ '\x1b[0m') | ||||
|  | ||||
| class GnpyAPI(Resource): | ||||
|     """ Compute requests using network, data and equipment with rest api | ||||
|     """ | ||||
|     def get(self): | ||||
|         return {"ping": True}, 200 | ||||
|  | ||||
|     def post(self): | ||||
|         data = request.get_json() | ||||
|         equipment = load_equipment('examples/2019-demo-equipment.json') | ||||
|         topo_json = load_json('examples/2019-demo-topology.json') | ||||
|         network = network_from_json(topo_json, equipment) | ||||
|         try: | ||||
|             propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment) | ||||
|             # Generate the output | ||||
|             result = [] | ||||
|             #assumes that list of rqs and list of propgatedpths have same order | ||||
|             for i, pth in enumerate(propagatedpths): | ||||
|                 result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) | ||||
|  | ||||
|             return {"result":path_result_json(result)}, 201 | ||||
|         except ServiceError as this_e: | ||||
|             msg = f'Service error: {this_e}' | ||||
|             return {"result": msg}, 400 | ||||
|  | ||||
| API.add_resource(GnpyAPI, '/gnpy-experimental') | ||||
|  | ||||
| def main(args): | ||||
|     """ main function that calls all functions | ||||
|     """ | ||||
|     LOGGER.info(f'Computing path requests {args.service_filename} into JSON format') | ||||
|     print('\x1b[1;34;40m' +\ | ||||
|           f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m') | ||||
|     # for debug | ||||
|     # print( args.eqpt_filename) | ||||
|  | ||||
|     try: | ||||
|         data = load_requests(args.service_filename, args.eqpt_filename, args.bidir) | ||||
|         equipment = load_equipment(args.eqpt_filename) | ||||
|         network = load_network(args.network_filename, equipment) | ||||
|     except EquipmentConfigError as this_e: | ||||
|         print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {this_e}') | ||||
|         exit(1) | ||||
|     except NetworkTopologyError as this_e: | ||||
|         print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {this_e}') | ||||
|         exit(1) | ||||
|     except ConfigurationError as this_e: | ||||
|         print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {this_e}') | ||||
|         exit(1) | ||||
|     except ServiceError as this_e: | ||||
|         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||
|         exit(1) | ||||
|     # input_str = raw_input("How will you use your program: c:[cli] , a:[api] ?") | ||||
|     # print(input_str) | ||||
|     # | ||||
|     if ((args.rest == 1) and (args.output is None)): | ||||
|         print('you have chosen the rest mode') | ||||
|         APP.run(host='0.0.0.0', port=5000, debug=True) | ||||
|     elif ((args.rest > 1) or ((args.rest == 1) and (args.output is not None))): | ||||
|         print('command is not well formulated') | ||||
|     else: | ||||
|         launch_cli(network, data, equipment) | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     ARGS = PARSER.parse_args() | ||||
|     basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(ARGS.verbose, DEBUG)) | ||||
|     main(ARGS) | ||||
|   | ||||
							
								
								
									
										98
									
								
								examples/raman_edfa_example_network.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								examples/raman_edfa_example_network.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| { | ||||
|   "elements": [ | ||||
|     { | ||||
|       "uid": "Site_A", | ||||
|       "type": "Transceiver", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0, | ||||
|           "longitude": 0, | ||||
|           "city": "Site A", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Span1", | ||||
|       "type": "RamanFiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "operational": { | ||||
|         "temperature": 283, | ||||
|         "raman_pumps": [ | ||||
|           { | ||||
|             "power": 200e-3, | ||||
|             "frequency": 205e12, | ||||
|             "propagation_direction": "counterprop" | ||||
|           }, | ||||
|           { | ||||
|             "power": 206e-3, | ||||
|             "frequency": 201e12, | ||||
|             "propagation_direction": "counterprop" | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 80.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Edfa1", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 15.0, | ||||
|         "delta_p": -2, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Site_B", | ||||
|       "type": "Transceiver", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2, | ||||
|           "longitude": 0, | ||||
|           "city": "Site B", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "connections": [ | ||||
|     { | ||||
|       "from_node": "Site_A", | ||||
|       "to_node": "Span1" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Span1", | ||||
|       "to_node": "Edfa1" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Edfa1", | ||||
|       "to_node": "Site_B" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										180
									
								
								examples/serviceDemov1.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								examples/serviceDemov1.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | ||||
| { | ||||
|   "path-request": [ | ||||
|     { | ||||
|       "request-id": "0", | ||||
|       "source": "trx site_a", | ||||
|       "destination": "trx site_b", | ||||
|       "src-tp-id": "trx site_a", | ||||
|       "dst-tp-id": "trx site_b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager", | ||||
|           "trx_mode": null, | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 100000000000.0 | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "1", | ||||
|       "source": "trx site_a", | ||||
|       "destination": "trx site_b", | ||||
|       "src-tp-id": "trx site_a", | ||||
|       "dst-tp-id": "trx site_b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager", | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 200000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 0, | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "Span1ab", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "STRICT" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "2", | ||||
|       "source": "trx site_a", | ||||
|       "destination": "trx site_b", | ||||
|       "src-tp-id": "trx site_a", | ||||
|       "dst-tp-id": "trx site_b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager", | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 200000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 0, | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm site_c", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "STRICT" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "3", | ||||
|       "source": "trx site_a", | ||||
|       "destination": "trx site_b", | ||||
|       "src-tp-id": "trx site_a", | ||||
|       "dst-tp-id": "trx site_b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager", | ||||
|           "trx_mode": null, | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 100000000000.0 | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "4", | ||||
|       "source": "trx site_a", | ||||
|       "destination": "trx site_b", | ||||
|       "src-tp-id": "trx site_a", | ||||
|       "dst-tp-id": "trx site_b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager", | ||||
|           "trx_mode": null, | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 100000000000.0 | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "synchronization": [ | ||||
|     { | ||||
|       "synchronization-id": "x", | ||||
|       "svec": { | ||||
|         "relaxable": "false", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "3", | ||||
|           "0" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "synchronization-id": "y", | ||||
|       "svec": { | ||||
|         "relaxable": "false", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "4", | ||||
|           "3", | ||||
|           "0" | ||||
|         ] | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										14
									
								
								examples/sim_params.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								examples/sim_params.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| { | ||||
|   "raman_computed_channels": [1, 18, 37, 56, 75], | ||||
|   "raman_parameters": { | ||||
|     "flag_raman": true, | ||||
|     "space_resolution": 10e3, | ||||
|     "tolerance": 1e-8 | ||||
|   }, | ||||
|   "nli_parameters": { | ||||
|   	"nli_method_name": "ggn_spectrally_separated", | ||||
|   	"wdm_grid_size": 50e9, | ||||
|   	"dispersion_tolerance": 1, | ||||
|   	"phase_shift_tollerance": 0.1 | ||||
|   } | ||||
| } | ||||
| @@ -4,6 +4,8 @@ | ||||
|       0.0359549, | ||||
|       5.82851 | ||||
|       ], | ||||
|       "f_min": 191.35e12, | ||||
|       "f_max": 196.1e12, | ||||
|       "nf_ripple": [ | ||||
|       -0.3110761646066259, | ||||
|       -0.3110761646066259, | ||||
|   | ||||
							
								
								
									
										703
									
								
								examples/topoDemov1.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										703
									
								
								examples/topoDemov1.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,703 @@ | ||||
| { | ||||
|   "elements": [ | ||||
|     { | ||||
|       "uid": "trx site_a", | ||||
|       "type": "Transceiver", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0, | ||||
|           "longitude": 0, | ||||
|           "city": "Site a", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm site_a", | ||||
|       "type": "Roadm", | ||||
|       "params": { | ||||
|         "target_pch_out_db": -20, | ||||
|         "restrictions": { | ||||
|           "preamp_variety_list": [], | ||||
|           "booster_variety_list": [] | ||||
|         } | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0, | ||||
|           "longitude": 0, | ||||
|           "city": "Site a", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Span1ab", | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 100.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Span1ba", | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 100.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Span2ab", | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 80.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Span2ba", | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 80.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm site_b", | ||||
|       "type": "Roadm", | ||||
|       "params": { | ||||
|         "target_pch_out_db": -20, | ||||
|         "restrictions": { | ||||
|           "preamp_variety_list": [], | ||||
|           "booster_variety_list": [] | ||||
|         } | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0, | ||||
|           "longitude": 0, | ||||
|           "city": "Site b", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx site_b", | ||||
|       "type": "Transceiver", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2, | ||||
|           "longitude": 0, | ||||
|           "city": "Site b", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "booster1 site_a", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_medium_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 19.0, | ||||
|         "delta_p": -1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site a", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "preamp site_b", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 18.0, | ||||
|         "delta_p": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site b", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "booster1 site_b", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_medium_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 19.0, | ||||
|         "delta_p": -1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site b", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "preamp1 site_a", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 18.0, | ||||
|         "delta_p": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site_a", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "booster2 site_a", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_medium_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 19.0, | ||||
|         "delta_p": -1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site a", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "preamp2 site_b", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 18.0, | ||||
|         "delta_p": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site_b", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "booster2 site_b", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_medium_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 19.0, | ||||
|         "delta_p": -1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site b", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "preamp2 site_a", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 18.0, | ||||
|         "delta_p": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site_a", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "booster3 site_a", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_medium_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 19.0, | ||||
|         "delta_p": -1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site a", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "preamp3 site_b", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 18.0, | ||||
|         "delta_p": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site_b", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "booster3 site_b", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_medium_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 19.0, | ||||
|         "delta_p": -1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site b", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "preamp3 site_a", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 18.0, | ||||
|         "delta_p": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site_a", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm site_c", | ||||
|       "type": "Roadm", | ||||
|       "params": { | ||||
|         "target_pch_out_db": -20, | ||||
|         "restrictions": { | ||||
|           "preamp_variety_list": [], | ||||
|           "booster_variety_list": [] | ||||
|         } | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0, | ||||
|           "longitude": 0, | ||||
|           "city": "Site c", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "booster1 site_c", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_medium_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 19.0, | ||||
|         "delta_p": -1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site c", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "preamp1 site_c", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 18.0, | ||||
|         "delta_p": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site_c", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "booster2 site_c", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_medium_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 19.0, | ||||
|         "delta_p": -1.0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site c", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "preamp2 site_c", | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "std_low_gain", | ||||
|       "operational": { | ||||
|         "gain_target": 18.0, | ||||
|         "delta_p": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0, | ||||
|           "city": "Site_c", | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Span1ac", | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 80.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Span1ca", | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 80.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Span1bc", | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 80.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "Span1cb", | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 80.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "connections": [ | ||||
|     { | ||||
|       "from_node": "trx site_a", | ||||
|       "to_node": "roadm site_a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm site_a", | ||||
|       "to_node": "booster1 site_a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "booster1 site_a", | ||||
|       "to_node": "Span1ab" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Span1ab", | ||||
|       "to_node": "preamp site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "preamp site_b", | ||||
|       "to_node": "roadm site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm site_b", | ||||
|       "to_node": "trx site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm site_a", | ||||
|       "to_node": "booster2 site_a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "booster2 site_a", | ||||
|       "to_node": "Span2ab" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Span2ab", | ||||
|       "to_node": "preamp2 site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "preamp2 site_b", | ||||
|       "to_node": "roadm site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm site_b", | ||||
|       "to_node": "booster1 site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "booster1 site_b", | ||||
|       "to_node": "Span1ba" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Span1ba", | ||||
|       "to_node": "preamp1 site_a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "preamp1 site_a", | ||||
|       "to_node": "roadm site_a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm site_b", | ||||
|       "to_node": "booster2 site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "booster2 site_b", | ||||
|       "to_node": "Span2ba" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Span2ba", | ||||
|       "to_node": "preamp2 site_a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "preamp2 site_a", | ||||
|       "to_node": "roadm site_a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm site_a", | ||||
|       "to_node": "booster3 site_a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "booster3 site_a", | ||||
|       "to_node": "Span1ac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Span1ac", | ||||
|       "to_node": "preamp1 site_c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "preamp1 site_c", | ||||
|       "to_node": "roadm site_c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm site_c", | ||||
|       "to_node": "booster1 site_c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "booster1 site_c", | ||||
|       "to_node": "Span1cb" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Span1cb", | ||||
|       "to_node": "preamp3 site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "preamp3 site_b", | ||||
|       "to_node": "roadm site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm site_b", | ||||
|       "to_node": "booster3 site_b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "booster3 site_b", | ||||
|       "to_node": "Span1bc" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Span1bc", | ||||
|       "to_node": "preamp2 site_c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "preamp2 site_c", | ||||
|       "to_node": "roadm site_c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm site_c", | ||||
|       "to_node": "booster2 site_c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "booster2 site_c", | ||||
|       "to_node": "Span1ca" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Span1ca", | ||||
|       "to_node": "preamp3 site_a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "preamp3 site_a", | ||||
|       "to_node": "roadm site_a" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -18,14 +18,16 @@ from pathlib import Path | ||||
| from json import loads | ||||
| from collections import Counter | ||||
| from logging import getLogger, basicConfig, INFO, ERROR, DEBUG | ||||
| from numpy import linspace, mean | ||||
| from numpy import linspace, mean, log10 | ||||
| from matplotlib.pyplot import show, axis, figure, title, text | ||||
| from networkx import (draw_networkx_nodes, draw_networkx_edges, | ||||
|                       draw_networkx_labels, dijkstra_path) | ||||
| from gnpy.core.network import load_network, build_network, save_network | ||||
| from gnpy.core.elements import Transceiver, Fiber, Edfa, Roadm | ||||
| from gnpy.core.network import load_network, build_network, save_network, load_sim_params, configure_network | ||||
| from gnpy.core.elements import Transceiver, Fiber, RamanFiber, Edfa, Roadm | ||||
| from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref | ||||
| from gnpy.core.request import Path_request, RequestParams, compute_constrained_path, propagate2 | ||||
| from gnpy.core.exceptions import ConfigurationError, EquipmentConfigError, NetworkTopologyError | ||||
| import gnpy.core.ansi_escapes as ansi_escapes | ||||
|  | ||||
| logger = getLogger(__name__) | ||||
|  | ||||
| @@ -97,7 +99,7 @@ def plot_results(network, path, source, destination, infos): | ||||
|     show() | ||||
|  | ||||
|  | ||||
| def main(network, equipment, source, destination, req = None): | ||||
| def main(network, equipment, source, destination, sim_params, req=None): | ||||
|     result_dicts = {} | ||||
|     network_data = [{ | ||||
|                     'network_name'  : str(args.filename), | ||||
| @@ -106,8 +108,8 @@ def main(network, equipment, source, destination, req = None): | ||||
|                     }] | ||||
|     result_dicts.update({'network': network_data}) | ||||
|     design_data = [{ | ||||
|                     'power_mode'        : equipment['Spans']['default'].power_mode, | ||||
|                     'span_power_range'  : equipment['Spans']['default'].delta_power_range_db, | ||||
|                     'power_mode'        : equipment['Span']['default'].power_mode, | ||||
|                     'span_power_range'  : equipment['Span']['default'].delta_power_range_db, | ||||
|                     'design_pch'        : equipment['SI']['default'].power_dbm, | ||||
|                     'baud_rate'         : equipment['SI']['default'].baud_rate | ||||
|                     }] | ||||
| @@ -115,17 +117,23 @@ def main(network, equipment, source, destination, req = None): | ||||
|     simulation_data = [] | ||||
|     result_dicts.update({'simulation results': simulation_data}) | ||||
|  | ||||
|     power_mode = equipment['Spans']['default'].power_mode | ||||
|     power_mode = equipment['Span']['default'].power_mode | ||||
|     print('\n'.join([f'Power mode is set to {power_mode}', | ||||
|                      f'=> it can be modified in eqpt_config.json - Spans'])) | ||||
|                      f'=> it can be modified in eqpt_config.json - Span'])) | ||||
|  | ||||
|     pref_ch_db = lin2db(req.power*1e3) #reference channel power / span (SL=20dB) | ||||
|     pref_total_db = pref_ch_db + lin2db(req.nb_channel) #reference total power / span (SL=20dB) | ||||
|     build_network(network, equipment, pref_ch_db, pref_total_db) | ||||
|     path = compute_constrained_path(network, req) | ||||
|  | ||||
|     spans = [s.length for s in path if isinstance(s, Fiber)] | ||||
|     print(f'\nThere are {len(spans)} fiber spans over {sum(spans):.0f}m between {source.uid} and {destination.uid}') | ||||
|     if len([s.length for s in path if isinstance(s, RamanFiber)]): | ||||
|         if sim_params is None: | ||||
|             print(f'{ansi_escapes.red}Invocation error:{ansi_escapes.reset} RamanFiber requires passing simulation params via --sim-params') | ||||
|             exit(1) | ||||
|         configure_network(network, sim_params) | ||||
|  | ||||
|     spans = [s.length for s in path if isinstance(s, RamanFiber) or isinstance(s, Fiber)] | ||||
|     print(f'\nThere are {len(spans)} fiber spans over {sum(spans)/1000:.0f} km between {source.uid} and {destination.uid}') | ||||
|     print(f'\nNow propagating between {source.uid} and {destination.uid}:') | ||||
|  | ||||
|     try: | ||||
| @@ -136,18 +144,32 @@ def main(network, equipment, source, destination, req = None): | ||||
|         print('invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]') | ||||
|         power_range = [0] | ||||
|  | ||||
|     if not power_mode: | ||||
|         #power cannot be changed in gain mode | ||||
|         power_range = [0] | ||||
|     for dp_db in power_range: | ||||
|         req.power = db2lin(pref_ch_db + dp_db)*1e-3 | ||||
|         print(f'\nPropagating with input power = {lin2db(req.power*1e3):.2f}dBm :') | ||||
|         infos = propagate2(path, req, equipment, show=len(power_range)==1) | ||||
|         print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :') | ||||
|         print(destination) | ||||
|         if power_mode: | ||||
|             print(f'\nPropagating with input power = {ansi_escapes.cyan}{lin2db(req.power*1e3):.2f} dBm{ansi_escapes.reset}:') | ||||
|         else: | ||||
|             print(f'\nPropagating in {ansi_escapes.cyan}gain mode{ansi_escapes.reset}: power cannot be set manually') | ||||
|         infos = propagate2(path, req, equipment) | ||||
|         if len(power_range) == 1: | ||||
|             for elem in path: | ||||
|                 print(elem) | ||||
|             if power_mode: | ||||
|                 print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:') | ||||
|             else: | ||||
|                 print(f'\nTransmission results:') | ||||
|             print(f'  Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}') | ||||
|         else: | ||||
|             print(path[-1]) | ||||
|  | ||||
|         #print(f'\n !!!!!!!!!!!!!!!!!     TEST POINT         !!!!!!!!!!!!!!!!!!!!!') | ||||
|         #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}') | ||||
|         # => use "in" or "out" parameter | ||||
|         # => use "nli" or "ase" or "signal" or "total" parameter         | ||||
|  | ||||
|         if power_mode: | ||||
|             simulation_data.append({ | ||||
|                         'Pch_dBm'               : pref_ch_db + dp_db, | ||||
|                         'OSNR_ASE_0.1nm'        : round(mean(destination.osnr_ase_01nm),2), | ||||
| @@ -155,6 +177,14 @@ def main(network, equipment, source, destination, req = None): | ||||
|                         'SNR_nli_signal_bw'     : round(mean(destination.osnr_nli),2), | ||||
|                         'SNR_total_signal_bw'   : round(mean(destination.snr),2) | ||||
|                                 }) | ||||
|         else: | ||||
|             simulation_data.append({ | ||||
|                         'gain_mode'             : 'power canot be set', | ||||
|                         'OSNR_ASE_0.1nm'        : round(mean(destination.osnr_ase_01nm),2), | ||||
|                         'OSNR_ASE_signal_bw'    : round(mean(destination.osnr_ase),2), | ||||
|                         'SNR_nli_signal_bw'     : round(mean(destination.osnr_nli),2), | ||||
|                         'SNR_total_signal_bw'   : round(mean(destination.snr),2) | ||||
|                                 }) | ||||
|     write_csv(result_dicts, 'simulation_result.csv') | ||||
|     return path, infos | ||||
|  | ||||
| @@ -162,6 +192,9 @@ def main(network, equipment, source, destination, req = None): | ||||
| parser = ArgumentParser() | ||||
| parser.add_argument('-e', '--equipment', type=Path, | ||||
|                     default=Path(__file__).parent / 'eqpt_config.json') | ||||
| parser.add_argument('--sim-params', type=Path, | ||||
|                     default=None, help='Path to the JSON containing simulation parameters (required for Raman)') | ||||
| parser.add_argument('--show-channels', action='store_true', help='Show final per-channel OSNR summary') | ||||
| parser.add_argument('-pl', '--plot', action='store_true') | ||||
| parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') | ||||
| parser.add_argument('-l', '--list-nodes', action='store_true', help='list all transceiver nodes') | ||||
| @@ -177,8 +210,19 @@ if __name__ == '__main__': | ||||
|     args = parser.parse_args() | ||||
|     basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, DEBUG)) | ||||
|  | ||||
|     try: | ||||
|         equipment = load_equipment(args.equipment) | ||||
|         network = load_network(args.filename, equipment, args.names_matching) | ||||
|         sim_params = load_sim_params(args.sim_params) if args.sim_params is not None else None | ||||
|     except EquipmentConfigError as e: | ||||
|         print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {e}') | ||||
|         exit(1) | ||||
|     except NetworkTopologyError as e: | ||||
|         print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}') | ||||
|         exit(1) | ||||
|     except ConfigurationError as e: | ||||
|         print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}') | ||||
|         exit(1) | ||||
|  | ||||
|     if args.plot: | ||||
|         plot_baseline(network) | ||||
| @@ -238,6 +282,7 @@ if __name__ == '__main__': | ||||
|     params['trx_mode'] = '' | ||||
|     params['source'] = source.uid | ||||
|     params['destination'] = destination.uid | ||||
|     params['bidir'] = False | ||||
|     params['nodes_list'] = [destination.uid] | ||||
|     params['loose_list'] = ['strict'] | ||||
|     params['format'] = '' | ||||
| @@ -247,9 +292,19 @@ if __name__ == '__main__': | ||||
|         trx_params['power'] = db2lin(float(args.power))*1e-3 | ||||
|     params.update(trx_params) | ||||
|     req = Path_request(**params) | ||||
|     path, infos = main(network, equipment, source, destination, req) | ||||
|     path, infos = main(network, equipment, source, destination, sim_params, req) | ||||
|     save_network(args.filename, network) | ||||
|  | ||||
|     if args.show_channels: | ||||
|         print('\nThe total SNR per channel at the end of the line is:') | ||||
|         print('{:>5}{:>26}{:>26}{:>28}{:>28}{:>28}' \ | ||||
|             .format('Ch. #', 'Channel frequency (THz)', 'Channel power (dBm)', 'OSNR ASE (signal bw, dB)', 'SNR NLI (signal bw, dB)', 'SNR total (signal bw, dB)')) | ||||
|         for final_carrier, ch_osnr, ch_snr_nl, ch_snr in zip(infos[path[-1]][1].carriers, path[-1].osnr_ase, path[-1].osnr_nli, path[-1].snr): | ||||
|             ch_freq = final_carrier.frequency * 1e-12 | ||||
|             ch_power = lin2db(final_carrier.power.signal*1e3) | ||||
|             print('{:5}{:26.2f}{:26.2f}{:28.2f}{:28.2f}{:28.2f}' \ | ||||
|                 .format(final_carrier.channel_number, round(ch_freq, 2), round(ch_power, 2), round(ch_osnr, 2), round(ch_snr_nl, 2), round(ch_snr, 2))) | ||||
|  | ||||
|     if not args.source: | ||||
|         print(f'\n(No source node specified: picked {source.uid})') | ||||
|     elif not valid_source: | ||||
|   | ||||
							
								
								
									
										13
									
								
								gnpy/core/ansi_escapes.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								gnpy/core/ansi_escapes.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| ''' | ||||
| gnpy.core.ansi_escapes | ||||
| ====================== | ||||
|  | ||||
| A random subset of ANSI terminal escape codes for colored messages | ||||
| ''' | ||||
|  | ||||
| red = '\x1b[1;31;40m' | ||||
| cyan = '\x1b[1;36;40m' | ||||
| reset = '\x1b[0m' | ||||
| @@ -31,6 +31,8 @@ from itertools import chain | ||||
| from json import dumps | ||||
| from pathlib import Path | ||||
| from difflib import get_close_matches | ||||
| from gnpy.core.utils import silent_remove | ||||
| from gnpy.core.exceptions import NetworkTopologyError | ||||
| import time | ||||
|  | ||||
| all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) | ||||
| @@ -54,7 +56,9 @@ class Node(object): | ||||
|         'region':       '', | ||||
|         'latitude':     0, | ||||
|         'longitude':    0, | ||||
|         'node_type':    'ILA' | ||||
|         'node_type':    'ILA', | ||||
|         'booster_restriction' : '', | ||||
|         'preamp_restriction'  : '' | ||||
|     } | ||||
|  | ||||
| class Link(object): | ||||
| @@ -113,9 +117,10 @@ class Eqpt(object): | ||||
|             'to_city':          '', | ||||
|             'east_amp_type':    '', | ||||
|             'east_att_in':      0, | ||||
|             'east_amp_gain':         0, | ||||
|             'east_amp_gain':    None, | ||||
|             'east_amp_dp':      None, | ||||
|             'east_tilt':        0, | ||||
|             'east_att_out':          0 | ||||
|             'east_att_out':     None | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -163,8 +168,8 @@ def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in): | ||||
|             slice_out = read_slice(my_sheet, start_line+iteration, slice_in, h0) | ||||
|             iteration += 1 | ||||
|         if slice_out == (-1, -1): | ||||
|             if h0 == 'east': | ||||
|                 print(f'\x1b[1;31;40m'+f'CRITICAL: missing _east_ header above other headers (hierarchical) _ ABORT'+ '\x1b[0m') | ||||
|             if h0 in ('east', 'Node A', 'Node Z', 'City') : | ||||
|                 print(f'\x1b[1;31;40m'+f'CRITICAL: missing _{h0}_ header: EXECUTION ENDS'+ '\x1b[0m') | ||||
|                 exit() | ||||
|             else: | ||||
|                 print(f'missing header {h0}') | ||||
| @@ -234,7 +239,6 @@ def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city): | ||||
|  | ||||
| def convert_file(input_filename, names_matching=False, filter_region=[]): | ||||
|     nodes, links, eqpts = parse_excel(input_filename) | ||||
|  | ||||
|     if filter_region: | ||||
|         nodes = [n for n in nodes if n.region.lower() in filter_region] | ||||
|         cities = {n.city for n in nodes} | ||||
| @@ -243,10 +247,8 @@ def convert_file(input_filename, names_matching=False, filter_region=[]): | ||||
|         cities = {lnk.from_city for lnk in links} | {lnk.to_city for lnk in links} | ||||
|         nodes = [n for n in nodes if n.city in cities] | ||||
|  | ||||
|  | ||||
|     global nodes_by_city | ||||
|     nodes_by_city = {n.city: n for n in nodes} | ||||
|  | ||||
|     #create matching dictionary for node name mismatch analysis | ||||
|  | ||||
|     cities = {''.join(c.strip() for c in n.city.split('C+L')).lower(): n.city for n in nodes} | ||||
| @@ -297,7 +299,22 @@ def convert_file(input_filename, names_matching=False, filter_region=[]): | ||||
|                                         'latitude':  x.latitude, | ||||
|                                         'longitude': x.longitude}}, | ||||
|               'type': 'Roadm'} | ||||
|              for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] + | ||||
|              for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' \ | ||||
|                  and x.booster_restriction == '' and x.preamp_restriction == ''] + | ||||
|             [{'uid': f'roadm {x.city}', | ||||
|               'params' : { | ||||
|                 'restrictions': { | ||||
|                   'preamp_variety_list': silent_remove(x.preamp_restriction.split(' | '),''), | ||||
|                   'booster_variety_list': silent_remove(x.booster_restriction.split(' | '),'') | ||||
|                   } | ||||
|               }, | ||||
|               'metadata': {'location': {'city':      x.city, | ||||
|                                         'region':    x.region, | ||||
|                                         'latitude':  x.latitude, | ||||
|                                         'longitude': x.longitude}}, | ||||
|               'type': 'Roadm'} | ||||
|              for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' and \ | ||||
|                  (x.booster_restriction != '' or x.preamp_restriction != '')] + | ||||
|             [{'uid': f'west fused spans in {x.city}', | ||||
|               'metadata': {'location': {'city':      x.city, | ||||
|                                         'region':    x.region, | ||||
| @@ -344,10 +361,12 @@ def convert_file(input_filename, names_matching=False, filter_region=[]): | ||||
|               'type': 'Edfa', | ||||
|               'type_variety': e.east_amp_type, | ||||
|               'operational': {'gain_target': e.east_amp_gain, | ||||
|                               'delta_p':     e.east_amp_dp, | ||||
|                               'tilt_target': e.east_tilt, | ||||
|                               'out_voa'    : e.east_att_out} | ||||
|              } | ||||
|              for e in eqpts if e.east_amp_type.lower() != ''] + | ||||
|              for e in eqpts if (e.east_amp_type.lower() != '' and \ | ||||
|                                 e.east_amp_type.lower() != 'fused')] + | ||||
|             [{'uid': f'west edfa in {e.from_city} to {e.to_city}', | ||||
|               'metadata': {'location': {'city':      nodes_by_city[e.from_city].city, | ||||
|                                         'region':    nodes_by_city[e.from_city].region, | ||||
| @@ -356,10 +375,34 @@ def convert_file(input_filename, names_matching=False, filter_region=[]): | ||||
|               'type': 'Edfa', | ||||
|               'type_variety': e.west_amp_type, | ||||
|               'operational': {'gain_target': e.west_amp_gain, | ||||
|                               'delta_p':     e.west_amp_dp, | ||||
|                               'tilt_target': e.west_tilt, | ||||
|                               'out_voa'    : e.west_att_out} | ||||
|              } | ||||
|              for e in eqpts if e.west_amp_type.lower() != ''], | ||||
|              for e in eqpts if (e.west_amp_type.lower() != '' and \ | ||||
|                                 e.west_amp_type.lower() != 'fused')] + | ||||
|             # fused edfa variety is a hack to indicate that there should not be | ||||
|             # booster amplifier out the roadm. | ||||
|             # If user specifies ILA in Nodes sheet and fused in Eqpt sheet, then assumes that | ||||
|             # this is a fused nodes. | ||||
|             [{'uid': f'east edfa in {e.from_city} to {e.to_city}', | ||||
|               'metadata': {'location': {'city':      nodes_by_city[e.from_city].city, | ||||
|                                         'region':    nodes_by_city[e.from_city].region, | ||||
|                                         'latitude':  nodes_by_city[e.from_city].latitude, | ||||
|                                         'longitude': nodes_by_city[e.from_city].longitude}}, | ||||
|               'type': 'Fused', | ||||
|               'params': {'loss': 0} | ||||
|              } | ||||
|              for e in eqpts if e.east_amp_type.lower() == 'fused'] + | ||||
|             [{'uid': f'west edfa in {e.from_city} to {e.to_city}', | ||||
|               'metadata': {'location': {'city':      nodes_by_city[e.from_city].city, | ||||
|                                         'region':    nodes_by_city[e.from_city].region, | ||||
|                                         'latitude':  nodes_by_city[e.from_city].latitude, | ||||
|                                         'longitude': nodes_by_city[e.from_city].longitude}}, | ||||
|               'type': 'Fused', | ||||
|               'params': {'loss': 0} | ||||
|              } | ||||
|              for e in eqpts if e.west_amp_type.lower() == 'fused'], | ||||
|         'connections': | ||||
|             list(chain.from_iterable([eqpt_connection_by_city(n.city) | ||||
|             for n in nodes])) | ||||
| @@ -411,7 +454,9 @@ def parse_excel(input_filename): | ||||
|         'Region':       'region', | ||||
|         'Latitude':     'latitude', | ||||
|         'Longitude':    'longitude', | ||||
|         'Type':         'node_type' | ||||
|         'Type':         'node_type', | ||||
|         'Booster_restriction': 'booster_restriction', | ||||
|         'Preamp_restriction': 'preamp_restriction' | ||||
|     } | ||||
|     eqpt_headers = \ | ||||
|     {  'Node A': 'from_city', | ||||
| @@ -420,6 +465,7 @@ def parse_excel(input_filename): | ||||
|             'amp type':         'east_amp_type', | ||||
|             'att_in':           'east_att_in', | ||||
|             'amp gain':         'east_amp_gain', | ||||
|             'delta p':          'east_amp_dp', | ||||
|             'tilt':             'east_tilt', | ||||
|             'att_out':          'east_att_out' | ||||
|        }, | ||||
| @@ -427,6 +473,7 @@ def parse_excel(input_filename): | ||||
|             'amp type':         'west_amp_type', | ||||
|             'att_in':           'west_att_in', | ||||
|             'amp gain':         'west_amp_gain', | ||||
|             'delta p':          'west_amp_dp', | ||||
|             'tilt':             'west_tilt', | ||||
|             'att_out':          'west_att_out' | ||||
|        } | ||||
| @@ -462,10 +509,13 @@ def parse_excel(input_filename): | ||||
|     # sanity check | ||||
|     all_cities = Counter(n.city for n in nodes) | ||||
|     if len(all_cities) != len(nodes): | ||||
|         ValueError(f'Duplicate city: {all_cities}') | ||||
|     if any(ln.from_city not in all_cities or | ||||
|            ln.to_city   not in all_cities for ln in links): | ||||
|         ValueError(f'Bad link.') | ||||
|         raise ValueError(f'Duplicate city: {all_cities}') | ||||
|     bad_links = [] | ||||
|     for lnk in links: | ||||
|         if lnk.from_city not in all_cities or lnk.to_city not in all_cities: | ||||
|             bad_links.append([lnk.from_city, lnk.to_city]) | ||||
|     if bad_links: | ||||
|         raise NetworkTopologyError(f'Bad link(s): {bad_links}.') | ||||
|  | ||||
|     return nodes, links, eqpts | ||||
|  | ||||
| @@ -566,12 +616,12 @@ def midpoint(city_a, city_b): | ||||
| #output_json_file_name = 'coronet_conus_example.json' | ||||
| #TODO get column size automatically from tupple size | ||||
|  | ||||
| NODES_COLUMN = 7 | ||||
| NODES_COLUMN = 10 | ||||
| NODES_LINE = 4 | ||||
| LINKS_COLUMN = 16 | ||||
| LINKS_LINE = 3 | ||||
| EQPTS_LINE = 3 | ||||
| EQPTS_COLUMN = 12 | ||||
| EQPTS_COLUMN = 14 | ||||
| parser = ArgumentParser() | ||||
| parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls') | ||||
| parser.add_argument('-f', '--filter-region', action='append', default=[]) | ||||
|   | ||||
| @@ -7,25 +7,26 @@ gnpy.core.elements | ||||
|  | ||||
| This module contains standard network elements. | ||||
|  | ||||
| A network element is a Python callable. It takes a .info.SpectralInformation | ||||
| A network element is a Python callable. It takes a :class:`.info.SpectralInformation` | ||||
| object and returns a copy with appropriate fields affected. This structure | ||||
| represents spectral information that is "propogated" by this network element. | ||||
| Network elements must have only a local "view" of the network and propogate | ||||
| SpectralInformation using only this information. They should be independent and | ||||
| :class:`.info.SpectralInformation` using only this information. They should be independent and | ||||
| self-contained. | ||||
|  | ||||
| Network elements MUST implement two attributes .uid and .name representing a | ||||
| unique identifier and a printable name. | ||||
| ''' | ||||
|  | ||||
| from numpy import abs, arange, arcsinh, array, exp, divide, errstate | ||||
| from numpy import abs, arange, array, exp, divide, errstate | ||||
| from numpy import interp, log10, mean, pi, polyfit, polyval, sum | ||||
| from scipy.constants import c, h | ||||
| from collections import namedtuple | ||||
|  | ||||
| from gnpy.core.node import Node | ||||
| from gnpy.core.units import UNITS | ||||
| from gnpy.core.utils import lin2db, db2lin, itufs, snr_sum | ||||
| from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum | ||||
| from gnpy.core.science_utils import propagate_raman_fiber, _psi | ||||
|  | ||||
| class Transceiver(Node): | ||||
|     def __init__(self, *args, **kwargs): | ||||
| @@ -41,7 +42,6 @@ class Transceiver(Node): | ||||
|         with errstate(divide='ignore'): | ||||
|             self.baud_rate = [c.baud_rate for c in spectral_info.carriers] | ||||
|             ratio_01nm = [lin2db(12.5e9/b_rate) for b_rate in self.baud_rate] | ||||
|              | ||||
|         #set raw values to record original calculation, before update_snr()             | ||||
|             self.raw_osnr_ase = [lin2db(divide(c.power.signal, c.power.ase)) | ||||
|                             for c in spectral_info.carriers] | ||||
| @@ -51,11 +51,14 @@ class Transceiver(Node): | ||||
|                              for c in spectral_info.carriers] | ||||
|             self.raw_snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase))  | ||||
|                         for c in spectral_info.carriers] | ||||
|             self.raw_snr_01nm = [snr - ratio for snr, ratio | ||||
|                                   in zip(self.raw_snr, ratio_01nm)] | ||||
|  | ||||
|             self.osnr_ase = self.raw_osnr_ase | ||||
|             self.osnr_ase_01nm = self.raw_osnr_ase_01nm | ||||
|             self.osnr_nli = self.raw_osnr_nli | ||||
|             self.snr = self.raw_snr | ||||
|             self.snr_01nm = self.raw_snr_01nm | ||||
|                          | ||||
|     def update_snr(self, *args): | ||||
|         """ | ||||
| @@ -75,6 +78,8 @@ class Transceiver(Node): | ||||
|                         self.raw_snr, self.baud_rate)) | ||||
|         self.osnr_ase_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),  | ||||
|                         self.raw_osnr_ase_01nm)) | ||||
|         self.snr_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),  | ||||
|                         self.raw_snr_01nm)) | ||||
|  | ||||
|     @property | ||||
|     def to_json(self): | ||||
| @@ -100,37 +105,42 @@ class Transceiver(Node): | ||||
|         snr = round(mean(self.snr),2) | ||||
|         osnr_ase = round(mean(self.osnr_ase),2) | ||||
|         osnr_ase_01nm = round(mean(self.osnr_ase_01nm), 2) | ||||
|         snr_01nm = round(mean(self.snr_01nm),2) | ||||
|  | ||||
|         return '\n'.join([f'{type(self).__name__} {self.uid}', | ||||
|  | ||||
|                           f'  OSNR ASE (0.1nm, dB):      {osnr_ase_01nm:.2f}', | ||||
|                           f'  OSNR ASE (signal bw, dB):  {osnr_ase:.2f}', | ||||
|                           f'  SNR total (signal bw, dB): {snr:.2f}']) | ||||
|  | ||||
|                           f'  SNR total (signal bw, dB): {snr:.2f}', | ||||
|                           f'  SNR total (0.1nm, dB): {snr_01nm:.2f}']) | ||||
|  | ||||
|     def __call__(self, spectral_info): | ||||
|         self._calc_snr(spectral_info) | ||||
|         return spectral_info | ||||
|  | ||||
| RoadmParams = namedtuple('RoadmParams', 'loss') | ||||
| RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr restrictions per_degree_target_pch_out_db') | ||||
|  | ||||
| class Roadm(Node): | ||||
|     def __init__(self, *args, params=None, **kwargs): | ||||
|         if params is None: | ||||
|             # default loss value if not mentioned in loaded network json | ||||
|             params = {'loss':None} | ||||
|     def __init__(self, *args, params, **kwargs): | ||||
|         if 'per_degree_target_pch_out_db' not in params.keys(): | ||||
|             params['per_degree_target_pch_out_db'] = [] | ||||
|         super().__init__(*args, params=RoadmParams(**params), **kwargs) | ||||
|         self.loss = self.params.loss | ||||
|         self.target_pch_out_db = None #set in Networks.py by def set_roadm_loss | ||||
|         self.effective_pch_out_db = None | ||||
|         self.effective_loss = None #set in self.propagate | ||||
|         self.loss = 0 #auto-design interest | ||||
|         self.effective_loss = None | ||||
|         self.effective_pch_out_db = self.params.target_pch_out_db | ||||
|         self.passive = True | ||||
|         self.restrictions = self.params.restrictions | ||||
|         self.per_degree_target_pch_out_db = self.params.per_degree_target_pch_out_db | ||||
|  | ||||
|     @property | ||||
|     def to_json(self): | ||||
|         return {'uid'       : self.uid, | ||||
|                 'type'      : type(self).__name__, | ||||
|                 'params'    : {'loss' : self.loss}, | ||||
|                 'params'    : { | ||||
|                     'target_pch_out_db' : self.effective_pch_out_db, | ||||
|                     'restrictions'      : self.restrictions, | ||||
|                     'per_degree_target_pch_out_db': self.per_degree_target_pch_out_db | ||||
|                     }, | ||||
|                 'metadata'      : { | ||||
|                     'location': self.metadata['location']._asdict() | ||||
|                                 } | ||||
| @@ -141,35 +151,45 @@ class Roadm(Node): | ||||
|  | ||||
|     def __str__(self): | ||||
|         return '\n'.join([f'{type(self).__name__} {self.uid}', | ||||
|                           f'  loss (dB):     {self.effective_loss:.2f}', | ||||
|                           f'  effective loss (dB):  {self.effective_loss:.2f}', | ||||
|                           f'  pch out (dBm):        {self.effective_pch_out_db!r}']) | ||||
|  | ||||
|     def propagate(self, pref, *carriers): | ||||
|     def propagate(self, pref, *carriers, degree): | ||||
|         #pin_target and loss are read from eqpt_config.json['Roadm'] | ||||
|         #all ingress channels in xpress are set to this power level | ||||
|         #but add channels are not, so we define an effective loss | ||||
|         #in the case of add channels | ||||
|         if self.target_pch_out_db: | ||||
|             self.effective_loss = pref.pi - self.target_pch_out_db | ||||
|         if self.per_degree_target_pch_out_db: | ||||
|             # find the target power on this degree | ||||
|             try: | ||||
|                 temp = next(el['target_pch_out_db'] \ | ||||
|                     for el in self.per_degree_target_pch_out_db if el['to_node']==degree) | ||||
|             except StopIteration: | ||||
|                 # if no target power is defined on this degree use the global one | ||||
|                 temp = self.params.target_pch_out_db | ||||
|         else: | ||||
|              self.effective_loss = self.loss | ||||
|         self.effective_pch_out_db = pref.pi - self.effective_loss | ||||
|         attenuation = db2lin(self.effective_loss) | ||||
|  | ||||
|         for carrier in carriers: | ||||
|             # if no per degree target power are defined, use the global one | ||||
|             temp = self.params.target_pch_out_db | ||||
|         self.effective_pch_out_db = min(pref.p_spani, temp) | ||||
|         self.effective_loss = pref.p_spani - self.effective_pch_out_db | ||||
|         carriers_power = array([c.power.signal +c.power.nli+c.power.ase for c in carriers]) | ||||
|         carriers_att = list(map(lambda x : lin2db(x*1e3)-self.effective_pch_out_db, carriers_power)) | ||||
|         exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default = 0) | ||||
|         carriers_att = list(map(lambda x: db2lin(x+exceeding_att), carriers_att)) | ||||
|         for carrier_att, carrier in zip(carriers_att, carriers) : | ||||
|             pwr = carrier.power | ||||
|             pwr = pwr._replace(signal=pwr.signal/attenuation, | ||||
|                                nonlinear_interference=pwr.nli/attenuation, | ||||
|                                amplified_spontaneous_emission=pwr.ase/attenuation) | ||||
|             pwr = pwr._replace( signal = pwr.signal/carrier_att, | ||||
|                                 nli = pwr.nli/carrier_att, | ||||
|                                 ase = pwr.ase/carrier_att) | ||||
|             yield carrier._replace(power=pwr) | ||||
|  | ||||
|     def update_pref(self, pref): | ||||
|         return pref._replace(p_span0=pref.p0, p_spani=self.effective_pch_out_db) | ||||
|         return pref._replace(p_span0=pref.p_span0, p_spani=self.effective_pch_out_db) | ||||
|  | ||||
|     def __call__(self, spectral_info): | ||||
|         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) | ||||
|     def __call__(self, spectral_info, degree): | ||||
|         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers, degree=degree)) | ||||
|         pref = self.update_pref(spectral_info.pref) | ||||
|         return spectral_info.update(carriers=carriers, pref=pref) | ||||
|         return spectral_info._replace(carriers=carriers, pref=pref) | ||||
|  | ||||
| FusedParams = namedtuple('FusedParams', 'loss') | ||||
|  | ||||
| @@ -186,6 +206,9 @@ class Fused(Node): | ||||
|     def to_json(self): | ||||
|         return {'uid'       : self.uid, | ||||
|                 'type'      : type(self).__name__, | ||||
|                 'params'    :{ | ||||
|                     'loss': self.loss | ||||
|                 }, | ||||
|                 'metadata'      : { | ||||
|                     'location': self.metadata['location']._asdict() | ||||
|                                     } | ||||
| @@ -204,17 +227,17 @@ class Fused(Node): | ||||
|         for carrier in carriers: | ||||
|             pwr = carrier.power | ||||
|             pwr = pwr._replace(signal=pwr.signal/attenuation, | ||||
|                                nonlinear_interference=pwr.nli/attenuation, | ||||
|                                amplified_spontaneous_emission=pwr.ase/attenuation) | ||||
|                                nli=pwr.nli/attenuation, | ||||
|                                ase=pwr.ase/attenuation) | ||||
|             yield carrier._replace(power=pwr) | ||||
|  | ||||
|     def update_pref(self, pref): | ||||
|         return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss) | ||||
|         return pref._replace(p_span0=pref.p_span0, p_spani=pref.p_spani - self.loss) | ||||
|  | ||||
|     def __call__(self, spectral_info): | ||||
|         carriers = tuple(self.propagate(*spectral_info.carriers)) | ||||
|         pref = self.update_pref(spectral_info.pref) | ||||
|         return spectral_info.update(carriers=carriers, pref=pref) | ||||
|         return spectral_info._replace(carriers=carriers, pref=pref) | ||||
|  | ||||
| FiberParams = namedtuple('FiberParams', 'type_variety length loss_coef length_units \ | ||||
|                                          att_in con_in con_out dispersion gamma') | ||||
| @@ -283,12 +306,12 @@ class Fiber(Node): | ||||
|  | ||||
|     @property | ||||
|     def fiber_loss(self): | ||||
|         # dB fiber loss, not including padding attenuator | ||||
|         """Fiber loss in dB, not including padding attenuator""" | ||||
|         return self.loss_coef * self.length + self.con_in + self.con_out | ||||
|  | ||||
|     @property | ||||
|     def loss(self): | ||||
|         #total loss incluiding padding att_in: useful for polymorphism with roadm loss | ||||
|         """total loss including padding att_in: useful for polymorphism with roadm loss""" | ||||
|         return self.loss_coef * self.length + self.con_in + self.con_out + self.att_in | ||||
|  | ||||
|     @property | ||||
| @@ -313,16 +336,13 @@ class Fiber(Node): | ||||
|  | ||||
|     def carriers(self, loc, attr): | ||||
|         """retrieve carriers information | ||||
|         loc = (in, out) of the class element | ||||
|         attr = (ase, nli, signal, total) power information""" | ||||
|  | ||||
|         :param loc: (in, out) of the class element | ||||
|         :param attr: (ase, nli, signal, total) power information | ||||
|         """ | ||||
|         if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')): | ||||
|             yield None | ||||
|             return | ||||
|         power_dict = { | ||||
|                         'nli':      'nonlinear_interference', | ||||
|                         'ase':      'amplified_spontaneous_emission' | ||||
|                     } | ||||
|         attr = power_dict.get(attr, attr) | ||||
|         loc_attr = 'carriers_'+loc | ||||
|         for c in getattr(self, loc_attr) : | ||||
|             if attr == 'total': | ||||
| @@ -330,46 +350,30 @@ class Fiber(Node): | ||||
|             else: | ||||
|                 yield c.power._asdict().get(attr, None) | ||||
|  | ||||
|     def beta2(self, ref_wavelength=None): | ||||
|         """ Returns beta2 from dispersion parameter. | ||||
|     def beta2(self, ref_wavelength=1550e-9): | ||||
|         """Returns beta2 from dispersion parameter. | ||||
|         Dispersion is entered in ps/nm/km. | ||||
|         Disperion can be a numpy array or a single value.  If a | ||||
|         value ref_wavelength is not entered 1550e-9m will be assumed. | ||||
|         ref_wavelength can be a numpy array. | ||||
|         Disperion can be a numpy array or a single value. | ||||
|  | ||||
|         :param ref_wavelength: can be a numpy array; default: 1550nm | ||||
|         """ | ||||
|         # TODO|jla: discuss beta2 as method or attribute | ||||
|         wl = 1550e-9 if ref_wavelength is None else ref_wavelength | ||||
|         D = abs(self.dispersion) | ||||
|         b2 = (wl ** 2) * D / (2 * pi * c)  # 10^21 scales [ps^2/km] | ||||
|         b2 = (ref_wavelength ** 2) * D / (2 * pi * c)  # 10^21 scales [ps^2/km] | ||||
|         return b2 # s/Hz/m | ||||
|  | ||||
|     def dbkm_2_lin(self): | ||||
|         """ calculates the linear loss coefficient | ||||
|         """ | ||||
|         # alpha_pcoef is linear loss coefficient in dB/km^-1 | ||||
|         # alpha_acoef is linear loss field amplitude coefficient in m^-1 | ||||
|         """calculates the linear loss coefficient""" | ||||
|         # linear loss coefficient in dB/km^-1 | ||||
|         alpha_pcoef = self.loss_coef | ||||
|         # linear loss field amplitude coefficient in m^-1 | ||||
|         alpha_acoef = alpha_pcoef / (2 * 10 * log10(exp(1))) | ||||
|         return alpha_pcoef, alpha_acoef | ||||
|  | ||||
|     def _psi(self, carrier, interfering_carrier): | ||||
|         """ Calculates eq. 123 from	arXiv:1209.0394. | ||||
|         """ | ||||
|         if carrier.num_chan == interfering_carrier.num_chan: # SCI | ||||
|             psi = arcsinh(0.5 * pi**2 * self.asymptotic_length | ||||
|                               * abs(self.beta2()) * carrier.baud_rate**2) | ||||
|         else: # XCI | ||||
|             delta_f = carrier.freq - interfering_carrier.freq | ||||
|             psi = arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2()) | ||||
|                                 * carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate)) | ||||
|             psi -= arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2()) | ||||
|                                  * carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate)) | ||||
|  | ||||
|         return psi | ||||
|  | ||||
|     def _gn_analytic(self, carrier, *carriers): | ||||
|         """ Computes the nonlinear interference power on a single carrier. | ||||
|         The method uses eq. 120 from arXiv:1209.0394. | ||||
|         """Computes the nonlinear interference power on a single carrier. | ||||
|         The method uses eq. 120 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__. | ||||
|  | ||||
|         :param carrier: the signal under analysis | ||||
|         :param carriers: the full WDM comb | ||||
|         :return: carrier_nli: the amount of nonlinear interference in W on the under analysis | ||||
| @@ -377,7 +381,7 @@ class Fiber(Node): | ||||
|  | ||||
|         g_nli = 0 | ||||
|         for interfering_carrier in carriers: | ||||
|             psi = self._psi(carrier, interfering_carrier) | ||||
|             psi = _psi(carrier, interfering_carrier, beta2=self.beta2(), asymptotic_length=self.asymptotic_length) | ||||
|             g_nli += (interfering_carrier.power.signal/interfering_carrier.baud_rate)**2 \ | ||||
|                      * (carrier.power.signal/carrier.baud_rate) * psi | ||||
|  | ||||
| @@ -396,8 +400,8 @@ class Fiber(Node): | ||||
|         for carrier in carriers: | ||||
|             pwr = carrier.power | ||||
|             pwr = pwr._replace(signal=pwr.signal/attenuation, | ||||
|                                nonlinear_interference=pwr.nli/attenuation, | ||||
|                                amplified_spontaneous_emission=pwr.ase/attenuation) | ||||
|                                nli=pwr.nli/attenuation, | ||||
|                                ase=pwr.ase/attenuation) | ||||
|             carrier = carrier._replace(power=pwr) | ||||
|             chan.append(carrier) | ||||
|  | ||||
| @@ -409,20 +413,77 @@ class Fiber(Node): | ||||
|             pwr = carrier.power | ||||
|             carrier_nli = self._gn_analytic(carrier, *carriers) | ||||
|             pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation/attenuation, | ||||
|                                nonlinear_interference=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation, | ||||
|                                amplified_spontaneous_emission=pwr.ase/self.lin_attenuation/attenuation) | ||||
|                                nli=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation, | ||||
|                                ase=pwr.ase/self.lin_attenuation/attenuation) | ||||
|             yield carrier._replace(power=pwr) | ||||
|  | ||||
|     def update_pref(self, pref): | ||||
|         self.pch_out_db = round(pref.pi - self.loss, 2) | ||||
|         return pref._replace(p_span0=pref.p0, p_spani=self.pch_out_db) | ||||
|         self.pch_out_db = round(pref.p_spani - self.loss, 2) | ||||
|         return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db) | ||||
|  | ||||
|     def __call__(self, spectral_info): | ||||
|         self.carriers_in = spectral_info.carriers | ||||
|         carriers = tuple(self.propagate(*spectral_info.carriers)) | ||||
|         pref = self.update_pref(spectral_info.pref) | ||||
|         self.carriers_out = carriers | ||||
|         return spectral_info.update(carriers=carriers, pref=pref) | ||||
|         return spectral_info._replace(carriers=carriers, pref=pref) | ||||
|  | ||||
| RamanFiberParams = namedtuple('RamanFiberParams', 'type_variety length loss_coef length_units \ | ||||
|                                          att_in con_in con_out dispersion gamma raman_efficiency') | ||||
|  | ||||
| class RamanFiber(Fiber): | ||||
|     def __init__(self, *args, params=None, **kwargs): | ||||
|         if params is None: | ||||
|             params = {} | ||||
|         if 'con_in' not in params: | ||||
|             # if not defined in the network json connector loss in/out | ||||
|             # the None value will be updated in network.py[build_network] | ||||
|             # with default values from eqpt_config.json[Spans] | ||||
|             params['con_in'] = None | ||||
|             params['con_out'] = None | ||||
|         if 'att_in' not in params: | ||||
|             #fixed attenuator for padding | ||||
|             params['att_in'] = 0 | ||||
|  | ||||
|         # TODO: can we re-use the Fiber constructor in a better way? | ||||
|         Node.__init__(self, *args, params=RamanFiberParams(**params), **kwargs) | ||||
|         self.type_variety = self.params.type_variety | ||||
|         self.length = self.params.length * UNITS[self.params.length_units] # in m | ||||
|         self.loss_coef = self.params.loss_coef * 1e-3 # lineic loss dB/m | ||||
|         self.lin_loss_coef = self.params.loss_coef / (20 * log10(exp(1))) | ||||
|         self.att_in = self.params.att_in | ||||
|         self.con_in = self.params.con_in | ||||
|         self.con_out = self.params.con_out | ||||
|         self.dispersion = self.params.dispersion  # s/m/m | ||||
|         self.gamma = self.params.gamma # 1/W/m | ||||
|         self.pch_out_db = None | ||||
|         self.carriers_in = None | ||||
|         self.carriers_out = None | ||||
|         # TODO|jla: discuss factor 2 in the linear lineic attenuation | ||||
|  | ||||
|     @property | ||||
|     def sim_params(self): | ||||
|         return self._sim_params | ||||
|  | ||||
|     @sim_params.setter | ||||
|     def sim_params(self, sim_params=None): | ||||
|         self._sim_params = sim_params | ||||
|  | ||||
|     def update_pref(self, pref, *carriers): | ||||
|         pch_out_db = lin2db(mean([carrier.power.signal for carrier in carriers])) + 30 | ||||
|         self.pch_out_db = round(pch_out_db, 2) | ||||
|         return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db) | ||||
|  | ||||
|     def __call__(self, spectral_info): | ||||
|         self.carriers_in = spectral_info.carriers | ||||
|         carriers = tuple(self.propagate(*spectral_info.carriers)) | ||||
|         pref = self.update_pref(spectral_info.pref, *carriers) | ||||
|         self.carriers_out = carriers | ||||
|         return spectral_info._replace(carriers=carriers, pref=pref) | ||||
|  | ||||
|     def propagate(self, *carriers): | ||||
|         for propagated_carrier in propagate_raman_fiber(self, *carriers): | ||||
|             yield propagated_carrier | ||||
|  | ||||
| class EdfaParams: | ||||
|     def __init__(self, **params): | ||||
| @@ -430,16 +491,16 @@ class EdfaParams: | ||||
|         if params == {}: | ||||
|             self.type_variety = '' | ||||
|             self.type_def = '' | ||||
|             self.gain_flatmax = 0 | ||||
|             self.gain_min = 0 | ||||
|             self.p_max = 0 | ||||
|             self.nf_model = None | ||||
|             self.nf_fit_coeff = None | ||||
|             self.nf_ripple = None | ||||
|             self.dgt = None | ||||
|             self.gain_ripple = None | ||||
|             self.out_voa_auto = False | ||||
|             self.allowed_for_design = None | ||||
|             # self.gain_flatmax = 0 | ||||
|             # self.gain_min = 0 | ||||
|             # self.p_max = 0 | ||||
|             # self.nf_model = None | ||||
|             # self.nf_fit_coeff = None | ||||
|             # self.nf_ripple = None | ||||
|             # self.dgt = None | ||||
|             # self.gain_ripple = None | ||||
|             # self.out_voa_auto = False | ||||
|             # self.allowed_for_design = None | ||||
|  | ||||
|     def update_params(self, kwargs): | ||||
|         for k,v in kwargs.items() : | ||||
| @@ -447,22 +508,33 @@ class EdfaParams: | ||||
|                 if isinstance(v, dict) else v) | ||||
|  | ||||
| class EdfaOperational: | ||||
|     def __init__(self, gain_target, tilt_target, out_voa=None): | ||||
|         self.gain_target = gain_target | ||||
|         self.tilt_target = tilt_target | ||||
|         self.out_voa = out_voa | ||||
|     default_values = \ | ||||
|     { | ||||
|         'gain_target':      None, | ||||
|         'delta_p':          None, | ||||
|         'out_voa':          None,         | ||||
|         'tilt_target':      0 | ||||
|     } | ||||
|  | ||||
|     def __init__(self, **operational): | ||||
|         self.update_attr(operational) | ||||
|  | ||||
|     def update_attr(self, kwargs): | ||||
|         clean_kwargs = {k:v for k,v in kwargs.items() if v !=''} | ||||
|         for k,v in self.default_values.items(): | ||||
|             setattr(self, k, clean_kwargs.get(k,v)) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return (f'{type(self).__name__}(' | ||||
|                 f'gain_target={self.gain_target!r}, ' | ||||
|                 f'tilt_target={self.tilt_target!r})') | ||||
|  | ||||
| class Edfa(Node): | ||||
|     def __init__(self, *args, params={}, operational={}, **kwargs): | ||||
|         #TBC is this useful? put in comment for now: | ||||
|         #if params is None: | ||||
|         #    params = {} | ||||
|         #if operational is None: | ||||
|         #    operational = {} | ||||
|     def __init__(self, *args, params=None, operational=None, **kwargs): | ||||
|         if params is None: | ||||
|             params = {} | ||||
|         if operational is None: | ||||
|             operational = {} | ||||
|         super().__init__( | ||||
|             *args, | ||||
|             params=EdfaParams(**params), | ||||
| @@ -479,14 +551,16 @@ class Edfa(Node): | ||||
|         self.pin_db = None | ||||
|         self.nch = None | ||||
|         self.pout_db = None | ||||
|         self.dp_db = None #delta P with Pref (power swwep) in power mode | ||||
|         self.target_pch_out_db = None | ||||
|         self.effective_pch_out_db = None | ||||
|         self.passive = False | ||||
|         self.effective_gain = self.operational.gain_target | ||||
|         self.att_in = None | ||||
|         self.carriers_in = None | ||||
|         self.carriers_out = None | ||||
|         self.effective_gain = self.operational.gain_target | ||||
|         self.delta_p = self.operational.delta_p #delta P with Pref (power swwep) in power mode | ||||
|         self.tilt_target = self.operational.tilt_target | ||||
|         self.out_voa = self.operational.out_voa | ||||
|  | ||||
|     @property | ||||
|     def to_json(self): | ||||
| @@ -494,9 +568,10 @@ class Edfa(Node): | ||||
|                 'type'          : type(self).__name__, | ||||
|                 'type_variety'  : self.params.type_variety, | ||||
|                 'operational'   : { | ||||
|                     'gain_target' : self.operational.gain_target, | ||||
|                     'tilt_target' : self.operational.tilt_target, | ||||
|                     'out_voa'     : self.operational.out_voa | ||||
|                     'gain_target' : self.effective_gain, | ||||
|                     'delta_p'     : self.delta_p, | ||||
|                     'tilt_target' : self.tilt_target, | ||||
|                     'out_voa'     : self.out_voa | ||||
|                 }, | ||||
|                 'metadata'      : { | ||||
|                     'location': self.metadata['location']._asdict() | ||||
| @@ -528,23 +603,20 @@ class Edfa(Node): | ||||
|                           f'  pad att_in (dB):        {self.att_in:.2f}', | ||||
|                           f'  Power In (dBm):         {self.pin_db:.2f}', | ||||
|                           f'  Power Out (dBm):        {self.pout_db:.2f}', | ||||
|                           f'  Delta_P (dB):           {self.dp_db!r}', | ||||
|                           f'  Delta_P (dB):           {self.delta_p!r}', | ||||
|                           f'  target pch (dBm):       {self.target_pch_out_db!r}', | ||||
|                           f'  effective pch (dBm):    {self.effective_pch_out_db!r}', | ||||
|                           f'  output VOA (dB):        {self.operational.out_voa:.2f}']) | ||||
|                           f'  output VOA (dB):        {self.out_voa:.2f}']) | ||||
|  | ||||
|     def carriers(self, loc, attr): | ||||
|         """retrieve carriers information | ||||
|         loc = (in, out) of the class element | ||||
|         attr = (ase, nli, signal, total) power information""" | ||||
|  | ||||
|         :param loc: (in, out) of the class element | ||||
|         :param attr: (ase, nli, signal, total) power information | ||||
|         """ | ||||
|         if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')): | ||||
|             yield None | ||||
|             return | ||||
|         power_dict = { | ||||
|                         'nli':      'nonlinear_interference', | ||||
|                         'ase':      'amplified_spontaneous_emission' | ||||
|                     } | ||||
|         attr = power_dict.get(attr, attr) | ||||
|         loc_attr = 'carriers_'+loc | ||||
|         for c in getattr(self, loc_attr) : | ||||
|             if attr == 'total': | ||||
| @@ -553,69 +625,106 @@ class Edfa(Node): | ||||
|                 yield c.power._asdict().get(attr, None) | ||||
|  | ||||
|     def interpol_params(self, frequencies, pin, baud_rates, pref): | ||||
|         """interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from json | ||||
|         """interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from JSON | ||||
|         set the edfa class __init__ None parameters : | ||||
|                 self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple | ||||
|         """ | ||||
|         # TODO|jla: read amplifier actual frequencies from additional params in json | ||||
|         amplifier_freq = itufs(0.05) * 1e12 # Hz | ||||
|         amplifier_freq = arrange_frequencies(len(self.params.dgt), self.params.f_min, self.params.f_max) # Hz | ||||
|         self.channel_freq = frequencies | ||||
|         self.interpol_dgt = interp(self.channel_freq, amplifier_freq, self.params.dgt) | ||||
|  | ||||
|         self.interpol_gain_ripple = interp(self.channel_freq, amplifier_freq, self.params.gain_ripple) | ||||
|         self.interpol_nf_ripple =interp(self.channel_freq, amplifier_freq, self.params.nf_ripple) | ||||
|  | ||||
|         self.nch = frequencies.size | ||||
|         self.pin_db = lin2db(sum(pin*1e3)) | ||||
|          | ||||
|         """in power mode: dp_db is defined and can be used to calculate the power target | ||||
|         """in power mode: delta_p is defined and can be used to calculate the power target | ||||
|         This power target is used calculate the amplifier gain""" | ||||
|         if self.dp_db is not None: | ||||
|             self.target_pch_out_db = round(self.dp_db + pref.p0, 2) | ||||
|             self.effective_gain = self.target_pch_out_db - pref.pi | ||||
|         else: | ||||
|             self.effective_gain = self.operational.gain_target | ||||
|         if self.delta_p is not None: | ||||
|             self.target_pch_out_db = round(self.delta_p + pref.p_span0, 2) | ||||
|             self.effective_gain = self.target_pch_out_db - pref.p_spani | ||||
|  | ||||
|         """check power saturation and correct effective gain & power accordingly:"""             | ||||
|         self.effective_gain = min(   | ||||
|                                     self.effective_gain,  | ||||
|                                     self.params.p_max - (pref.p_spani + pref.neq_ch) | ||||
|                                     ) | ||||
|         #print(self.uid, self.effective_gain, self.operational.gain_target) | ||||
|         self.effective_pch_out_db = round(pref.p_spani + self.effective_gain, 2) | ||||
|  | ||||
|         """check power saturation and correct target_gain accordingly:""" | ||||
|         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) | ||||
|  | ||||
|         #print(self.uid, self.effective_gain, self.pin_db, pref.p_spani) | ||||
|         self.nf = self._calc_nf() | ||||
|         self.gprofile = self._gain_profile(pin) | ||||
|  | ||||
|         pout = (pin + self.noise_profile(baud_rates))*db2lin(self.gprofile) | ||||
|         self.pout_db = lin2db(sum(pout*1e3)) | ||||
|         self.operational.gain_target = self.effective_gain | ||||
|         # ase & nli are only calculated in signal bandwidth | ||||
|         #    pout_db is not the absolute full output power (negligible if sufficient channels) | ||||
|  | ||||
|     def _nf(self, type_def, nf_model, nf_fit_coeff, gain_min, gain_flatmax, gain_target): | ||||
|         #if hybrid raman, use edfa_gain_flatmax attribute, else use gain_flatmax  | ||||
|         #gain_flatmax = getattr(params, 'edfa_gain_flatmax', params.gain_flatmax) | ||||
|         pad = max(gain_min - gain_target, 0) | ||||
|         gain_target += pad | ||||
|         dg = max(gain_flatmax - gain_target, 0) | ||||
|         if type_def == 'variable_gain': | ||||
|             g1a = gain_target - nf_model.delta_p - dg | ||||
|             nf_avg = lin2db(db2lin(nf_model.nf1) + db2lin(nf_model.nf2)/db2lin(g1a))             | ||||
|         elif type_def == 'fixed_gain': | ||||
|             nf_avg = nf_model.nf0 | ||||
|         elif type_def == 'openroadm': | ||||
|             pin_ch = self.pin_db - lin2db(self.nch) | ||||
|             # model OSNR = f(Pin) | ||||
|             nf_avg = pin_ch - polyval(nf_model.nf_coef, pin_ch) + 58 | ||||
|         elif type_def == 'advanced_model': | ||||
|             nf_avg = polyval(nf_fit_coeff, -dg) | ||||
|         else: | ||||
|             assert False, "Unrecognized amplifier type, this should have been checked by the JSON loader" | ||||
|         return nf_avg+pad, pad | ||||
|  | ||||
|     def _calc_nf(self, avg = False): | ||||
|         """nf calculation based on 2 models: self.params.nf_model.enabled from json import: | ||||
|         True => 2 stages amp modelling based on precalculated nf1, nf2 and delta_p in build_OA_json | ||||
|         False => polynomial fit based on self.params.nf_fit_coeff""" | ||||
|         # TODO|jla: TBD alarm rising or input VOA padding in case | ||||
|         # gain_min > gain_target TBD: | ||||
|         pad = max(self.params.gain_min - self.effective_gain, 0) | ||||
|         self.att_in = pad | ||||
|         gain_target = self.effective_gain + pad | ||||
|         dg = max(self.params.gain_flatmax - gain_target, 0) | ||||
|         if self.params.type_def == 'variable_gain': | ||||
|             g1a = gain_target - self.params.nf_model.delta_p - dg | ||||
|             nf_avg = lin2db(db2lin(self.params.nf_model.nf1) + db2lin(self.params.nf_model.nf2)/db2lin(g1a)) | ||||
|         elif self.params.type_def == 'fixed_gain': | ||||
|             nf_avg = self.params.nf_model.nf0 | ||||
|         elif self.params.type_def == 'openroadm': | ||||
|             pin_ch = self.pin_db - lin2db(self.nch) | ||||
|             # model OSNR = f(Pin) | ||||
|             nf_avg = pin_ch - polyval(self.params.nf_model.nf_coef, pin_ch) + 58 | ||||
|         if self.params.type_def == 'dual_stage': | ||||
|             g1 = self.params.preamp_gain_flatmax | ||||
|             g2 = self.effective_gain - g1 | ||||
|             nf1_avg, pad = self._nf( self.params.preamp_type_def,  | ||||
|                                 self.params.preamp_nf_model, | ||||
|                                 self.params.preamp_nf_fit_coeff, | ||||
|                                 self.params.preamp_gain_min, | ||||
|                                 self.params.preamp_gain_flatmax,  | ||||
|                                 g1) | ||||
|             #no padding expected for the 1stage because g1 = gain_max | ||||
|             nf2_avg, pad = self._nf( self.params.booster_type_def, | ||||
|                                 self.params.booster_nf_model, | ||||
|                                 self.params.booster_nf_fit_coeff, | ||||
|                                 self.params.booster_gain_min, | ||||
|                                 self.params.booster_gain_flatmax,  | ||||
|                                 g2) | ||||
|             nf_avg = lin2db(db2lin(nf1_avg) + db2lin(nf2_avg-g1)) | ||||
|             #no padding expected for the 1stage because g1 = gain_max             | ||||
|             pad = 0 | ||||
|         else:       | ||||
|             nf_avg = polyval(self.params.nf_fit_coeff, -dg) | ||||
|             nf_avg, pad = self._nf(  self.params.type_def, | ||||
|                                 self.params.nf_model, | ||||
|                                 self.params.nf_fit_coeff, | ||||
|                                 self.params.gain_min, | ||||
|                                 self.params.gain_flatmax, | ||||
|                                 self.effective_gain) | ||||
|  | ||||
|         self.att_in = pad # not used to attenuate carriers, only used in _repr_ and _str_ | ||||
|         if avg: | ||||
|             return nf_avg + pad | ||||
|             return nf_avg | ||||
|         else: | ||||
|             return self.interpol_nf_ripple + nf_avg + pad # input VOA = 1 for 1 NF degradation | ||||
|             return self.interpol_nf_ripple + nf_avg # input VOA = 1 for 1 NF degradation | ||||
|  | ||||
|     def noise_profile(self, df): | ||||
|         """ noise_profile(bw) computes amplifier ase (W) in signal bw (Hz) | ||||
|         """noise_profile(bw) computes amplifier ase (W) in signal bw (Hz) | ||||
|         noise is calculated at amplifier input | ||||
|  | ||||
|         :bw: signal bandwidth = baud rate in Hz | ||||
| @@ -705,7 +814,7 @@ class Edfa(Node): | ||||
|  | ||||
|         # Calculate the target slope - currently assumes equal spaced channels | ||||
|         # TODO|jla: support arbitrary channel spacing | ||||
|         targ_slope = self.operational.tilt_target / (len(nb_channel) - 1) | ||||
|         targ_slope = self.tilt_target / (len(nb_channel) - 1) | ||||
|  | ||||
|         # first estimate of DGT scaling | ||||
|         if abs(dgt_slope) > 0.001: # check for zero value due to flat dgt | ||||
| @@ -770,7 +879,7 @@ class Edfa(Node): | ||||
|         return g1st - voa + array(self.interpol_dgt) * dgts3 | ||||
|  | ||||
|     def propagate(self, pref, *carriers): | ||||
|         """add ase noise to the propagating carriers of SpectralInformation""" | ||||
|         """add ASE noise to the propagating carriers of :class:`.info.SpectralInformation`""" | ||||
|         pin = array([c.power.signal+c.power.nli+c.power.ase for c in carriers]) # pin in W | ||||
|         freq = array([c.frequency for c in carriers]) | ||||
|         brate = array([c.baud_rate for c in carriers]) | ||||
| @@ -779,22 +888,22 @@ class Edfa(Node): | ||||
|  | ||||
|         gains = db2lin(self.gprofile) | ||||
|         carrier_ases = self.noise_profile(brate) | ||||
|         att = db2lin(self.operational.out_voa) | ||||
|         att = db2lin(self.out_voa) | ||||
|  | ||||
|         for gain, carrier_ase, carrier in zip(gains, carrier_ases, carriers): | ||||
|             pwr = carrier.power | ||||
|             pwr = pwr._replace(signal=pwr.signal*gain/att, | ||||
|                                nonlinear_interference=pwr.nli*gain/att, | ||||
|                                amplified_spontaneous_emission=(pwr.ase+carrier_ase)*gain/att) | ||||
|                                nli=pwr.nli*gain/att, | ||||
|                                ase=(pwr.ase+carrier_ase)*gain/att) | ||||
|             yield carrier._replace(power=pwr) | ||||
|  | ||||
|     def update_pref(self, pref): | ||||
|         return pref._replace(p_span0=pref.p0, | ||||
|                             p_spani=pref.pi + self.effective_gain - self.operational.out_voa) | ||||
|         return pref._replace(p_span0=pref.p_span0, | ||||
|                             p_spani=pref.p_spani + self.effective_gain - self.out_voa) | ||||
|  | ||||
|     def __call__(self, spectral_info): | ||||
|         self.carriers_in = spectral_info.carriers | ||||
|         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) | ||||
|         pref = self.update_pref(spectral_info.pref) | ||||
|         self.carriers_out = carriers | ||||
|         return spectral_info.update(carriers=carriers, pref=pref) | ||||
|         return spectral_info._replace(carriers=carriers, pref=pref) | ||||
|   | ||||
| @@ -9,7 +9,6 @@ This module contains functionality for specifying equipment. | ||||
| ''' | ||||
|  | ||||
| from numpy import clip, polyval | ||||
| from sys import exit | ||||
| from operator import itemgetter | ||||
| from math import isclose | ||||
| from pathlib import Path | ||||
| @@ -17,64 +16,169 @@ from json import load | ||||
| from gnpy.core.utils import lin2db, db2lin, load_json | ||||
| from collections import namedtuple | ||||
| from gnpy.core.elements import Edfa | ||||
| from gnpy.core.exceptions import EquipmentConfigError | ||||
| import time | ||||
|  | ||||
| Model_vg = namedtuple('Model_vg', 'nf1 nf2 delta_p') | ||||
| Model_fg = namedtuple('Model_fg', 'nf0') | ||||
| Model_openroadm = namedtuple('Model_openroadm', 'nf_coef') | ||||
| Fiber = namedtuple('Fiber', 'type_variety dispersion gamma') | ||||
| Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \ | ||||
|                              max_loss padding EOL con_in con_out') | ||||
| Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') | ||||
| Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target add_drop_osnr') | ||||
| SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ | ||||
|                        power_dbm power_range_db tx_osnr sys_margins') | ||||
| AmpBase = namedtuple( | ||||
|     'AmpBase', | ||||
|     'type_variety type_def gain_flatmax gain_min p_max' | ||||
|     ' nf_model nf_fit_coeff nf_ripple dgt gain_ripple out_voa_auto allowed_for_design') | ||||
| class Amp(AmpBase): | ||||
|     def __new__(cls, | ||||
|             type_variety, type_def, gain_flatmax, gain_min, p_max, nf_model=None, | ||||
|             nf_fit_coeff=None, nf_ripple=None, dgt=None, gain_ripple=None, | ||||
|              out_voa_auto=False, allowed_for_design=True): | ||||
|         return super().__new__(cls, | ||||
|             type_variety, type_def, gain_flatmax, gain_min, p_max, | ||||
|             nf_model, nf_fit_coeff, nf_ripple, dgt, gain_ripple, | ||||
|             out_voa_auto, allowed_for_design) | ||||
| Model_hybrid = namedtuple('Model_hybrid', 'nf_ram gain_ram edfa_variety') | ||||
| Model_dual_stage = namedtuple('Model_dual_stage', 'preamp_variety booster_variety') | ||||
|  | ||||
| class common: | ||||
|     def update_attr(self, default_values, kwargs, name): | ||||
|         clean_kwargs = {k:v for k, v in kwargs.items() if v != ''} | ||||
|         for k, v in default_values.items(): | ||||
|             setattr(self, k, clean_kwargs.get(k, v)) | ||||
|             if k not in clean_kwargs and name != 'Amp': | ||||
|                 print(f'\x1b[1;31;40m'+ | ||||
|                       f'\n WARNING missing {k} attribute in eqpt_config.json[{name}]'+ | ||||
|                       f'\n default value is {k} = {v}'+ | ||||
|                       f'\x1b[0m') | ||||
|                 time.sleep(1) | ||||
|  | ||||
| class SI(common): | ||||
|     default_values =\ | ||||
|     { | ||||
|         "f_min":            191.35e12, | ||||
|         "f_max":            196.1e12, | ||||
|         "baud_rate":        32e9, | ||||
|         "spacing":          50e9, | ||||
|         "power_dbm":        0, | ||||
|         "power_range_db":   [0, 0, 0.5], | ||||
|         "roll_off":         0.15, | ||||
|         "tx_osnr":          45, | ||||
|         "sys_margins":      0 | ||||
|     } | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         self.update_attr(self.default_values, kwargs, 'SI') | ||||
|  | ||||
| class Span(common): | ||||
|     default_values = \ | ||||
|     { | ||||
|         'power_mode':                       True, | ||||
|         'delta_power_range_db':             None, | ||||
|         'max_fiber_lineic_loss_for_raman':  0.25, | ||||
|         'target_extended_gain':             2.5, | ||||
|         'max_length':                       150, | ||||
|         'length_units':                     'km', | ||||
|         'max_loss':                         None, | ||||
|         'padding':                          10, | ||||
|         'EOL':                              0, | ||||
|         'con_in':                           0, | ||||
|         'con_out':                          0 | ||||
|     } | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         self.update_attr(self.default_values, kwargs, 'Span') | ||||
|  | ||||
| class Roadm(common): | ||||
|     default_values = \ | ||||
|     { | ||||
|         'target_pch_out_db':   -17, | ||||
|         'add_drop_osnr':       100, | ||||
|         'restrictions': { | ||||
|             'preamp_variety_list':[], | ||||
|             'booster_variety_list':[] | ||||
|             } | ||||
|     } | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         self.update_attr(self.default_values, kwargs, 'Roadm') | ||||
|  | ||||
| class Transceiver(common): | ||||
|     default_values = \ | ||||
|     { | ||||
|         'type_variety': None, | ||||
|         'frequency':    None, | ||||
|         'mode':         {} | ||||
|     } | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         self.update_attr(self.default_values, kwargs, 'Transceiver') | ||||
|  | ||||
| class Fiber(common): | ||||
|     default_values = \ | ||||
|     { | ||||
|         'type_variety':  '', | ||||
|         'dispersion':    None, | ||||
|         'gamma':         0 | ||||
|     } | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         self.update_attr(self.default_values, kwargs, 'Fiber') | ||||
|  | ||||
| class RamanFiber(common): | ||||
|     default_values = \ | ||||
|     { | ||||
|         'type_variety':  '', | ||||
|         'dispersion':    None, | ||||
|         'gamma':         0, | ||||
|         'raman_efficiency': None | ||||
|     } | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         self.update_attr(self.default_values, kwargs, 'RamanFiber') | ||||
|         for param in ('cr', 'frequency_offset'): | ||||
|             if param not in self.raman_efficiency: | ||||
|                 raise EquipmentConfigError(f'RamanFiber.raman_efficiency: missing "{param}" parameter') | ||||
|         if self.raman_efficiency['frequency_offset'] != sorted(self.raman_efficiency['frequency_offset']): | ||||
|             raise EquipmentConfigError(f'RamanFiber.raman_efficiency.frequency_offset is not sorted') | ||||
|  | ||||
| class Amp(common): | ||||
|     default_values = \ | ||||
|     { | ||||
|         'f_min':                191.35e12, | ||||
|         'f_max':                196.1e12, | ||||
|         'type_variety':         '', | ||||
|         'type_def':             '', | ||||
|         'gain_flatmax':         None, | ||||
|         'gain_min':             None, | ||||
|         'p_max':                None, | ||||
|         'nf_model':             None, | ||||
|         'dual_stage_model':     None, | ||||
|         'nf_fit_coeff':         None, | ||||
|         'nf_ripple':            None, | ||||
|         'dgt':                  None, | ||||
|         'gain_ripple':          None, | ||||
|         'out_voa_auto':         False, | ||||
|         'allowed_for_design':   False, | ||||
|         'raman':                False | ||||
|     } | ||||
|  | ||||
|     def __init__(self, **kwargs): | ||||
|         self.update_attr(self.default_values, kwargs, 'Amp') | ||||
|  | ||||
|     @classmethod | ||||
|     def from_advanced_json(cls, filename, **kwargs): | ||||
|         with open(filename, encoding='utf-8') as f: | ||||
|             json_data = load(f) | ||||
|         return cls(**{**kwargs, **json_data, 'type_def':None, 'nf_model':None}) | ||||
|     def from_json(cls, filename, **kwargs): | ||||
|         config = Path(filename).parent / 'default_edfa_config.json' | ||||
|  | ||||
|     @classmethod | ||||
|     def from_default_json(cls, filename, **kwargs): | ||||
|         with open(filename, encoding='utf-8') as f: | ||||
|             json_data = load(f) | ||||
|         type_variety = kwargs['type_variety'] | ||||
|         type_def = kwargs.get('type_def', 'variable_gain') #default compatibility with older json eqpt files | ||||
|         type_def = kwargs.get('type_def', 'variable_gain') # default compatibility with older json eqpt files | ||||
|         nf_def = None | ||||
|         dual_stage_def = None | ||||
|  | ||||
|         if type_def == 'fixed_gain': | ||||
|             try: | ||||
|                 nf0 = kwargs.pop('nf0') | ||||
|             except KeyError: #nf0 is expected for a fixed gain amp | ||||
|                 print(f'missing nf0 value input for amplifier: {type_variety} in eqpt_config.json') | ||||
|                 exit() | ||||
|             try: #remove all remaining nf inputs | ||||
|                 del kwargs['nf_min'] | ||||
|                 del kwargs['nf_max'] | ||||
|             except KeyError: pass #nf_min and nf_max are not needed for fixed gain amp | ||||
|                 raise EquipmentConfigError(f'missing nf0 value input for amplifier: {type_variety} in equipment config') | ||||
|             for k in ('nf_min', 'nf_max'): | ||||
|                 try: | ||||
|                     del kwargs[k] | ||||
|                 except KeyError: | ||||
|                     pass | ||||
|             nf_def = Model_fg(nf0) | ||||
|         elif type_def == 'advanced_model': | ||||
|             config = Path(filename).parent / kwargs.pop('advanced_config_from_json') | ||||
|         elif type_def == 'variable_gain': | ||||
|             gain_min, gain_max = kwargs['gain_min'], kwargs['gain_flatmax'] | ||||
|             try: #nf_min and nf_max are expected for a variable gain amp | ||||
|                 nf_min = kwargs.pop('nf_min') | ||||
|                 nf_max = kwargs.pop('nf_max') | ||||
|             except KeyError: | ||||
|                 print(f'missing nf_min/max value input for amplifier: {type_variety} in eqpt_config.json') | ||||
|                 exit() | ||||
|                 raise EquipmentConfigError(f'missing nf_min or nf_max value input for amplifier: {type_variety} in equipment config') | ||||
|             try: #remove all remaining nf inputs | ||||
|                 del kwargs['nf0'] | ||||
|             except KeyError: pass #nf0 is not needed for variable gain amp | ||||
| @@ -84,19 +188,28 @@ class Amp(AmpBase): | ||||
|             try: | ||||
|                 nf_coef = kwargs.pop('nf_coef') | ||||
|             except KeyError: #nf_coef is expected for openroadm amp | ||||
|                 print(f'missing nf_coef input for amplifier: {type_variety} in eqpt_config.json') | ||||
|                 exit() | ||||
|                 raise EquipmentConfigError(f'missing nf_coef input for amplifier: {type_variety} in equipment config') | ||||
|             nf_def = Model_openroadm(nf_coef) | ||||
|         return cls(**{**kwargs, **json_data, 'nf_model': nf_def}) | ||||
|         elif type_def == 'dual_stage': | ||||
|             try: #nf_ram and gain_ram are expected for a hybrid amp | ||||
|                 preamp_variety = kwargs.pop('preamp_variety') | ||||
|                 booster_variety = kwargs.pop('booster_variety') | ||||
|             except KeyError: | ||||
|                 raise EquipmentConfigError(f'missing preamp/booster variety input for amplifier: {type_variety} in equipment config') | ||||
|             dual_stage_def = Model_dual_stage(preamp_variety, booster_variety) | ||||
|  | ||||
|         with open(config, encoding='utf-8') as f: | ||||
|             json_data = load(f) | ||||
|  | ||||
|         return cls(**{**kwargs, **json_data, | ||||
|             'nf_model': nf_def, 'dual_stage_model': dual_stage_def}) | ||||
|  | ||||
|  | ||||
| def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max): | ||||
|     if nf_min < -10: | ||||
|         print(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}') | ||||
|         exit() | ||||
|         raise EquipmentConfigError(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}') | ||||
|     if nf_max < -10: | ||||
|         print(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}') | ||||
|         exit() | ||||
|         raise EquipmentConfigError(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}') | ||||
|  | ||||
|     # NF estimation model based on nf_min and nf_max | ||||
|     # delta_p:  max power dB difference between first and second stage coils | ||||
| @@ -111,8 +224,7 @@ def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max): | ||||
|     nf1 = lin2db(db2lin(nf_min) - db2lin(nf2)/db2lin(g1a_max)) | ||||
|  | ||||
|     if nf1 < 4: | ||||
|         print(f'First coil value too low {nf1} for amplifier {type_variety}') | ||||
|         exit() | ||||
|         raise EquipmentConfigError(f'First coil value too low {nf1} for amplifier {type_variety}') | ||||
|  | ||||
|     # Check 1 dB < delta_p < 6 dB to ensure nf_min and nf_max values make sense. | ||||
|     # There shouldn't be high nf differences between the two coils: | ||||
| @@ -123,21 +235,18 @@ def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max): | ||||
|         g1a_max = lin2db(db2lin(nf2) / (db2lin(nf_min) - db2lin(nf1))) | ||||
|         delta_p = gain_max - g1a_max | ||||
|         g1a_min = gain_min - (gain_max-gain_min) - delta_p | ||||
|         if not 1 < delta_p < 6: | ||||
|             print(f'Computed \N{greek capital letter delta}P invalid \ | ||||
|         if not 1 < delta_p < 11: | ||||
|             raise EquipmentConfigError(f'Computed \N{greek capital letter delta}P invalid \ | ||||
|                 \n 1st coil vs 2nd coil calculated DeltaP {delta_p:.2f} for \ | ||||
|                 \n amplifier {type_variety} is not valid: revise inputs \ | ||||
|                 \n calculated 1st coil NF = {nf1:.2f}, 2nd coil NF = {nf2:.2f}') | ||||
|             exit() | ||||
|     # Check calculated values for nf1 and nf2 | ||||
|     calc_nf_min = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_max)) | ||||
|     if not isclose(nf_min, calc_nf_min, abs_tol=0.01): | ||||
|         print(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}') | ||||
|         exit() | ||||
|         raise EquipmentConfigError(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}') | ||||
|     calc_nf_max = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_min)) | ||||
|     if not isclose(nf_max, calc_nf_max, abs_tol=0.01): | ||||
|         print(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}') | ||||
|         exit() | ||||
|         raise EquipmentConfigError(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}') | ||||
|  | ||||
|     return nf1, nf2, delta_p | ||||
|  | ||||
| @@ -145,7 +254,7 @@ def edfa_nf(gain_target, variety_type, equipment): | ||||
|     amp_params = equipment['Edfa'][variety_type] | ||||
|     amp = Edfa( | ||||
|             uid = f'calc_NF', | ||||
|             params = amp_params._asdict(), | ||||
|             params = amp_params.__dict__, | ||||
|             operational = { | ||||
|                 'gain_target': gain_target, | ||||
|                 'tilt_target': 0 | ||||
| @@ -172,10 +281,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F | ||||
|             trx_params = {**mode_params} | ||||
|             # sanity check: spacing baudrate must be smaller than min spacing | ||||
|             if trx_params['baud_rate'] > trx_params['min_spacing'] : | ||||
|                 msg = f'Inconsistency in equipment library:\n Transpoder "{trx_type_variety}" mode "{trx_params["format"]}" '+\ | ||||
|                     f'has baud rate: {trx_params["baud_rate"]*1e-9} GHz greater than min_spacing {trx_params["min_spacing"]*1e-9}.' | ||||
|                 print(msg) | ||||
|                 exit() | ||||
|                 raise EquipmentConfigError(f'Inconsistency in equipment library:\n Transpoder "{trx_type_variety}" mode "{trx_params["format"]}" '+\ | ||||
|                     f'has baud rate: {trx_params["baud_rate"]*1e-9} GHz greater than min_spacing {trx_params["min_spacing"]*1e-9}.') | ||||
|         else: | ||||
|             mode_params = {"format": "undetermined", | ||||
|                            "baud_rate": None, | ||||
| @@ -195,9 +302,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F | ||||
|         # print(f'spacing {temp}') | ||||
|     except StopIteration : | ||||
|         if error_message: | ||||
|             print(f'could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library') | ||||
|             print('Computation stopped.') | ||||
|             exit() | ||||
|             raise EquipmentConfigError(f'Computation stoped: could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library') | ||||
|         else: | ||||
|             # default transponder charcteristics | ||||
|             # mainly used with transmission_main_example.py | ||||
| @@ -222,8 +327,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F | ||||
| def automatic_spacing(baud_rate): | ||||
|     """return the min possible channel spacing for a given baud rate""" | ||||
|     # TODO : this should parametrized in a cfg file | ||||
|     spacing_list = [(33e9,37.5e9), (38e9,50e9), (50e9,62.5e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples | ||||
|                                                 #[(max_baud_rate, spacing_for_this_baud_rate)] | ||||
|     # list of possible tuples [(max_baud_rate, spacing_for_this_baud_rate)] | ||||
|     spacing_list = [(33e9, 37.5e9), (38e9, 50e9), (50e9, 62.5e9), (67e9, 75e9), (92e9, 100e9)] | ||||
|     return min((s[1] for s in spacing_list if s[0] > baud_rate), default=baud_rate*1.2) | ||||
|  | ||||
| def automatic_nch(f_min, f_max, spacing): | ||||
| @@ -243,6 +348,34 @@ def update_trx_osnr(equipment): | ||||
|             m['OSNR'] = m['OSNR'] + equipment['SI']['default'].sys_margins | ||||
|     return equipment | ||||
|  | ||||
| def update_dual_stage(equipment): | ||||
|     edfa_dict = equipment['Edfa'] | ||||
|     for edfa in edfa_dict.values(): | ||||
|         if edfa.type_def == 'dual_stage': | ||||
|             edfa_preamp = edfa_dict[edfa.dual_stage_model.preamp_variety] | ||||
|             edfa_booster = edfa_dict[edfa.dual_stage_model.booster_variety] | ||||
|             for key, value in edfa_preamp.__dict__.items(): | ||||
|                 attr_k = 'preamp_' + key | ||||
|                 setattr(edfa, attr_k, value) | ||||
|             for key, value in edfa_booster.__dict__.items(): | ||||
|                 attr_k = 'booster_' + key | ||||
|                 setattr(edfa, attr_k, value) | ||||
|             edfa.p_max = edfa_booster.p_max | ||||
|             edfa.gain_flatmax = edfa_booster.gain_flatmax + edfa_preamp.gain_flatmax | ||||
|             if edfa.gain_min < edfa_preamp.gain_min: | ||||
|                 raise EquipmentConfigError(f'Dual stage {edfa.type_variety} min gain is lower than its preamp min gain') | ||||
|     return equipment | ||||
|  | ||||
| def roadm_restrictions_sanity_check(equipment): | ||||
|     """ verifies that booster and preamp restrictions specified in roadm equipment are listed | ||||
|     in the edfa. | ||||
|     """ | ||||
|     restrictions = equipment['Roadm']['default'].restrictions['booster_variety_list'] + \ | ||||
|         equipment['Roadm']['default'].restrictions['preamp_variety_list'] | ||||
|     for amp_name in restrictions: | ||||
|         if amp_name not in equipment['Edfa']: | ||||
|             raise EquipmentConfigError(f'ROADM restriction {amp_name} does not refer to a defined EDFA name') | ||||
|  | ||||
| def equipment_from_json(json_data, filename): | ||||
|     """build global dictionnary eqpt_library that stores all eqpt characteristics: | ||||
|     edfa type type_variety, fiber type_variety | ||||
| @@ -259,13 +392,10 @@ def equipment_from_json(json_data, filename): | ||||
|         for entry in entries: | ||||
|             subkey = entry.get('type_variety', 'default') | ||||
|             if key == 'Edfa': | ||||
|                 if 'advanced_config_from_json' in entry: | ||||
|                     config = Path(filename).parent / entry.pop('advanced_config_from_json') | ||||
|                     equipment[key][subkey] = Amp.from_advanced_json(config, **entry) | ||||
|                 else: | ||||
|                     config = Path(filename).parent / 'default_edfa_config.json' | ||||
|                     equipment[key][subkey] = Amp.from_default_json(config, **entry) | ||||
|                 equipment[key][subkey] = Amp.from_json(filename, **entry) | ||||
|             else: | ||||
|                 equipment[key][subkey] = typ(**entry) | ||||
|     equipment = update_trx_osnr(equipment) | ||||
|     equipment = update_dual_stage(equipment) | ||||
|     roadm_restrictions_sanity_check(equipment) | ||||
|     return equipment | ||||
|   | ||||
							
								
								
									
										29
									
								
								gnpy/core/exceptions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								gnpy/core/exceptions.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| ''' | ||||
| gnpy.core.exceptions | ||||
| ==================== | ||||
|  | ||||
| Exceptions thrown by other gnpy modules | ||||
| ''' | ||||
|  | ||||
|  | ||||
| class ConfigurationError(Exception): | ||||
|     '''User-provided configuration contains an error''' | ||||
|  | ||||
| class EquipmentConfigError(ConfigurationError): | ||||
|     '''Incomplete or wrong configuration within the equipment library''' | ||||
|  | ||||
| class NetworkTopologyError(ConfigurationError): | ||||
|     '''Topology of user-provided network is wrong''' | ||||
|  | ||||
| class ServiceError(Exception): | ||||
|     '''Service of user-provided request is wrong''' | ||||
|  | ||||
| class DisjunctionError(ServiceError): | ||||
|     '''Disjunction of user-provided request can not be satisfied''' | ||||
|  | ||||
| class SpectrumError(Exception): | ||||
|     '''Spectrum errors of the program''' | ||||
|  | ||||
| @@ -5,7 +5,7 @@ | ||||
| gnpy.core.info | ||||
| ============== | ||||
|  | ||||
| This module contains classes for modelling SpectralInformation. | ||||
| This module contains classes for modelling :class:`SpectralInformation`. | ||||
| ''' | ||||
|  | ||||
|  | ||||
| @@ -16,53 +16,34 @@ from json import loads | ||||
| from gnpy.core.utils import load_json | ||||
| from gnpy.core.equipment import automatic_nch, automatic_spacing | ||||
|  | ||||
| class ConvenienceAccess: | ||||
|  | ||||
|     def __init_subclass__(cls): | ||||
|         for abbrev, field in getattr(cls, '_ABBREVS', {}).items(): | ||||
|             setattr(cls, abbrev, property(lambda self, f=field: getattr(self, f))) | ||||
|  | ||||
|     def update(self, **kwargs): | ||||
|         for abbrev, field in getattr(self, '_ABBREVS', {}).items(): | ||||
|             if abbrev in kwargs: | ||||
|                 kwargs[field] = kwargs.pop(abbrev) | ||||
|         return self._replace(**kwargs) | ||||
| class Power(namedtuple('Power', 'signal nli ase')): | ||||
|     """carriers power in W""" | ||||
|  | ||||
|  | ||||
| class Power(namedtuple('Power', 'signal nonlinear_interference amplified_spontaneous_emission'), ConvenienceAccess): | ||||
|  | ||||
|     _ABBREVS = {'nli': 'nonlinear_interference', | ||||
|                 'ase': 'amplified_spontaneous_emission',} | ||||
| class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power')): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power'), ConvenienceAccess): | ||||
| class Pref(namedtuple('Pref', 'p_span0, p_spani, neq_ch ')): | ||||
|     """noiseless reference power in dBm:  | ||||
|     p_span0: inital target carrier power | ||||
|     p_spani: carrier power after element i | ||||
|     neq_ch: equivalent channel count in dB""" | ||||
|  | ||||
|     _ABBREVS = {'channel':  'channel_number', | ||||
|                 'num_chan': 'channel_number', | ||||
|                 'ffs':      'frequency', | ||||
|                 'freq':     'frequency',} | ||||
|  | ||||
| class Pref(namedtuple('Pref', 'p_span0, p_spani'), ConvenienceAccess): | ||||
| class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers')): | ||||
|  | ||||
|     _ABBREVS = {'p0' :  'p_span0', | ||||
|                 'pi' :  'p_spani'} | ||||
|  | ||||
| class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers'), ConvenienceAccess): | ||||
|  | ||||
|     def __new__(cls, pref=Pref(0, 0), *carriers): | ||||
|     def __new__(cls, pref, carriers): | ||||
|         return super().__new__(cls, pref, carriers) | ||||
|  | ||||
| def merge_input_spectral_information(*si): | ||||
|     """mix channel combs of different baud rates and power""" | ||||
|     #TODO | ||||
|     pass | ||||
|  | ||||
| def create_input_spectral_information(f_min, f_max, roll_off, baud_rate, power, spacing): | ||||
|     # pref in dB : convert power lin into power in dB | ||||
|     pref = lin2db(power * 1e3) | ||||
|     si = SpectralInformation(pref=Pref(pref, pref)) | ||||
|     nb_channel = automatic_nch(f_min, f_max, spacing) | ||||
|     si = si.update(carriers=[ | ||||
|     si = SpectralInformation( | ||||
|         pref=Pref(pref, pref, lin2db(nb_channel)), | ||||
|         carriers=[ | ||||
|             Channel(f, (f_min+spacing*f), | ||||
|             baud_rate, roll_off, Power(power, 0, 0)) for f in range(1,nb_channel+1) | ||||
|             ]) | ||||
| @@ -81,11 +62,11 @@ if __name__ == '__main__': | ||||
|     si = SpectralInformation() | ||||
|     spacing = 0.05 # THz | ||||
|  | ||||
|     si = si.update(carriers=tuple(Channel(f+1, 191.3+spacing*(f+1), 32e9, 0.15, Power(1e-3, f, 1)) for f in range(96))) | ||||
|     si = si._replace(carriers=tuple(Channel(f+1, 191.3+spacing*(f+1), 32e9, 0.15, Power(1e-3, f, 1)) for f in range(96))) | ||||
|  | ||||
|     print(f'si = {si}') | ||||
|     print(f'si = {si.carriers[0].power.nli}') | ||||
|     print(f'si = {si.carriers[20].power.nli}') | ||||
|     si2 = si.update(carriers=tuple(c.update(power = c.power.update(nli = c.power.nli * 1e5)) | ||||
|     si2 = si._replace(carriers=tuple(c._replace(power = c.power._replace(nli = c.power.nli * 1e5)) | ||||
|                               for c in si.carriers)) | ||||
|     print(f'si2 = {si2}') | ||||
|   | ||||
| @@ -14,13 +14,15 @@ from numpy import arange | ||||
| from scipy.interpolate import interp1d | ||||
| from logging import getLogger | ||||
| from os import path | ||||
| from operator import itemgetter | ||||
| from operator import itemgetter, attrgetter | ||||
| from gnpy.core import elements | ||||
| from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused | ||||
| from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused, RamanFiber | ||||
| from gnpy.core.equipment import edfa_nf | ||||
| from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError | ||||
| from gnpy.core.units import UNITS | ||||
| from gnpy.core.utils import load_json, save_json, round2float, db2lin, lin2db | ||||
| from sys import exit | ||||
| from gnpy.core.utils import (load_json, save_json, round2float, db2lin, | ||||
|                             merge_amplifier_restrictions) | ||||
| from gnpy.core.science_utils import SimParams | ||||
| from collections import namedtuple | ||||
|  | ||||
| logger = getLogger(__name__) | ||||
| @@ -52,11 +54,12 @@ def network_from_json(json_data, equipment): | ||||
|         variety = el_config.pop('type_variety', 'default') | ||||
|         if typ in equipment and variety in equipment[typ]: | ||||
|             extra_params = equipment[typ][variety] | ||||
|             el_config.setdefault('params', {}).update(extra_params._asdict()) | ||||
|         elif typ in ['Edfa', 'Fiber']: #catch it now because the code will crash later! | ||||
|             print( f'The {typ} of variety type {variety} was not recognized:' | ||||
|             temp = el_config.setdefault('params', {}) | ||||
|             temp = merge_amplifier_restrictions(temp, extra_params.__dict__) | ||||
|             el_config['params'] = temp | ||||
|         elif typ in ['Edfa', 'Fiber']: # catch it now because the code will crash later! | ||||
|             raise ConfigurationError(f'The {typ} of variety type {variety} was not recognized:' | ||||
|                     '\nplease check it is properly defined in the eqpt_config json file') | ||||
|             exit() | ||||
|         cls = getattr(elements, typ) | ||||
|         el = cls(**el_config) | ||||
|         g.add_node(el) | ||||
| @@ -66,11 +69,13 @@ def network_from_json(json_data, equipment): | ||||
|     for cx in json_data['connections']: | ||||
|         from_node, to_node = cx['from_node'], cx['to_node'] | ||||
|         try: | ||||
|             g.add_edge(nodes[from_node], nodes[to_node]) | ||||
|             if isinstance(nodes[from_node], Fiber): | ||||
|                 edge_length = nodes[from_node].params.length | ||||
|             else: | ||||
|                 edge_length = 0.01 | ||||
|             g.add_edge(nodes[from_node], nodes[to_node], weight = edge_length) | ||||
|         except KeyError: | ||||
|             msg = f'In {__name__} network_from_json function:\n\tcan not find {from_node} or {to_node} defined in {cx}' | ||||
|             print(msg) | ||||
|             exit(1) | ||||
|             raise NetworkTopologyError(f'can not find {from_node} or {to_node} defined in {cx}') | ||||
|  | ||||
|     return g | ||||
|  | ||||
| @@ -87,16 +92,27 @@ def network_to_json(network): | ||||
|     data.update(connections) | ||||
|     return data | ||||
|  | ||||
| def select_edfa(gain_target, power_target, equipment): | ||||
| def select_edfa(raman_allowed, gain_target, power_target, equipment, uid, restrictions=None): | ||||
|     """amplifer selection algorithm | ||||
|     @Orange Jean-Luc Augé | ||||
|     """ | ||||
|     Edfa_list = namedtuple('Edfa_list', 'variety power gain nf') | ||||
|     TARGET_EXTENDED_GAIN = 2.1 | ||||
|     #MAX_EXTENDED_GAIN = 5 | ||||
|     edfa_dict = equipment['Edfa'] | ||||
|     Edfa_list = namedtuple('Edfa_list', 'variety power gain_min nf') | ||||
|     TARGET_EXTENDED_GAIN = equipment['Span']['default'].target_extended_gain | ||||
|  | ||||
|     # for roadm restriction only: create a dict including not allowed for design amps | ||||
|     # because main use case is to have specific radm amp which are not allowed for ILA | ||||
|     # with the auto design | ||||
|     edfa_dict = {name: amp for (name, amp) in equipment['Edfa'].items() | ||||
|         if restrictions is None or name in restrictions} | ||||
|  | ||||
|     pin = power_target - gain_target | ||||
|  | ||||
|     # create 2 list of available amplifiers with relevant attributes for their selection | ||||
|  | ||||
|     # edfa list with: | ||||
|     # extended gain min allowance of 3dB: could be parametrized, but a bit complex | ||||
|     # extended gain max allowance TARGET_EXTENDED_GAIN is coming from eqpt_config.json | ||||
|     # power attribut include power AND gain limitations | ||||
|     edfa_list = [Edfa_list( | ||||
|                 variety=edfa_variety, | ||||
|                 power=min( | ||||
| @@ -106,88 +122,118 @@ def select_edfa(gain_target, power_target, equipment): | ||||
|                     edfa.p_max | ||||
|                     ) | ||||
|                     -power_target, | ||||
|                 gain=edfa.gain_flatmax-gain_target, | ||||
|                 gain_min= | ||||
|                     gain_target+3 | ||||
|                     -edfa.gain_min, | ||||
|                 nf=edfa_nf(gain_target, edfa_variety, equipment)) \ | ||||
|                 for edfa_variety, edfa in edfa_dict.items() | ||||
|                 if edfa.allowed_for_design] | ||||
|                 if ((edfa.allowed_for_design or restrictions is not None) and not edfa.raman)] | ||||
|  | ||||
|     acceptable_gain_list = \ | ||||
|     list(filter(lambda x : x.gain>-TARGET_EXTENDED_GAIN, edfa_list)) | ||||
|     if len(acceptable_gain_list) < 1: | ||||
|         #no amplifier satisfies the required gain, so pick the highest gain: | ||||
|         gain_max = max(edfa_list, key=itemgetter(2)).gain | ||||
|         #pick up all amplifiers that share this max gain: | ||||
|         acceptable_gain_list = \ | ||||
|         list(filter(lambda x : x.gain-gain_max>-0.1, edfa_list)) | ||||
|     acceptable_power_list = \ | ||||
|     list(filter(lambda x : x.power>=0, acceptable_gain_list)) | ||||
|     #consider a Raman list because of different gain_min requirement:  | ||||
|     #do not allow extended gain min for Raman | ||||
|     raman_list = [Edfa_list( | ||||
|                 variety=edfa_variety, | ||||
|                 power=min( | ||||
|                     pin | ||||
|                     +edfa.gain_flatmax | ||||
|                     +TARGET_EXTENDED_GAIN, | ||||
|                     edfa.p_max | ||||
|                     ) | ||||
|                     -power_target, | ||||
|                 gain_min= | ||||
|                     gain_target | ||||
|                     -edfa.gain_min, | ||||
|                 nf=edfa_nf(gain_target, edfa_variety, equipment)) | ||||
|                 for edfa_variety, edfa in edfa_dict.items() | ||||
|                 if (edfa.allowed_for_design and edfa.raman)] \ | ||||
|                 if raman_allowed else [] | ||||
|  | ||||
|     #merge raman and edfa lists | ||||
|     amp_list = edfa_list + raman_list | ||||
|  | ||||
|     #filter on min gain limitation:  | ||||
|     acceptable_gain_min_list = [x for x in amp_list if x.gain_min>0] | ||||
|  | ||||
|     if len(acceptable_gain_min_list) < 1: | ||||
|         #do not take this empty list into account for the rest of the code | ||||
|         #but issue a warning to the user and do not consider Raman | ||||
|         #Raman below min gain should not be allowed because i is meant to be a design requirement | ||||
|         #and raman padding at the amplifier input is impossible! | ||||
|  | ||||
|         if len(edfa_list) < 1: | ||||
|             raise ConfigurationError(f'auto_design could not find any amplifier \ | ||||
|                     to satisfy min gain requirement in node {uid} \ | ||||
|                     please increase span fiber padding') | ||||
|         else: | ||||
|             # TODO: convert to logging | ||||
|             print( | ||||
|                 f'\x1b[1;31;40m'\ | ||||
|                 + f'WARNING: target gain in node {uid} is below all available amplifiers min gain: \ | ||||
|                     amplifier input padding will be assumed, consider increase span fiber padding instead'\ | ||||
|                 + '\x1b[0m' | ||||
|                 ) | ||||
|             acceptable_gain_min_list = edfa_list | ||||
|  | ||||
|     #filter on gain+power limitation: | ||||
|     #this list checks both the gain and the power requirement | ||||
|     #because of the way .power is calculated in the list | ||||
|     acceptable_power_list = [x for x in acceptable_gain_min_list if x.power>0] | ||||
|     if len(acceptable_power_list) < 1: | ||||
|         #no amplifier satisfies the required power, so pick the highest power: | ||||
|         power_max = \ | ||||
|         max(acceptable_gain_list, key=itemgetter(1)).power | ||||
|         #pick up all amplifiers that share this max gain: | ||||
|         acceptable_power_list = \ | ||||
|         list(filter(lambda x : x.power-power_max>-0.1, acceptable_gain_list)) | ||||
|         #no amplifier satisfies the required power, so pick the highest power(s): | ||||
|         power_max = max(acceptable_gain_min_list, key=attrgetter('power')).power | ||||
|         #check and pick if other amplifiers may have a similar gain/power | ||||
|         #allow a 0.3dB power range  | ||||
|         #this allows to chose an amplifier with a better NF subsequentely | ||||
|         acceptable_power_list = [x for x in acceptable_gain_min_list | ||||
|                                  if x.power-power_max>-0.3] | ||||
|  | ||||
|      | ||||
|     # gain and power requirements are resolved, | ||||
|     #       =>chose the amp with the best NF among the acceptable ones: | ||||
|     return min(acceptable_power_list, key=itemgetter(3)).variety #filter on NF | ||||
|     selected_edfa = min(acceptable_power_list, key=attrgetter('nf')) #filter on NF | ||||
|     #check what are the gain and power limitations of this amp | ||||
|     power_reduction = round(min(selected_edfa.power, 0),2) | ||||
|     if power_reduction < -0.5: | ||||
|         print( | ||||
|             f'\x1b[1;31;40m'\ | ||||
|             + f'WARNING: target gain and power in node {uid}\n \ | ||||
|     is beyond all available amplifiers capabilities and/or extended_gain_range:\n\ | ||||
|     a power reduction of {power_reduction} is applied\n'\ | ||||
|             + '\x1b[0m' | ||||
|             ) | ||||
|  | ||||
|  | ||||
| def set_roadm_loss(network, equipment, pref_ch_db): | ||||
|     roadms = [roadm for roadm in network if isinstance(roadm, Roadm)] | ||||
|     power_mode = equipment['Spans']['default'].power_mode | ||||
|     default_roadm_loss = equipment['Roadms']['default'].gain_mode_default_loss | ||||
|     pout_target = equipment['Roadms']['default'].power_mode_pout_target | ||||
|     roadm_loss = pref_ch_db - pout_target | ||||
|     return selected_edfa.variety, power_reduction | ||||
|  | ||||
|     for roadm in roadms: | ||||
|         if power_mode: | ||||
|             roadm.loss = roadm_loss | ||||
|             roadm.target_pch_out_db = pout_target | ||||
|         elif roadm.loss == None: | ||||
|             roadm.loss = default_roadm_loss | ||||
|  | ||||
| def target_power(dp_from_gain, network, node, equipment): #get_fiber_dp | ||||
| def target_power(network, node, equipment): #get_fiber_dp | ||||
|     SPAN_LOSS_REF = 20 | ||||
|     POWER_SLOPE = 0.3 | ||||
|     power_mode = equipment['Spans']['default'].power_mode | ||||
|     dp_range = list(equipment['Spans']['default'].delta_power_range_db) | ||||
|     power_mode = equipment['Span']['default'].power_mode | ||||
|     dp_range = list(equipment['Span']['default'].delta_power_range_db) | ||||
|     node_loss = span_loss(network, node) | ||||
|  | ||||
|     dp_gain_mode = 0 | ||||
|     try: | ||||
|         dp_power_mode = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2]) | ||||
|         dp_power_mode = max(dp_range[0], dp_power_mode) | ||||
|         dp_power_mode = min(dp_range[1], dp_power_mode) | ||||
|         dp = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2]) | ||||
|         dp = max(dp_range[0], dp) | ||||
|         dp = min(dp_range[1], dp) | ||||
|     except KeyError: | ||||
|         print(f'invalid delta_power_range_db definition in eqpt_config[Spans]' | ||||
|         raise ConfigurationError(f'invalid delta_power_range_db definition in eqpt_config[Span]' | ||||
|               f'delta_power_range_db: [lower_bound, upper_bound, step]') | ||||
|         exit() | ||||
|  | ||||
|     if dp_from_gain: | ||||
|         dp_power_mode = dp_from_gain | ||||
|         dp_gain_mode = dp_from_gain | ||||
|     if isinstance(node, Roadm): | ||||
|         dp_power_mode = 0 | ||||
|  | ||||
|     dp = dp_power_mode if power_mode else dp_gain_mode | ||||
|     #print(f'{repr(node)} delta power in:\n{dp}dB') | ||||
|         dp = 0 | ||||
|  | ||||
|     return dp | ||||
|  | ||||
|  | ||||
| def prev_node_generator(network, node): | ||||
|     """fused spans interest: | ||||
|     iterate over all predecessors while they are Fused or Fiber type""" | ||||
|     try: | ||||
|         prev_node = next(n for n in network.predecessors(node)) | ||||
|     except StopIteration: | ||||
|         msg = f'In {__name__} prev_node_generator function:\n\t{node.uid} is not properly connected, please check network topology' | ||||
|         print(msg) | ||||
|         logger.critical(msg) | ||||
|         exit(1) | ||||
|         raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology') | ||||
|     # yield and re-iterate | ||||
|     if isinstance(prev_node, Fused) or isinstance(node, Fused): | ||||
|     if isinstance(prev_node, Fused) or isinstance(node, Fused) and not isinstance(prev_node, Roadm): | ||||
|         yield prev_node | ||||
|         yield from prev_node_generator(network, prev_node) | ||||
|     else: | ||||
| @@ -199,10 +245,9 @@ def next_node_generator(network, node): | ||||
|     try: | ||||
|         next_node = next(n for n in network.successors(node)) | ||||
|     except StopIteration: | ||||
|         print(f'In {__name__} next_node_generator function:\n\t{node.uid}  is not properly connected, please check network topology') | ||||
|         exit(1)         | ||||
|         raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology') | ||||
|     # yield and re-iterate | ||||
|     if isinstance(next_node, Fused) or isinstance(node, Fused): | ||||
|     if isinstance(next_node, Fused) or isinstance(node, Fused) and not isinstance(next_node, Roadm): | ||||
|         yield next_node | ||||
|         yield from next_node_generator(network, next_node) | ||||
|     else: | ||||
| @@ -244,23 +289,22 @@ def find_last_node(network, node): | ||||
|         pass | ||||
|     return this_node | ||||
|  | ||||
| def set_amplifier_voa(amp, pref_total_db, power_mode): | ||||
|     VOA_MARGIN = 0 | ||||
|     if amp.operational.out_voa is None: | ||||
| def set_amplifier_voa(amp, power_target, power_mode): | ||||
|     VOA_MARGIN = 1 #do not maximize the VOA optimization | ||||
|     if amp.out_voa is None: | ||||
|         if power_mode: | ||||
|             gain_target = amp.operational.gain_target | ||||
|             pout = pref_total_db + amp.dp_db | ||||
|             voa = min(amp.params.p_max-pout, | ||||
|                       amp.params.gain_flatmax-amp.operational.gain_target) | ||||
|             voa = round2float(max(voa, 0), 0.5) - VOA_MARGIN if amp.params.out_voa_auto else 0 | ||||
|             amp.dp_db = amp.dp_db + voa | ||||
|             amp.operational.gain_target = amp.operational.gain_target + voa | ||||
|             gain_target = amp.effective_gain | ||||
|             voa = min(amp.params.p_max-power_target, | ||||
|                       amp.params.gain_flatmax-amp.effective_gain) | ||||
|             voa = max(round2float(max(voa, 0), 0.5) - VOA_MARGIN, 0) if amp.params.out_voa_auto else 0 | ||||
|             amp.delta_p = amp.delta_p + voa | ||||
|             amp.effective_gain = amp.effective_gain + voa | ||||
|         else: | ||||
|             voa = 0 # no output voa optimization in gain mode | ||||
|         amp.operational.out_voa = voa | ||||
|         amp.out_voa = voa | ||||
|  | ||||
| def set_egress_amplifier(network, roadm, equipment, pref_total_db): | ||||
|     power_mode = equipment['Spans']['default'].power_mode | ||||
|     power_mode = equipment['Span']['default'].power_mode | ||||
|     next_oms = (n for n in network.successors(roadm) if not isinstance(n, Transceiver)) | ||||
|     for oms in next_oms: | ||||
|         #go through all the OMS departing from the Roadm | ||||
| @@ -271,30 +315,82 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db): | ||||
|         #     node = find_last_node(next_node) | ||||
|         #     next_node = next(n for n in network.successors(node)) | ||||
|         #     next_node = find_last_node(next_node) | ||||
|         prev_dp = 0 | ||||
|         dp = 0 | ||||
|          | ||||
|         if node.per_degree_target_pch_out_db: | ||||
|             # find the target power on this degree | ||||
|             try: | ||||
|                 prev_dp = next(el["target_pch_out_db"] for el in \ | ||||
|                     node.per_degree_target_pch_out_db if el["to_node"]==next_node.uid) | ||||
|             except StopIteration: | ||||
|                 # if no target power is defined on this degree use the global one | ||||
|                 prev_dp = getattr(node.params, 'target_pch_out_db', 0) | ||||
|         else: | ||||
|             # if no per degree target power is given use the global one | ||||
|             prev_dp = getattr(node.params, 'target_pch_out_db', 0) | ||||
|         dp = prev_dp | ||||
|         prev_voa = 0 | ||||
|         voa = 0 | ||||
|         while True: | ||||
|         #go through all nodes in the OMS (loop until next Roadm instance) | ||||
|             if isinstance(node, Edfa): | ||||
|                 node_loss = span_loss(network, prev_node) | ||||
|                 dp_from_gain = prev_dp + node.operational.gain_target - node_loss \ | ||||
|                     if node.operational.gain_target > 0 else None | ||||
|                 dp = target_power(dp_from_gain, network, next_node, equipment) | ||||
|                 gain_target = node_loss + dp - prev_dp | ||||
|                 voa = node.out_voa if node.out_voa else 0 | ||||
|                 if node.delta_p is None: | ||||
|                     dp = target_power(network, next_node, equipment) | ||||
|                 else: | ||||
|                     dp = node.delta_p | ||||
|                 gain_from_dp = node_loss + dp - prev_dp + prev_voa | ||||
|                 if node.effective_gain is None or power_mode: | ||||
|                     gain_target = gain_from_dp | ||||
|                 else: #gain mode with effective_gain  | ||||
|                     gain_target = node.effective_gain | ||||
|                     dp = prev_dp - node_loss + gain_target | ||||
|  | ||||
|                 if power_mode: | ||||
|                     node.dp_db = dp | ||||
|                 node.operational.gain_target = gain_target | ||||
|                 power_target = pref_total_db + dp          | ||||
|  | ||||
|                 raman_allowed = False | ||||
|                 if isinstance(prev_node, Fiber): | ||||
|                     max_fiber_lineic_loss_for_raman = \ | ||||
|                             equipment['Span']['default'].max_fiber_lineic_loss_for_raman | ||||
|                     raman_allowed = prev_node.params.loss_coef < max_fiber_lineic_loss_for_raman | ||||
|  | ||||
|                 # implementation of restrictions on roadm boosters | ||||
|                 if isinstance(prev_node,Roadm): | ||||
|                     if prev_node.restrictions['booster_variety_list']: | ||||
|                         restrictions = prev_node.restrictions['booster_variety_list'] | ||||
|                     else: | ||||
|                         restrictions = None | ||||
|                 elif isinstance(next_node,Roadm): | ||||
|                     # implementation of restrictions on roadm preamp | ||||
|                     if next_node.restrictions['preamp_variety_list']: | ||||
|                         restrictions = next_node.restrictions['preamp_variety_list'] | ||||
|                     else: | ||||
|                         restrictions = None | ||||
|                 else: | ||||
|                     restrictions = None | ||||
|  | ||||
|                 if node.params.type_variety == '':                    | ||||
|                     power_target = pref_total_db + dp | ||||
|                     edfa_variety = select_edfa(gain_target, power_target, equipment) | ||||
|                     edfa_variety, power_reduction = select_edfa(raman_allowed,  | ||||
|                                    gain_target, power_target, equipment, node.uid, restrictions) | ||||
|                     extra_params = equipment['Edfa'][edfa_variety] | ||||
|                     node.params.update_params(extra_params._asdict()) | ||||
|                 set_amplifier_voa(node, pref_total_db, power_mode) | ||||
|                     node.params.update_params(extra_params.__dict__) | ||||
|                     dp += power_reduction | ||||
|                     gain_target += power_reduction | ||||
|                 elif node.params.raman and not raman_allowed: | ||||
|                     print( | ||||
|                         f'\x1b[1;31;40m'\ | ||||
|                         + f'WARNING: raman is used in node {node.uid}\n \ | ||||
|                 but fiber lineic loss is above threshold\n'\ | ||||
|                         + '\x1b[0m' | ||||
|                         )                     | ||||
|                                  | ||||
|                 node.delta_p = dp if power_mode else None | ||||
|                 node.effective_gain = gain_target | ||||
|                 set_amplifier_voa(node, power_target, power_mode) | ||||
|             if isinstance(next_node, Roadm) or isinstance(next_node, Transceiver): | ||||
|                 break | ||||
|             prev_dp = dp | ||||
|             prev_voa = voa | ||||
|             prev_node = node | ||||
|             node = next_node | ||||
|             # print(f'{node.uid}') | ||||
| @@ -319,12 +415,16 @@ def add_egress_amplifier(network, node): | ||||
|                         } | ||||
|                     }, | ||||
|                     operational = { | ||||
|                         'gain_target': 0, | ||||
|                         'gain_target': None, | ||||
|                         'tilt_target': 0, | ||||
|                     }) | ||||
|         network.add_node(amp) | ||||
|         network.add_edge(node, amp) | ||||
|         network.add_edge(amp, next_node) | ||||
|         if isinstance(node,Fiber): | ||||
|             edgeweight = node.params.length | ||||
|         else: | ||||
|             edgeweight = 0.01 | ||||
|         network.add_edge(node, amp, weight = edgeweight) | ||||
|         network.add_edge(amp, next_node, weight = 0.01) | ||||
|  | ||||
|  | ||||
| def calculate_new_length(fiber_length, bounds, target_length): | ||||
| @@ -360,9 +460,7 @@ def split_fiber(network, fiber, bounds, target_length, equipment): | ||||
|         next_node = next(network.successors(fiber)) | ||||
|         prev_node = next(network.predecessors(fiber)) | ||||
|     except StopIteration: | ||||
|  | ||||
|         print(f'In {__name__} split_fiber function:\n\t{fiber.uid}   is not properly connected, please check network topology') | ||||
|         exit() | ||||
|         raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology') | ||||
|  | ||||
|     network.remove_node(fiber) | ||||
|  | ||||
| @@ -385,17 +483,25 @@ def split_fiber(network, fiber, bounds, target_length, equipment): | ||||
|                             } | ||||
|                           }, | ||||
|                           params = fiber_params) | ||||
|         network.add_edge(prev_node, new_span) | ||||
|         prev_node = new_span | ||||
|     network.add_edge(prev_node, next_node) | ||||
|  | ||||
| def add_connector_loss(fibers, con_in, con_out, EOL): | ||||
|     for fiber in fibers: | ||||
|         if fiber.con_in is None: fiber.con_in = con_in | ||||
|         if fiber.con_out is None: | ||||
|             fiber.con_out = con_out #con_out includes EOL | ||||
|         if isinstance(prev_node,Fiber): | ||||
|             edgeweight = prev_node.params.length | ||||
|         else: | ||||
|             fiber.con_out = fiber.con_out+EOL | ||||
|             edgeweight = 0.01 | ||||
|         network.add_edge(prev_node, new_span, weight = edgeweight) | ||||
|         prev_node = new_span | ||||
|     if isinstance(prev_node,Fiber): | ||||
|         edgeweight = prev_node.params.length | ||||
|     else: | ||||
|         edgeweight = 0.01     | ||||
|     network.add_edge(prev_node, next_node, weight = edgeweight) | ||||
|  | ||||
| def add_connector_loss(network, fibers, default_con_in, default_con_out, EOL): | ||||
|     for fiber in fibers: | ||||
|         if fiber.con_in is None: fiber.con_in = default_con_in | ||||
|         if fiber.con_out is None: fiber.con_out = default_con_out | ||||
|         next_node = next(n for n in network.successors(fiber)) | ||||
|         if not isinstance(next_node, Fused): | ||||
|             fiber.con_out += EOL | ||||
|  | ||||
| def add_fiber_padding(network, fibers, padding): | ||||
|     """last_fibers = (fiber for n in network.nodes() | ||||
| @@ -407,33 +513,32 @@ def add_fiber_padding(network, fibers, padding): | ||||
|         try: | ||||
|             next_node = next(network.successors(fiber)) | ||||
|         except StopIteration: | ||||
|             msg = f'In {__name__} add_fiber_padding function:\n\t{fiber.uid}   is not properly connected, please check network topology' | ||||
|             print(msg) | ||||
|             logger.critical(msg) | ||||
|             exit(1)             | ||||
|             raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology') | ||||
|         if this_span_loss < padding and not (isinstance(next_node, Fused)): | ||||
|             #add a padding att_in at the input of the 1st fiber: | ||||
|             #address the case when several fibers are spliced together | ||||
|             first_fiber = find_first_node(network, fiber) | ||||
|             # in order to support no booster , fused might be placed | ||||
|             # just after a roadm: need to check that first_fiber is really a fiber | ||||
|             if isinstance(first_fiber,Fiber): | ||||
|                 if first_fiber.att_in is None: | ||||
|                     first_fiber.att_in = padding - this_span_loss | ||||
|             else : | ||||
|                 else: | ||||
|                     first_fiber.att_in = first_fiber.att_in + padding - this_span_loss | ||||
|  | ||||
| def build_network(network, equipment, pref_ch_db, pref_total_db): | ||||
|     default_span_data = equipment['Spans']['default'] | ||||
|     default_span_data = equipment['Span']['default'] | ||||
|     max_length = int(default_span_data.max_length * UNITS[default_span_data.length_units]) | ||||
|     min_length = max(int(default_span_data.padding/0.2*1e3),50_000) | ||||
|     bounds = range(min_length, max_length) | ||||
|     target_length = max(min_length, 90_000) | ||||
|     con_in = default_span_data.con_in | ||||
|     con_out = default_span_data.con_out + default_span_data.EOL | ||||
|     default_con_in = default_span_data.con_in | ||||
|     default_con_out = default_span_data.con_out | ||||
|     padding = default_span_data.padding | ||||
|  | ||||
|     #set raodm loss for gain_mode before to build network | ||||
|     set_roadm_loss(network, equipment, pref_ch_db) | ||||
|     #set roadm loss for gain_mode before to build network | ||||
|     fibers = [f for f in network.nodes() if isinstance(f, Fiber)] | ||||
|     add_connector_loss(fibers, con_in, con_out, default_span_data.EOL) | ||||
|     add_connector_loss(network, fibers, default_con_in, default_con_out, default_span_data.EOL) | ||||
|     add_fiber_padding(network, fibers, padding) | ||||
|     # don't group split fiber and add amp in the same loop | ||||
|     # =>for code clarity (at the expense of speed): | ||||
| @@ -442,6 +547,7 @@ def build_network(network, equipment, pref_ch_db, pref_total_db): | ||||
|  | ||||
|     amplified_nodes = [n for n in network.nodes() | ||||
|                         if isinstance(n, Fiber) or isinstance(n, Roadm)] | ||||
|  | ||||
|     for node in amplified_nodes: | ||||
|         add_egress_amplifier(network, node) | ||||
|  | ||||
| @@ -455,3 +561,11 @@ def build_network(network, equipment, pref_ch_db, pref_total_db): | ||||
|         for t in trx: | ||||
|             set_egress_amplifier(network, t, equipment, pref_total_db) | ||||
|  | ||||
| def load_sim_params(filename): | ||||
|     sim_params = load_json(filename) | ||||
|     return SimParams(params=sim_params) | ||||
|  | ||||
| def configure_network(network, sim_params): | ||||
|     for node in network.nodes: | ||||
|         if isinstance(node, RamanFiber): | ||||
|             node.sim_params = sim_params | ||||
|   | ||||
| @@ -8,13 +8,13 @@ gnpy.core.node | ||||
| This module contains the base class for a network element. | ||||
|  | ||||
| Strictly, a network element is any callable which accepts an immutable | ||||
| .info.SpectralInformation object and returns a .info.SpectralInformation object | ||||
| (a copy.) | ||||
| :class:`.info.SpectralInformation` object and returns an :class:`.info.SpectralInformation` object | ||||
| (a copy). | ||||
|  | ||||
| Network elements MUST implement two attributes .uid and .name representing a | ||||
| unique identifier and a printable name. | ||||
|  | ||||
| This base class provides a mode convenient way to define a network element | ||||
| This base class provides a more convenient way to define a network element | ||||
| via subclassing. | ||||
| ''' | ||||
|  | ||||
| @@ -26,10 +26,12 @@ class Location(namedtuple('Location', 'latitude longitude city region')): | ||||
|         return super().__new__(cls, latitude, longitude, city, region) | ||||
|  | ||||
| class Node: | ||||
|     def __init__(self, uid, name=None, params=None, metadata={'location':{}}, operational=None): | ||||
|     def __init__(self, uid, name=None, params=None, metadata=None, operational=None): | ||||
|         if name is None: | ||||
|             name = uid | ||||
|         self.uid, self.name = uid, name | ||||
|         if metadata is None: | ||||
|             metadata = {'location': {}} | ||||
|         if metadata and not isinstance(metadata.get('location'), Location): | ||||
|             metadata['location'] = Location(**metadata.pop('location', {})) | ||||
|         self.params, self.metadata, self.operational = params, metadata, operational | ||||
|   | ||||
							
								
								
									
										1088
									
								
								gnpy/core/request.py
									
									
									
									
									
								
							
							
						
						
									
										1088
									
								
								gnpy/core/request.py
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										820
									
								
								gnpy/core/science_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										820
									
								
								gnpy/core/science_utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,820 @@ | ||||
| import numpy as np | ||||
| from operator import attrgetter | ||||
| from collections import namedtuple | ||||
| from logging import getLogger | ||||
| import scipy.constants as ph | ||||
| from scipy.integrate import solve_bvp | ||||
| from scipy.integrate import cumtrapz | ||||
| from scipy.interpolate import interp1d | ||||
| from scipy.optimize import OptimizeResult | ||||
|  | ||||
| from gnpy.core.utils import db2lin | ||||
|  | ||||
|  | ||||
| logger = getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class RamanParams(): | ||||
|     def __init__(self, params): | ||||
|         self._flag_raman = params['flag_raman'] | ||||
|         self._space_resolution = params['space_resolution'] | ||||
|         self._tolerance = params['tolerance'] | ||||
|  | ||||
|     @property | ||||
|     def flag_raman(self): | ||||
|         return self._flag_raman | ||||
|  | ||||
|     @property | ||||
|     def space_resolution(self): | ||||
|         return self._space_resolution | ||||
|  | ||||
|     @property | ||||
|     def tolerance(self): | ||||
|         return self._tolerance | ||||
|  | ||||
| class NLIParams(): | ||||
|     def __init__(self, params): | ||||
|         self._nli_method_name = params['nli_method_name'] | ||||
|         self._wdm_grid_size = params['wdm_grid_size'] | ||||
|         self._dispersion_tolerance = params['dispersion_tolerance'] | ||||
|         self._phase_shift_tollerance = params['phase_shift_tollerance'] | ||||
|         self._f_cut_resolution = None | ||||
|         self._f_pump_resolution = None | ||||
|  | ||||
|     @property | ||||
|     def nli_method_name(self): | ||||
|         return self._nli_method_name | ||||
|  | ||||
|     @property | ||||
|     def wdm_grid_size(self): | ||||
|         return self._wdm_grid_size | ||||
|  | ||||
|     @property | ||||
|     def dispersion_tolerance(self): | ||||
|         return self._dispersion_tolerance | ||||
|  | ||||
|     @property | ||||
|     def phase_shift_tollerance(self): | ||||
|         return self._phase_shift_tollerance | ||||
|  | ||||
|     @property | ||||
|     def f_cut_resolution(self): | ||||
|         return self._f_cut_resolution | ||||
|  | ||||
|     @f_cut_resolution.setter | ||||
|     def f_cut_resolution(self, f_cut_resolution): | ||||
|         self._f_cut_resolution = f_cut_resolution | ||||
|  | ||||
|     @property | ||||
|     def f_pump_resolution(self): | ||||
|         return self._f_pump_resolution | ||||
|  | ||||
|     @f_pump_resolution.setter | ||||
|     def f_pump_resolution(self, f_pump_resolution): | ||||
|         self._f_pump_resolution = f_pump_resolution | ||||
|  | ||||
| class SimParams(): | ||||
|     def __init__(self, params): | ||||
|         self._raman_computed_channels = params['raman_computed_channels'] | ||||
|         self._raman_params = RamanParams(params=params['raman_parameters']) | ||||
|         self._nli_params = NLIParams(params=params['nli_parameters']) | ||||
|  | ||||
|     @property | ||||
|     def raman_computed_channels(self): | ||||
|         return self._raman_computed_channels | ||||
|  | ||||
|     @property | ||||
|     def raman_params(self): | ||||
|         return self._raman_params | ||||
|  | ||||
|     @property | ||||
|     def nli_params(self): | ||||
|         return self._nli_params | ||||
|  | ||||
| class FiberParams(): | ||||
|     def __init__(self, fiber): | ||||
|         self._loss_coef = 2 * fiber.dbkm_2_lin()[1] | ||||
|         self._length = fiber.length | ||||
|         self._gamma = fiber.gamma | ||||
|         self._beta2 = fiber.beta2() | ||||
|         self._beta3 = fiber.beta3 if hasattr(fiber, 'beta3') else 0 | ||||
|         self._f_ref_beta = fiber.f_ref_beta if hasattr(fiber, 'f_ref_beta') else 0 | ||||
|         self._raman_efficiency = fiber.params.raman_efficiency | ||||
|         self._temperature = fiber.operational['temperature'] | ||||
|  | ||||
|     @property | ||||
|     def loss_coef(self): | ||||
|         return self._loss_coef | ||||
|  | ||||
|     @property | ||||
|     def length(self): | ||||
|         return self._length | ||||
|  | ||||
|     @property | ||||
|     def gamma(self): | ||||
|         return self._gamma | ||||
|  | ||||
|     @property | ||||
|     def beta2(self): | ||||
|         return self._beta2 | ||||
|  | ||||
|     @property | ||||
|     def beta3(self): | ||||
|         return self._beta3 | ||||
|  | ||||
|     @property | ||||
|     def f_ref_beta(self): | ||||
|         return self._f_ref_beta | ||||
|  | ||||
|     @property | ||||
|     def raman_efficiency(self): | ||||
|         return self._raman_efficiency | ||||
|  | ||||
|     @property | ||||
|     def temperature(self): | ||||
|         return self._temperature | ||||
|  | ||||
|     def alpha0(self, f_ref=193.5e12): | ||||
|         """ It returns the zero element of the series expansion of attenuation coefficient alpha(f) in the | ||||
|         reference frequency f_ref | ||||
|  | ||||
|         :param f_ref: reference frequency of series expansion [Hz] | ||||
|         :return: alpha0: power attenuation coefficient in f_ref [Neper/m] | ||||
|         """ | ||||
|         if not hasattr(self.loss_coef, 'alpha_power'): | ||||
|             alpha0 = self.loss_coef | ||||
|         else: | ||||
|             alpha_interp = interp1d(self.loss_coef['frequency'], | ||||
|                                     self.loss_coef['alpha_power']) | ||||
|             alpha0 = alpha_interp(f_ref) | ||||
|         return alpha0 | ||||
|  | ||||
| pump = namedtuple('RamanPump', 'power frequency propagation_direction') | ||||
|  | ||||
| def propagate_raman_fiber(fiber, *carriers): | ||||
|     sim_params = fiber.sim_params | ||||
|     raman_params = fiber.sim_params.raman_params | ||||
|     nli_params = fiber.sim_params.nli_params | ||||
|     # apply input attenuation to carriers | ||||
|     attenuation_in = db2lin(fiber.con_in + fiber.att_in) | ||||
|     chan = [] | ||||
|     for carrier in carriers: | ||||
|         pwr = carrier.power | ||||
|         pwr = pwr._replace(signal=pwr.signal / attenuation_in, | ||||
|                            nli=pwr.nli / attenuation_in, | ||||
|                            ase=pwr.ase / attenuation_in) | ||||
|         carrier = carrier._replace(power=pwr) | ||||
|         chan.append(carrier) | ||||
|     carriers = tuple(f for f in chan) | ||||
|     fiber_params = FiberParams(fiber) | ||||
|  | ||||
|     # evaluate fiber attenuation involving also SRS if required by sim_params | ||||
|     if 'raman_pumps' in fiber.operational: | ||||
|         raman_pumps = tuple(pump(p['power'], p['frequency'], p['propagation_direction']) | ||||
|                             for p in fiber.operational['raman_pumps']) | ||||
|     else: | ||||
|         raman_pumps = None | ||||
|     raman_solver = RamanSolver(raman_params=raman_params, fiber_params=fiber_params) | ||||
|     stimulated_raman_scattering = raman_solver.stimulated_raman_scattering(carriers=carriers, | ||||
|                                                                            raman_pumps=raman_pumps) | ||||
|     fiber_attenuation = (stimulated_raman_scattering.rho[:, -1])**-2 | ||||
|     if not raman_params.flag_raman: | ||||
|         fiber_attenuation = tuple(fiber.lin_attenuation for _ in carriers) | ||||
|  | ||||
|     # evaluate Raman ASE noise if required by sim_params and if raman pumps are present | ||||
|     if raman_params.flag_raman and raman_pumps: | ||||
|         raman_ase = raman_solver.spontaneous_raman_scattering.power[:, -1] | ||||
|     else: | ||||
|         raman_ase = tuple(0 for _ in carriers) | ||||
|  | ||||
|     # evaluate nli and propagate in fiber | ||||
|     attenuation_out = db2lin(fiber.con_out) | ||||
|     nli_solver = NliSolver(nli_params=nli_params, fiber_params=fiber_params) | ||||
|     nli_solver.stimulated_raman_scattering = stimulated_raman_scattering | ||||
|  | ||||
|     nli_frequencies = [] | ||||
|     computed_nli = [] | ||||
|     for carrier in (c for c in carriers if c.channel_number in sim_params.raman_computed_channels): | ||||
|         resolution_param = frequency_resolution(carrier, carriers, sim_params, fiber_params) | ||||
|         f_cut_resolution, f_pump_resolution, _, _ = resolution_param | ||||
|         nli_params.f_cut_resolution = f_cut_resolution | ||||
|         nli_params.f_pump_resolution = f_pump_resolution | ||||
|         nli_frequencies.append(carrier.frequency) | ||||
|         computed_nli.append(nli_solver.compute_nli(carrier, *carriers)) | ||||
|  | ||||
|     new_carriers = [] | ||||
|     for carrier, attenuation, rmn_ase in zip(carriers, fiber_attenuation, raman_ase): | ||||
|         carrier_nli = np.interp(carrier.frequency, nli_frequencies, computed_nli) | ||||
|         pwr = carrier.power | ||||
|         pwr = pwr._replace(signal=pwr.signal/attenuation/attenuation_out, | ||||
|                            nli=(pwr.nli+carrier_nli)/attenuation/attenuation_out, | ||||
|                            ase=((pwr.ase/attenuation)+rmn_ase)/attenuation_out) | ||||
|         new_carriers.append(carrier._replace(power=pwr)) | ||||
|     return new_carriers | ||||
|  | ||||
| def frequency_resolution(carrier, carriers, sim_params, fiber_params): | ||||
|     def _get_freq_res_k_phi(delta_count, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol): | ||||
|         res_phi = _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol) | ||||
|         res_k = _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol) | ||||
|         res_dict = {'res_phi': res_phi, 'res_k': res_k} | ||||
|         method = min(res_dict, key=res_dict.get) | ||||
|         return res_dict[method], method, res_dict | ||||
|  | ||||
|     def _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol): | ||||
|         return k_tol * abs(alpha0) / abs(beta2) / (1 + delta_count) / (4 * np.pi ** 2 * grid_size) | ||||
|  | ||||
|     def _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol): | ||||
|         return phi_tol / abs(beta2) / (1 + delta_count) / delta_z / (4 * np.pi ** 2 * grid_size) | ||||
|  | ||||
|     grid_size = sim_params.nli_params.wdm_grid_size | ||||
|     delta_z = sim_params.raman_params.space_resolution | ||||
|     alpha0 = fiber_params.alpha0() | ||||
|     beta2 = fiber_params.beta2 | ||||
|     k_tol = sim_params.nli_params.dispersion_tolerance | ||||
|     phi_tol = sim_params.nli_params.phase_shift_tollerance | ||||
|     f_pump_resolution, method_f_pump, res_dict_pump = \ | ||||
|         _get_freq_res_k_phi(0, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol) | ||||
|     f_cut_resolution = {} | ||||
|     method_f_cut = {} | ||||
|     res_dict_cut = {} | ||||
|     for cut_carrier in carriers: | ||||
|         delta_number = cut_carrier.channel_number - carrier.channel_number | ||||
|         delta_count = abs(delta_number) | ||||
|         f_res, method, res_dict = \ | ||||
|             _get_freq_res_k_phi(delta_count, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol) | ||||
|         f_cut_resolution[f'delta_{delta_number}'] = f_res | ||||
|         method_f_cut[delta_number] = method | ||||
|         res_dict_cut[delta_number] = res_dict | ||||
|     return [f_cut_resolution, f_pump_resolution, (method_f_cut, method_f_pump), (res_dict_cut, res_dict_pump)] | ||||
|  | ||||
| def raised_cosine_comb(f, *carriers): | ||||
|     """ Returns an array storing the PSD of a WDM comb of raised cosine shaped | ||||
|     channels at the input frequencies defined in array f | ||||
|     :param f: numpy array of frequencies in Hz | ||||
|     :param carriers: namedtuple describing the WDM comb | ||||
|     :return: PSD of the WDM comb evaluated over f | ||||
|     """ | ||||
|     psd = np.zeros(np.shape(f)) | ||||
|     for carrier in carriers: | ||||
|         f_nch = carrier.frequency | ||||
|         g_ch = carrier.power.signal / carrier.baud_rate | ||||
|         ts = 1 / carrier.baud_rate | ||||
|         passband = (1 - carrier.roll_off) / (2 / carrier.baud_rate) | ||||
|         stopband = (1 + carrier.roll_off) / (2 / carrier.baud_rate) | ||||
|         ff = np.abs(f - f_nch) | ||||
|         tf = ff - passband | ||||
|         if carrier.roll_off == 0: | ||||
|             psd = np.where(tf <= 0, g_ch, 0.) + psd | ||||
|         else: | ||||
|             psd = g_ch * (np.where(tf <= 0, 1., 0.) + 1 / 2 * (1 + np.cos(np.pi * ts / carrier.roll_off * tf)) * | ||||
|                           np.where(tf > 0, 1., 0.) * np.where(np.abs(ff) <= stopband, 1., 0.)) + psd | ||||
|     return psd | ||||
|  | ||||
| class RamanSolver: | ||||
|     def __init__(self, raman_params=None, fiber_params=None): | ||||
|         """ Initialize the fiber object with its physical parameters | ||||
|         :param length: fiber length in m. | ||||
|         :param alphap: fiber power attenuation coefficient vs frequency in 1/m. numpy array | ||||
|         :param freq_alpha: frequency axis of alphap in Hz. numpy array | ||||
|         :param cr_raman: Raman efficiency vs frequency offset in 1/W/m. numpy array | ||||
|         :param freq_cr: reference frequency offset axis for cr_raman. numpy array | ||||
|         :param raman_params: namedtuple containing the solver parameters (optional). | ||||
|         """ | ||||
|         self.fiber_params = fiber_params | ||||
|         self.raman_params = raman_params | ||||
|         self._carriers = None | ||||
|         self._stimulated_raman_scattering = None | ||||
|         self._spontaneous_raman_scattering = None | ||||
|  | ||||
|     @property | ||||
|     def fiber_params(self): | ||||
|         return self._fiber_params | ||||
|  | ||||
|     @fiber_params.setter | ||||
|     def fiber_params(self, fiber_params): | ||||
|         self._stimulated_raman_scattering = None | ||||
|         self._fiber_params = fiber_params | ||||
|  | ||||
|     @property | ||||
|     def carriers(self): | ||||
|         return self._carriers | ||||
|  | ||||
|     @carriers.setter | ||||
|     def carriers(self, carriers): | ||||
|         """ | ||||
|         :param carriers: tuple of namedtuples containing information about carriers | ||||
|         :return: | ||||
|         """ | ||||
|         self._carriers = carriers | ||||
|         self._stimulated_raman_scattering = None | ||||
|  | ||||
|     @property | ||||
|     def raman_pumps(self): | ||||
|         return self._raman_pumps | ||||
|  | ||||
|     @raman_pumps.setter | ||||
|     def raman_pumps(self, raman_pumps): | ||||
|         self._raman_pumps = raman_pumps | ||||
|         self._stimulated_raman_scattering = None | ||||
|  | ||||
|     @property | ||||
|     def raman_params(self): | ||||
|         return self._raman_params | ||||
|  | ||||
|     @raman_params.setter | ||||
|     def raman_params(self, raman_params): | ||||
|         """ | ||||
|         :param raman_params: namedtuple containing the solver parameters (optional). | ||||
|         :return: | ||||
|         """ | ||||
|         self._raman_params = raman_params | ||||
|         self._stimulated_raman_scattering = None | ||||
|         self._spontaneous_raman_scattering = None | ||||
|  | ||||
|     @property | ||||
|     def spontaneous_raman_scattering(self): | ||||
|         if self._spontaneous_raman_scattering is None: | ||||
|             # SET STUFF | ||||
|             loss_coef = self.fiber_params.loss_coef | ||||
|             raman_efficiency = self.fiber_params.raman_efficiency | ||||
|             temperature = self.fiber_params.temperature | ||||
|             carriers = self.carriers | ||||
|             raman_pumps = self.raman_pumps | ||||
|  | ||||
|             logger.debug('Start computing fiber Spontaneous Raman Scattering') | ||||
|             power_spectrum, freq_array, prop_direct, bn_array = self._compute_power_spectrum(carriers, raman_pumps) | ||||
|  | ||||
|             if not hasattr(loss_coef, 'alpha_power'): | ||||
|                 alphap_fiber = loss_coef * np.ones(freq_array.shape) | ||||
|             else: | ||||
|                 interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power']) | ||||
|                 alphap_fiber = interp_alphap(freq_array) | ||||
|  | ||||
|             freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1))) | ||||
|             interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr']) | ||||
|             cr = interp_cr(freq_diff) | ||||
|  | ||||
|             # z propagation axis | ||||
|             z_array = self._stimulated_raman_scattering.z | ||||
|             ase_bc = np.zeros(freq_array.shape) | ||||
|  | ||||
|             # calculate ase power | ||||
|             spontaneous_raman_scattering = self._int_spontaneous_raman(z_array, self._stimulated_raman_scattering.power, | ||||
|                                                                        alphap_fiber, freq_array, cr, freq_diff, ase_bc, | ||||
|                                                                        bn_array, temperature) | ||||
|  | ||||
|             setattr(spontaneous_raman_scattering, 'frequency', freq_array) | ||||
|             setattr(spontaneous_raman_scattering, 'z', z_array) | ||||
|             setattr(spontaneous_raman_scattering, 'power', spontaneous_raman_scattering.x) | ||||
|             delattr(spontaneous_raman_scattering, 'x') | ||||
|  | ||||
|             logger.debug(spontaneous_raman_scattering.message) | ||||
|  | ||||
|             self._spontaneous_raman_scattering = spontaneous_raman_scattering | ||||
|  | ||||
|         return self._spontaneous_raman_scattering | ||||
|  | ||||
|     @staticmethod | ||||
|     def _compute_power_spectrum(carriers, raman_pumps=None): | ||||
|         """ | ||||
|         Rearrangement of spectral and Raman pump information to make them compatible with Raman solver | ||||
|         :param carriers: a tuple of namedtuples describing the transmitted channels | ||||
|         :param raman_pumps: a namedtuple describing the Raman pumps | ||||
|         :return: | ||||
|         """ | ||||
|  | ||||
|         # Signal power spectrum | ||||
|         pow_array = np.array([]) | ||||
|         f_array = np.array([]) | ||||
|         noise_bandwidth_array = np.array([]) | ||||
|         for carrier in sorted(carriers, key=attrgetter('frequency')): | ||||
|             f_array = np.append(f_array, carrier.frequency) | ||||
|             pow_array = np.append(pow_array, carrier.power.signal) | ||||
|             ref_bw = carrier.baud_rate | ||||
|             noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw) | ||||
|  | ||||
|         propagation_direction = np.ones(len(f_array)) | ||||
|  | ||||
|         # Raman pump power spectrum | ||||
|         if raman_pumps: | ||||
|             for pump in raman_pumps: | ||||
|                 pow_array = np.append(pow_array, pump.power) | ||||
|                 f_array = np.append(f_array, pump.frequency) | ||||
|                 direction = +1 if pump.propagation_direction.lower() == 'coprop' else -1 | ||||
|                 propagation_direction = np.append(propagation_direction, direction) | ||||
|                 noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw) | ||||
|  | ||||
|         # Final sorting | ||||
|         ind = np.argsort(f_array) | ||||
|         f_array = f_array[ind] | ||||
|         pow_array = pow_array[ind] | ||||
|         propagation_direction = propagation_direction[ind] | ||||
|  | ||||
|         return pow_array, f_array, propagation_direction, noise_bandwidth_array | ||||
|  | ||||
|     def _int_spontaneous_raman(self, z_array, raman_matrix, alphap_fiber, freq_array, cr_raman_matrix, freq_diff, ase_bc, bn_array, temperature): | ||||
|         spontaneous_raman_scattering = OptimizeResult() | ||||
|  | ||||
|         dx = self.raman_params.space_resolution | ||||
|         h = ph.value('Planck constant') | ||||
|         kb = ph.value('Boltzmann constant') | ||||
|  | ||||
|         power_ase = np.nan * np.ones(raman_matrix.shape) | ||||
|         int_pump = cumtrapz(raman_matrix, z_array, dx=dx, axis=1, initial=0) | ||||
|  | ||||
|         for f_ind, f_ase in enumerate(freq_array): | ||||
|             cr_raman = cr_raman_matrix[f_ind, :] | ||||
|             vibrational_loss = f_ase / freq_array[:f_ind] | ||||
|             eta = 1/(np.exp((h*freq_diff[f_ind, f_ind+1:])/(kb*temperature)) - 1) | ||||
|  | ||||
|             int_fiber_loss = -alphap_fiber[f_ind] * z_array | ||||
|             int_raman_loss = np.sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(), axis=0) | ||||
|             int_raman_gain = np.sum((cr_raman[f_ind + 1:] * int_pump[f_ind + 1:, :].transpose()).transpose(), axis=0) | ||||
|  | ||||
|             int_gain_loss = int_fiber_loss + int_raman_gain + int_raman_loss | ||||
|  | ||||
|             new_ase = np.sum((cr_raman[f_ind+1:] * (1 + eta) * raman_matrix[f_ind+1:, :].transpose()).transpose() * h * f_ase * bn_array[f_ind], axis=0) | ||||
|  | ||||
|             bc_evolution = ase_bc[f_ind] * np.exp(int_gain_loss) | ||||
|             ase_evolution = np.exp(int_gain_loss) * cumtrapz(new_ase*np.exp(-int_gain_loss), z_array, dx=dx, initial=0) | ||||
|  | ||||
|             power_ase[f_ind, :] = bc_evolution + ase_evolution | ||||
|  | ||||
|         spontaneous_raman_scattering.x = 2 * power_ase | ||||
|         spontaneous_raman_scattering.success = True | ||||
|         spontaneous_raman_scattering.message = "Spontaneous Raman Scattering evaluated successfully" | ||||
|  | ||||
|         return spontaneous_raman_scattering | ||||
|  | ||||
|     def stimulated_raman_scattering(self, carriers, raman_pumps=None): | ||||
|         """ Returns stimulated Raman scattering solution including  | ||||
|         fiber gain/loss profile. | ||||
|         :return: self._stimulated_raman_scattering: the SRS problem solution. | ||||
|         scipy.interpolate.PPoly instance | ||||
|         """ | ||||
|  | ||||
|         if self._stimulated_raman_scattering is None: | ||||
|             # fiber parameters | ||||
|             fiber_length = self.fiber_params.length | ||||
|             loss_coef = self.fiber_params.loss_coef | ||||
|             if self.raman_params.flag_raman: | ||||
|                 raman_efficiency = self.fiber_params.raman_efficiency | ||||
|             else: | ||||
|                 raman_efficiency = self.fiber_params.raman_efficiency | ||||
|                 raman_efficiency['cr'] = np.array(raman_efficiency['cr']) * 0 | ||||
|             # raman solver parameters | ||||
|             z_resolution = self.raman_params.space_resolution | ||||
|             tolerance = self.raman_params.tolerance | ||||
|  | ||||
|             logger.debug('Start computing fiber Stimulated Raman Scattering') | ||||
|  | ||||
|             power_spectrum, freq_array, prop_direct, _ = self._compute_power_spectrum(carriers, raman_pumps) | ||||
|  | ||||
|             if not hasattr(loss_coef, 'alpha_power'): | ||||
|                 alphap_fiber = loss_coef * np.ones(freq_array.shape) | ||||
|             else: | ||||
|                 interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power']) | ||||
|                 alphap_fiber = interp_alphap(freq_array) | ||||
|  | ||||
|             freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1))) | ||||
|             interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr']) | ||||
|             cr = interp_cr(freq_diff) | ||||
|  | ||||
|             # z propagation axis | ||||
|             z = np.arange(0, fiber_length+1, z_resolution) | ||||
|  | ||||
|             ode_function = lambda z, p: self._ode_stimulated_raman(z, p, alphap_fiber, freq_array, cr, prop_direct) | ||||
|             boundary_residual = lambda ya, yb: self._residuals_stimulated_raman(ya, yb, power_spectrum, prop_direct) | ||||
|             initial_guess_conditions = self._initial_guess_stimulated_raman(z, power_spectrum, alphap_fiber, prop_direct) | ||||
|  | ||||
|             # ODE SOLVER | ||||
|             stimulated_raman_scattering = solve_bvp(ode_function, boundary_residual, z, initial_guess_conditions, tol=tolerance) | ||||
|  | ||||
|             rho = (stimulated_raman_scattering.y.transpose() / power_spectrum).transpose() | ||||
|             rho = np.sqrt(rho)    # From power attenuation to field attenuation | ||||
|             setattr(stimulated_raman_scattering, 'frequency', freq_array) | ||||
|             setattr(stimulated_raman_scattering, 'z', stimulated_raman_scattering.x) | ||||
|             setattr(stimulated_raman_scattering, 'rho', rho) | ||||
|             setattr(stimulated_raman_scattering, 'power', stimulated_raman_scattering.y) | ||||
|             delattr(stimulated_raman_scattering, 'x') | ||||
|             delattr(stimulated_raman_scattering, 'y') | ||||
|  | ||||
|             self.carriers = carriers | ||||
|             self.raman_pumps = raman_pumps | ||||
|             self._stimulated_raman_scattering = stimulated_raman_scattering | ||||
|  | ||||
|         return self._stimulated_raman_scattering | ||||
|  | ||||
|     def _residuals_stimulated_raman(self, ya, yb, power_spectrum, prop_direct): | ||||
|  | ||||
|         computed_boundary_value = np.zeros(ya.size) | ||||
|  | ||||
|         for index, direction in enumerate(prop_direct): | ||||
|             if direction == +1: | ||||
|                 computed_boundary_value[index] = ya[index] | ||||
|             else: | ||||
|                 computed_boundary_value[index] = yb[index] | ||||
|  | ||||
|         return power_spectrum - computed_boundary_value | ||||
|  | ||||
|     def _initial_guess_stimulated_raman(self, z, power_spectrum, alphap_fiber, prop_direct): | ||||
|         """ Computes the initial guess knowing the boundary conditions | ||||
|         :param z: patial axis [m]. numpy array | ||||
|         :param power_spectrum: power in each frequency slice [W].    Frequency axis is defined by freq_array. numpy array | ||||
|         :param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array | ||||
|         :param prop_direct: indicates the propagation direction of each power slice in power_spectrum: | ||||
|         +1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array | ||||
|         :return: power_guess: guess on the initial conditions [W]. The first ndarray index identifies the frequency slice, | ||||
|         the second ndarray index identifies the step in z. ndarray | ||||
|         """ | ||||
|  | ||||
|         power_guess = np.empty((power_spectrum.size, z.size)) | ||||
|         for f_index, power_slice in enumerate(power_spectrum): | ||||
|             if prop_direct[f_index] == +1: | ||||
|                 power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z) * power_slice | ||||
|             else: | ||||
|                 power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z[::-1]) * power_slice | ||||
|  | ||||
|         return power_guess | ||||
|  | ||||
|     def _ode_stimulated_raman(self, z, power_spectrum, alphap_fiber, freq_array, cr_raman_matrix, prop_direct): | ||||
|         """ Aim of ode_raman is to implement the set of ordinary differential equations (ODEs) describing the Raman effect. | ||||
|         :param z: spatial axis (unused). | ||||
|         :param power_spectrum: power in each frequency slice [W].    Frequency axis is defined by freq_array. numpy array. Size n | ||||
|         :param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array. Size n | ||||
|         :param freq_array: reference frequency axis [Hz]. numpy array. Size n | ||||
|         :param cr_raman: Cr(f) Raman gain efficiency variation in frequency [1/W/m]. Frequency defined by freq_array. numpy ndarray. Size nxn | ||||
|         :param prop_direct: indicates the propagation direction of each power slice in power_spectrum: | ||||
|         +1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array. Size n | ||||
|         :return: dP/dz: the power variation in dz [W/m]. numpy array. Size n | ||||
|         """ | ||||
|  | ||||
|         dpdz = np.nan * np.ones(power_spectrum.shape) | ||||
|         for f_ind, power in enumerate(power_spectrum): | ||||
|             cr_raman = cr_raman_matrix[f_ind, :] | ||||
|             vibrational_loss = freq_array[f_ind] / freq_array[:f_ind] | ||||
|  | ||||
|             for z_ind, power_sample in enumerate(power): | ||||
|                 raman_gain = np.sum(cr_raman[f_ind+1:] * power_spectrum[f_ind+1:, z_ind]) | ||||
|                 raman_loss = np.sum(vibrational_loss * cr_raman[:f_ind] * power_spectrum[:f_ind, z_ind]) | ||||
|  | ||||
|                 dpdz_element = prop_direct[f_ind] * (-alphap_fiber[f_ind] + raman_gain - raman_loss) * power_sample | ||||
|                 dpdz[f_ind][z_ind] = dpdz_element | ||||
|  | ||||
|         return np.vstack(dpdz) | ||||
|  | ||||
| class NliSolver: | ||||
|     """ This class implements the NLI models. | ||||
|         Model and method can be specified in `self.nli_params.method`. | ||||
|         List of implemented methods: | ||||
|         'gn_model_analytic': brute force triple integral solution | ||||
|         'ggn_spectrally_separated_xpm_spm': XPM plus SPM | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, nli_params=None, fiber_params=None): | ||||
|         """ Initialize the fiber object with its physical parameters | ||||
|         """ | ||||
|         self.fiber_params = fiber_params | ||||
|         self.nli_params = nli_params | ||||
|         self.stimulated_raman_scattering = None | ||||
|  | ||||
|     @property | ||||
|     def fiber_params(self): | ||||
|         return self._fiber_params | ||||
|  | ||||
|     @fiber_params.setter | ||||
|     def fiber_params(self, fiber_params): | ||||
|         self._fiber_params = fiber_params | ||||
|  | ||||
|     @property | ||||
|     def stimulated_raman_scattering(self): | ||||
|         return self._stimulated_raman_scattering | ||||
|  | ||||
|     @stimulated_raman_scattering.setter | ||||
|     def stimulated_raman_scattering(self, stimulated_raman_scattering): | ||||
|         self._stimulated_raman_scattering = stimulated_raman_scattering | ||||
|  | ||||
|     @property | ||||
|     def nli_params(self): | ||||
|         return self._nli_params | ||||
|  | ||||
|     @nli_params.setter | ||||
|     def nli_params(self, nli_params): | ||||
|         """ | ||||
|         :param model_params: namedtuple containing the parameters used to compute the NLI. | ||||
|         """ | ||||
|         self._nli_params = nli_params | ||||
|  | ||||
|     def compute_nli(self, carrier, *carriers): | ||||
|         """ Compute NLI power generated by the WDM comb `*carriers` on the channel under test `carrier` | ||||
|         at the end of the fiber span. | ||||
|         """ | ||||
|         if 'gn_model_analytic' == self.nli_params.nli_method_name.lower(): | ||||
|             carrier_nli = self._gn_analytic(carrier, *carriers) | ||||
|         elif 'ggn_spectrally_separated' in self.nli_params.nli_method_name.lower(): | ||||
|             eta_matrix = self._compute_eta_matrix(carrier, *carriers) | ||||
|             carrier_nli = self._carrier_nli_from_eta_matrix(eta_matrix, carrier, *carriers) | ||||
|         else: | ||||
|             raise ValueError(f'Method {self.nli_params.method_nli} not implemented.') | ||||
|  | ||||
|         return carrier_nli | ||||
|  | ||||
|     @staticmethod | ||||
|     def _carrier_nli_from_eta_matrix(eta_matrix, carrier, *carriers): | ||||
|         carrier_nli = 0 | ||||
|         for pump_carrier_1 in carriers: | ||||
|             for pump_carrier_2 in carriers: | ||||
|                 carrier_nli += eta_matrix[pump_carrier_1.channel_number-1, pump_carrier_2.channel_number-1] * \ | ||||
|                                pump_carrier_1.power.signal * pump_carrier_2.power.signal | ||||
|         carrier_nli *= carrier.power.signal | ||||
|  | ||||
|         return carrier_nli | ||||
|  | ||||
|     def _compute_eta_matrix(self, carrier_cut, *carriers): | ||||
|         cut_index = carrier_cut.channel_number - 1 | ||||
|         # Matrix initialization | ||||
|         matrix_size = max(carriers, key=lambda x: getattr(x, 'channel_number')).channel_number | ||||
|         eta_matrix = np.zeros(shape=(matrix_size, matrix_size)) | ||||
|  | ||||
|         # SPM | ||||
|         logger.debug(f'Start computing SPM on channel #{carrier_cut.channel_number}') | ||||
|         # SPM GGN | ||||
|         if 'ggn' in self.nli_params.nli_method_name.lower(): | ||||
|             partial_nli = self._generalized_spectrally_separated_spm(carrier_cut) | ||||
|         # SPM GN | ||||
|         elif 'gn' in self.nli_params.nli_method_name.lower(): | ||||
|             partial_nli = self._gn_analytic(carrier_cut, *[carrier_cut]) | ||||
|         eta_matrix[cut_index, cut_index] = partial_nli / (carrier_cut.power.signal**3) | ||||
|  | ||||
|         # XPM | ||||
|         for pump_carrier in carriers: | ||||
|             pump_index = pump_carrier.channel_number - 1 | ||||
|             if not (cut_index == pump_index): | ||||
|                 logger.debug(f'Start computing XPM on channel #{carrier_cut.channel_number} ' | ||||
|                              f'from channel #{pump_carrier.channel_number}') | ||||
|                 # XPM GGN | ||||
|                 if 'ggn' in self.nli_params.nli_method_name.lower(): | ||||
|                     partial_nli = self._generalized_spectrally_separated_xpm(carrier_cut, pump_carrier) | ||||
|                 # XPM GGN | ||||
|                 elif 'gn' in self.nli_params.nli_method_name.lower(): | ||||
|                     partial_nli = self._gn_analytic(carrier_cut, *[pump_carrier]) | ||||
|                 eta_matrix[pump_index, pump_index] = partial_nli /\ | ||||
|                                                      (carrier_cut.power.signal * pump_carrier.power.signal**2) | ||||
|         return eta_matrix | ||||
|  | ||||
|     # Methods for computing GN-model | ||||
|     def _gn_analytic(self, carrier, *carriers): | ||||
|         """ Computes the nonlinear interference power on a single carrier. | ||||
|         The method uses eq. 120 from arXiv:1209.0394. | ||||
|         :param carrier: the signal under analysis | ||||
|         :param carriers: the full WDM comb | ||||
|         :return: carrier_nli: the amount of nonlinear interference in W on the carrier under analysis | ||||
|         """ | ||||
|         alpha = self.fiber_params.alpha0() / 2 | ||||
|         beta2 = self.fiber_params.beta2 | ||||
|         gamma = self.fiber_params.gamma | ||||
|         length = self.fiber_params.length | ||||
|         effective_length = (1 - np.exp(-2 * alpha * length)) / (2 * alpha) | ||||
|         asymptotic_length = 1 / (2 * alpha) | ||||
|  | ||||
|         g_nli = 0 | ||||
|         for interfering_carrier in carriers: | ||||
|             g_interfearing = interfering_carrier.power.signal / interfering_carrier.baud_rate | ||||
|             g_signal = carrier.power.signal / carrier.baud_rate | ||||
|             g_nli += g_interfearing**2 * g_signal \ | ||||
|                 * _psi(carrier, interfering_carrier, beta2=self.fiber_params.beta2, asymptotic_length=1/self.fiber_params.alpha0()) | ||||
|         g_nli *= (16.0 / 27.0) * (gamma * effective_length)**2 /\ | ||||
|                  (2 * np.pi * abs(beta2) * asymptotic_length) | ||||
|         carrier_nli = carrier.baud_rate * g_nli | ||||
|         return carrier_nli | ||||
|  | ||||
|     # Methods for computing the GGN-model | ||||
|     def _generalized_spectrally_separated_spm(self, carrier): | ||||
|         f_cut_resolution = self.nli_params.f_cut_resolution['delta_0'] | ||||
|         f_eval = carrier.frequency | ||||
|         g_cut = (carrier.power.signal / carrier.baud_rate) | ||||
|  | ||||
|         spm_nli = carrier.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_cut**3 * \ | ||||
|                   self._generalized_psi(carrier, carrier, f_eval, f_cut_resolution, f_cut_resolution) | ||||
|         return spm_nli | ||||
|  | ||||
|     def _generalized_spectrally_separated_xpm(self, carrier_cut, pump_carrier): | ||||
|         delta_index = pump_carrier.channel_number - carrier_cut.channel_number | ||||
|         f_cut_resolution = self.nli_params.f_cut_resolution[f'delta_{delta_index}'] | ||||
|         f_pump_resolution = self.nli_params.f_pump_resolution | ||||
|         f_eval = carrier_cut.frequency | ||||
|         g_pump = (pump_carrier.power.signal / pump_carrier.baud_rate) | ||||
|         g_cut = (carrier_cut.power.signal / carrier_cut.baud_rate) | ||||
|         frequency_offset_threshold = self._frequency_offset_threshold(pump_carrier.baud_rate) | ||||
|         if abs(carrier_cut.frequency - pump_carrier.frequency) <= frequency_offset_threshold: | ||||
|             xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \ | ||||
|                       2 * self._generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution) | ||||
|         else: | ||||
|             xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \ | ||||
|                       2 * self._fast_generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution) | ||||
|         return xpm_nli | ||||
|  | ||||
|     def _fast_generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution): | ||||
|         """ It computes the generalized psi function similarly to the one used in the GN model | ||||
|         :return: generalized_psi | ||||
|         """ | ||||
|         # Fiber parameters | ||||
|         alpha0 = self.fiber_params.alpha0(f_eval) | ||||
|         beta2 = self.fiber_params.beta2 | ||||
|         beta3 = self.fiber_params.beta3 | ||||
|         f_ref_beta = self.fiber_params.f_ref_beta | ||||
|         z = self.stimulated_raman_scattering.z | ||||
|         frequency_rho = self.stimulated_raman_scattering.frequency | ||||
|         rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2) | ||||
|         if len(frequency_rho) == 1: | ||||
|             rho_function = lambda f: rho_norm[0, :] | ||||
|         else: | ||||
|             rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate') | ||||
|         rho_norm_pump = rho_function(pump_carrier.frequency) | ||||
|  | ||||
|         f1_array = np.array([pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2), | ||||
|                              pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2)]) | ||||
|         f2_array = np.arange(carrier_cut.frequency, | ||||
|                              carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2), | ||||
|                              f_cut_resolution)  # Only positive f2 is used since integrand_f2 is symmetric | ||||
|  | ||||
|         integrand_f1 = np.zeros(len(f1_array)) | ||||
|         for f1_index, f1 in enumerate(f1_array): | ||||
|             delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \ | ||||
|                          (beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta)) | ||||
|             integrand_f2 = self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0) | ||||
|             integrand_f1[f1_index] = 2 * np.trapz(integrand_f2, f2_array)  # 2x since integrand_f2 is symmetric in f2 | ||||
|         generalized_psi = 0.5 * sum(integrand_f1) * pump_carrier.baud_rate | ||||
|         return generalized_psi | ||||
|  | ||||
|     def _generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution): | ||||
|         """ It computes the generalized psi function similarly to the one used in the GN model | ||||
|         :return: generalized_psi | ||||
|         """ | ||||
|         # Fiber parameters | ||||
|         alpha0 = self.fiber_params.alpha0(f_eval) | ||||
|         beta2 = self.fiber_params.beta2 | ||||
|         beta3 = self.fiber_params.beta3 | ||||
|         f_ref_beta = self.fiber_params.f_ref_beta | ||||
|         z = self.stimulated_raman_scattering.z | ||||
|         frequency_rho = self.stimulated_raman_scattering.frequency | ||||
|         rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2) | ||||
|         if len(frequency_rho) == 1: | ||||
|             rho_function = lambda f: rho_norm[0, :] | ||||
|         else: | ||||
|             rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate') | ||||
|         rho_norm_pump = rho_function(pump_carrier.frequency) | ||||
|  | ||||
|         f1_array = np.arange(pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2), | ||||
|                              pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2), | ||||
|                              f_pump_resolution) | ||||
|         f2_array = np.arange(carrier_cut.frequency - (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2), | ||||
|                              carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2), | ||||
|                              f_cut_resolution) | ||||
|         psd1 = raised_cosine_comb(f1_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal) | ||||
|  | ||||
|         integrand_f1 = np.zeros(len(f1_array)) | ||||
|         for f1_index, (f1, psd1_sample) in enumerate(zip(f1_array, psd1)): | ||||
|             f3_array = f1 + f2_array - f_eval | ||||
|             psd2 = raised_cosine_comb(f2_array, carrier_cut) * (carrier_cut.baud_rate / carrier_cut.power.signal) | ||||
|             psd3 = raised_cosine_comb(f3_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal) | ||||
|             ggg = psd1_sample * psd2 * psd3 | ||||
|  | ||||
|             delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \ | ||||
|                          (beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta)) | ||||
|  | ||||
|             integrand_f2 = ggg * self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0) | ||||
|             integrand_f1[f1_index] = np.trapz(integrand_f2, f2_array) | ||||
|         generalized_psi = np.trapz(integrand_f1, f1_array) | ||||
|         return generalized_psi | ||||
|  | ||||
|     @staticmethod | ||||
|     def _generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0): | ||||
|         w = 1j * delta_beta - alpha0 | ||||
|         generalized_rho_nli = (rho_norm_pump[-1]**2 * np.exp(w * z[-1]) - rho_norm_pump[0]**2 * np.exp(w * z[0])) / w | ||||
|         for z_ind in range(0, len(z) - 1): | ||||
|             derivative_rho = (rho_norm_pump[z_ind + 1]**2 - rho_norm_pump[z_ind]**2) / (z[z_ind + 1] - z[z_ind]) | ||||
|             generalized_rho_nli -= derivative_rho * (np.exp(w * z[z_ind + 1]) - np.exp(w * z[z_ind])) / (w**2) | ||||
|         generalized_rho_nli = np.abs(generalized_rho_nli)**2 | ||||
|         return generalized_rho_nli | ||||
|  | ||||
|     def _frequency_offset_threshold(self, symbol_rate): | ||||
|         k_ref = 5 | ||||
|         beta2_ref = 21.3e-27 | ||||
|         delta_f_ref = 50e9 | ||||
|         rs_ref = 32e9 | ||||
|         freq_offset_th = ((k_ref * delta_f_ref) * rs_ref * beta2_ref) / (self.fiber_params.beta2 * symbol_rate) | ||||
|         return freq_offset_th | ||||
|  | ||||
| def _psi(carrier, interfering_carrier, beta2, asymptotic_length): | ||||
|     """Calculates eq. 123 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__""" | ||||
|  | ||||
|     if carrier.channel_number == interfering_carrier.channel_number: # SCI, SPM | ||||
|         psi = np.arcsinh(0.5 * np.pi**2 * asymptotic_length * abs(beta2) * carrier.baud_rate**2) | ||||
|     else: # XCI, XPM | ||||
|         delta_f = carrier.frequency - interfering_carrier.frequency | ||||
|         psi = np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) * | ||||
|                          carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate)) | ||||
|         psi -= np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) * | ||||
|                           carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate)) | ||||
|     return psi | ||||
| @@ -22,6 +22,7 @@ from json import dumps | ||||
| from pathlib import Path | ||||
| from gnpy.core.equipment import load_equipment | ||||
| from gnpy.core.utils import db2lin, lin2db | ||||
| from gnpy.core.exceptions import ServiceError | ||||
|  | ||||
| SERVICES_COLUMN = 12 | ||||
| #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | ||||
| @@ -43,17 +44,18 @@ class Element: | ||||
|         return hash((type(self), self.uid)) | ||||
|  | ||||
| class Request_element(Element): | ||||
|     def __init__(self,Request,eqpt_filename): | ||||
|     def __init__(self, Request, eqpt_filename, bidir): | ||||
|         # request_id is str | ||||
|         # excel has automatic number formatting that adds .0 on integer values | ||||
|         # the next lines recover the pure int value, assuming this .0 is unwanted | ||||
|         self.request_id = correct_xlrd_int_to_str_reading(Request.request_id) | ||||
|         self.source = Request.source | ||||
|         self.destination = Request.destination | ||||
|         self.source = f'trx {Request.source}' | ||||
|         self.destination = f'trx {Request.destination}' | ||||
|         # TODO: the automatic naming generated by excel parser requires that source and dest name  | ||||
|         # be a string starting with 'trx' : this is manually added here. | ||||
|         self.srctpid = f'trx {Request.source}' | ||||
|         self.dsttpid = f'trx {Request.destination}' | ||||
|         self.bidir = bidir | ||||
|         # test that trx_type belongs to eqpt_config.json | ||||
|         # if not replace it with a default | ||||
|         equipment = load_equipment(eqpt_filename) | ||||
| @@ -73,17 +75,17 @@ class Request_element(Element): | ||||
|                 Requestmode = None | ||||
|                 self.mode = Request.mode | ||||
|         except KeyError: | ||||
|             msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Requestmode}\' in eqpt library \nComputation stopped.' | ||||
|             msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Request.mode}\' in eqpt library \nComputation stopped.' | ||||
|             #print(msg) | ||||
|             logger.critical(msg) | ||||
|             exit() | ||||
|             raise ServiceError(msg) | ||||
|         # excel input are in GHz and dBm | ||||
|         if Request.spacing is not None: | ||||
|             self.spacing = Request.spacing * 1e9 | ||||
|         else: | ||||
|             msg = f'Request {self.request_id} missing spacing: spacing is mandatory.\ncomputation stopped' | ||||
|             logger.critical(msg) | ||||
|             exit() | ||||
|             raise ServiceError(msg) | ||||
|         if Request.power is not None: | ||||
|             self.power =  db2lin(Request.power) * 1e-3 | ||||
|         else: | ||||
| @@ -120,9 +122,9 @@ class Request_element(Element): | ||||
|  | ||||
|         # the excel parser applies the same hop-type to all nodes in the route nodes_list. | ||||
|         # user can change this per node in the generated json | ||||
|         self.loose = 'loose' | ||||
|         self.loose = 'LOOSE' | ||||
|         if Request.is_loose == 'no' : | ||||
|             self.loose = 'strict' | ||||
|             self.loose = 'STRICT' | ||||
|         self.path_bandwidth = None | ||||
|         if Request.path_bandwidth is not None: | ||||
|             self.path_bandwidth = Request.path_bandwidth * 1e9 | ||||
| @@ -132,46 +134,41 @@ class Request_element(Element): | ||||
|     uid = property(lambda self: repr(self)) | ||||
|     @property | ||||
|     def pathrequest(self): | ||||
|         # Default assumption for bidir is False | ||||
|         req_dictionnary = { | ||||
|                     'request-id':self.request_id, | ||||
|                     'source':    self.source, | ||||
|                     'destination':  self.destination, | ||||
|                     'src-tp-id': self.srctpid, | ||||
|                     'dst-tp-id': self.dsttpid, | ||||
|                     'bidirectional': self.bidir, | ||||
|                     'path-constraints':{ | ||||
|                         'te-bandwidth': { | ||||
|                             'technology': 'flexi-grid', | ||||
|                             'trx_type'  : self.trx_type, | ||||
|                             'trx_mode'  : self.mode, | ||||
|                             'effective-freq-slot':[{'n': 'null','m': 'null'}] , | ||||
|                             'effective-freq-slot':[{'N': 'null', 'M': 'null'}], | ||||
|                             'spacing'   : self.spacing, | ||||
|                             'max-nb-of-channel'  : self.nb_channel, | ||||
|                             'output-power'       : self.power | ||||
|                             # 'path_bandwidth'       : self.path_bandwidth  | ||||
|                         } | ||||
|                     }, | ||||
|                     'optimizations': { | ||||
|                         'explicit-route-include-objects': [ | ||||
|                         { | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|         if self.nodes_list: | ||||
|             req_dictionnary['explicit-route-objects'] = {} | ||||
|             temp = {'route-object-include-exclude' : [ | ||||
|                         {'explicit-route-usage': 'route-include-ero', | ||||
|                         'index': self.nodes_list.index(node), | ||||
|                             'unnumbered-hop':{ | ||||
|                         'num-unnum-hop': { | ||||
|                             'node-id': f'{node}', | ||||
|                             'link-tp-id': 'link-tp-id is not used', | ||||
|                             'hop-type': f'{self.loose}', | ||||
|                                 'direction': 'direction is not used' | ||||
|                             }, | ||||
|                             'label-hop':{ | ||||
|                                 'te-label': { | ||||
|                                     'generic': 'generic is not used', | ||||
|                                     'direction': 'direction is not used' | ||||
|                             } | ||||
|                         } | ||||
|                         for node in self.nodes_list] | ||||
|                    } | ||||
|                         for node in self.nodes_list | ||||
|                     ] | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             req_dictionnary['explicit-route-objects'] = temp | ||||
|         if self.path_bandwidth is not None: | ||||
|             req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth | ||||
|              | ||||
| @@ -181,30 +178,41 @@ class Request_element(Element): | ||||
|         if self.disjoint_from : | ||||
|             return {'synchronization-id':self.request_id, | ||||
|                 'svec': { | ||||
|                     'relaxable' : 'False', | ||||
|                     'link-diverse': 'True', | ||||
|                     'node-diverse': 'True', | ||||
|                     'relaxable' : 'false', | ||||
|                     'disjointness': 'node link', | ||||
|                     'request-id-number': [self.request_id]+ [n for n in self.disjoint_from] | ||||
|                 } | ||||
|             } | ||||
|         else: | ||||
|             return None | ||||
|         # TO-DO: avoid multiple entries with same synchronisation vectors | ||||
|     @property | ||||
|     def json(self): | ||||
|         return self.pathrequest , self.pathsync | ||||
|  | ||||
| def convert_service_sheet(input_filename, eqpt_filename, output_filename='', filter_region=[]): | ||||
| def convert_service_sheet(input_filename, eqpt_filename, output_filename='', bidir=False, filter_region=None): | ||||
|     """ converts a service sheet into a json structure | ||||
|     """ | ||||
|     if filter_region is None: | ||||
|         filter_region = [] | ||||
|     service = parse_excel(input_filename) | ||||
|     req = [Request_element(n,eqpt_filename) for n in service] | ||||
|     req = [Request_element(n, eqpt_filename, bidir) for n in service] | ||||
|     # dumps the output into a json file with name | ||||
|     # split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]] | ||||
|     if output_filename=='': | ||||
|         output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json' | ||||
|     # for debug | ||||
|     # print(json_filename) | ||||
|     # if there is no sync vector , do not write any synchronization | ||||
|     synchro = [n.json[1] for n in req if n.json[1] is not None] | ||||
|     if synchro: | ||||
|         data = { | ||||
|             'path-request': [n.json[0] for n in req], | ||||
|         'synchronization': [n.json[1] for n in req | ||||
|         if n.json[1] is not None] | ||||
|             'synchronization': synchro | ||||
|         } | ||||
|     else: | ||||
|         data = { | ||||
|             'path-request': [n.json[0] for n in req] | ||||
|             } | ||||
|     with open(output_filename, 'w', encoding='utf-8') as f: | ||||
|         f.write(dumps(data, indent=2, ensure_ascii=False)) | ||||
| @@ -232,21 +240,25 @@ def parse_excel(input_filename): | ||||
|     return services | ||||
|  | ||||
| def parse_service_sheet(service_sheet): | ||||
|     """ reads each column according to authorized fieldnames. order is not important. | ||||
|     """ | ||||
|     logger.info(f'Validating headers on {service_sheet.name!r}') | ||||
|     # add a test on field to enable the '' field case that arises when columns on the | ||||
|     # right hand side are used as comments or drawing in the excel sheet | ||||
|         header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN] if len(x.value.strip())>0] | ||||
|     header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN] | ||||
|                 if len(x.value.strip()) > 0] | ||||
|  | ||||
|     # create a service_fieldname independant from the excel column order | ||||
|     # to be compatible with any version of the sheet | ||||
|     # the following dictionnary records the excel field names and the corresponding parameter's name | ||||
|  | ||||
|         authorized_fieldnames = {'route id':'request_id', 'Source':'source', 'Destination':'destination', \ | ||||
|     authorized_fieldnames = { | ||||
|         'route id':'request_id', 'Source':'source', 'Destination':'destination', \ | ||||
|         'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \ | ||||
|         'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\ | ||||
|         'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\ | ||||
|         'routing: is loose?':'is_loose', 'path bandwidth':'path_bandwidth'} | ||||
|         try : | ||||
|     try: | ||||
|         service_fieldnames = [authorized_fieldnames[e] for e in header] | ||||
|     except KeyError: | ||||
|         msg = f'Malformed header on Service sheet: {header} field not in {authorized_fieldnames}' | ||||
|   | ||||
							
								
								
									
										386
									
								
								gnpy/core/spectrum_assignment.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										386
									
								
								gnpy/core/spectrum_assignment.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,386 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
|  | ||||
| """ | ||||
| gnpy.core.spectrum_assignment | ||||
| ============================= | ||||
|  | ||||
| This module contains the Oms and Bitmap classes and the different method to | ||||
| select and assign spectrum. Spectrum_selection function identifies the free | ||||
| slots and select_candidate selects the candidate spectrum according to | ||||
| strategy: for example first fit | ||||
| oms records its elements, and elements are updated with an oms to have | ||||
| element/oms correspondace | ||||
| """ | ||||
|  | ||||
| from collections import namedtuple | ||||
| from logging import getLogger | ||||
| from math import ceil | ||||
| from gnpy.core.elements import Roadm, Transceiver | ||||
| from gnpy.core.exceptions import SpectrumError | ||||
|  | ||||
| LOGGER = getLogger(__name__) | ||||
|  | ||||
| class Bitmap: | ||||
|     """ records the spectrum occupation | ||||
|     """ | ||||
|     def __init__(self, f_min, f_max, grid, guardband=0.15e12, bitmap=None): | ||||
|         # n is the min index including guardband. Guardband is require to be sure | ||||
|         # that a channel can be assigned  with center frequency fmin (means that its | ||||
|         # slot occupation goes below freq_index_min | ||||
|         n_min = frequency_to_n(f_min-guardband, grid) | ||||
|         n_max = frequency_to_n(f_max+guardband, grid) - 1 | ||||
|         self.n_min = n_min | ||||
|         self.n_max = n_max | ||||
|         self.freq_index_min = frequency_to_n(f_min) | ||||
|         self.freq_index_max = frequency_to_n(f_max) | ||||
|         self.freq_index = list(range(n_min, n_max+1)) | ||||
|         if bitmap is None: | ||||
|             self.bitmap = [1] * (n_max-n_min+1) | ||||
|         elif len(bitmap) == len(self.freq_index): | ||||
|             self.bitmap = bitmap | ||||
|         else: | ||||
|             raise SpectrumError(f'bitmap is not consistant with f_min{f_min} - n: {n_min} and f_max{f_max}- n :{n_max}') | ||||
|  | ||||
|     def getn(self, i): | ||||
|         """ converts the n (itu grid) into a local index | ||||
|         """ | ||||
|         return self.freq_index[i] | ||||
|     def geti(self, nvalue): | ||||
|         """ converts the local index into n (itu grid) | ||||
|         """ | ||||
|         return self.freq_index.index(nvalue) | ||||
|     def insert_left(self, newbitmap): | ||||
|         """ insert bitmap on the left to align oms bitmaps if their start frequencies are different | ||||
|         """ | ||||
|         self.bitmap = newbitmap + self.bitmap | ||||
|         temp = list(range(self.n_min-len(newbitmap), self.n_min)) | ||||
|         self.freq_index = temp + self.freq_index | ||||
|         self.n_min = self.freq_index[0] | ||||
|     def insert_right(self, newbitmap): | ||||
|         """ insert bitmap on the right to align oms bitmaps if their stop frequencies are different | ||||
|         """ | ||||
|         self.bitmap = self.bitmap + newbitmap | ||||
|         self.freq_index = self.freq_index + list(range(self.n_max, self.n_max+len(newbitmap))) | ||||
|         self.n_max = self.freq_index[-1] | ||||
|  | ||||
| #    +'grid available_slots f_min f_max services_list') | ||||
| OMSParams = namedtuple('OMSParams', 'oms_id el_id_list el_list') | ||||
|  | ||||
| class OMS: | ||||
|     """ OMS class is the logical container that represent a link between two adjacent ROADMs and | ||||
|         records the crossed elements and the occupied spectrum | ||||
|     """ | ||||
|     def __init__(self, *args, **params): | ||||
|         params = OMSParams(**params) | ||||
|         self.oms_id = params.oms_id | ||||
|         self.el_id_list = params.el_id_list | ||||
|         self.el_list = params.el_list | ||||
|         self.spectrum_bitmap = [] | ||||
|         self.nb_channels = 0 | ||||
|         self.service_list = [] | ||||
|     # TODO | ||||
|     def __str__(self): | ||||
|         return '\n\t'.join([f'{type(self).__name__} {self.oms_id}', | ||||
|                                f'{self.el_id_list[0]} - {self.el_id_list[-1]}']) | ||||
|     def __repr__(self): | ||||
|         return '\n\t'.join([f'{type(self).__name__} {self.oms_id}', | ||||
|                                f'{self.el_id_list[0]} - {self.el_id_list[-1]}', '\n']) | ||||
|  | ||||
|     def add_element(self, elem): | ||||
|         """ records oms elements | ||||
|         """ | ||||
|         self.el_id_list.append(elem.uid) | ||||
|         self.el_list.append(elem) | ||||
|  | ||||
|     def update_spectrum(self, f_min, f_max, guardband=0.15e12, existing_spectrum=None, | ||||
|                         grid=0.00625e12): | ||||
|         """ frequencies expressed in Hz | ||||
|         """ | ||||
|         if existing_spectrum is None: | ||||
|             # add some 150 GHz margin to enable a center channel on f_min | ||||
|             # use ITU-T G694.1 | ||||
|             # Flexible DWDM grid definition | ||||
|             # For the flexible DWDM grid, the allowed frequency slots have a nominal | ||||
|             # central frequency (in THz) defined by: | ||||
|             # 193.1 + n × 0.00625 where n is a positive or negative integer including 0 | ||||
|             # and 0.00625 is the nominal central frequency granularity in THz | ||||
|             # and a slot width defined by: | ||||
|             # 12.5 × m where m is a positive integer and 12.5 is the slot width granularity in | ||||
|             # GHz. | ||||
|             # Any combination of frequency slots is allowed as long as no two frequency | ||||
|             # slots overlap. | ||||
|  | ||||
|             # TODO : add explaination on that / parametrize .... | ||||
|             self.spectrum_bitmap = Bitmap(f_min, f_max, grid, guardband) | ||||
|             # print(len(self.spectrum_bitmap.bitmap)) | ||||
|  | ||||
|     def assign_spectrum(self, nvalue, mvalue): | ||||
|         """ change oms spectrum to mark spectrum assigned | ||||
|         """ | ||||
|         if (nvalue is None or mvalue is None or isinstance(nvalue, float) | ||||
|                 or isinstance(mvalue, float) or mvalue == 0): | ||||
|             raise SpectrumError('could not assign None values') | ||||
|         startn, stopn = mvalue_to_slots(nvalue, mvalue) | ||||
|         # print(f'startn stop n {startn} , {stopn}') | ||||
|         # assumes that guardbands are sufficient to ensure that assigning a center channel | ||||
|         # at fmin or fmax is OK is startn > self.spectrum_bitmap.n_min | ||||
|         if (nvalue <= self.spectrum_bitmap.freq_index_max and | ||||
|                 nvalue >= self.spectrum_bitmap.freq_index_min and | ||||
|                 stopn <= self.spectrum_bitmap.n_max and | ||||
|                 startn > self.spectrum_bitmap.n_min): | ||||
|             # verification that both length are identical | ||||
|             self.spectrum_bitmap.bitmap[self.spectrum_bitmap.geti(startn):self.spectrum_bitmap.geti(stopn)+1] = [0] * (stopn-startn+1) | ||||
|             return True | ||||
|         else: | ||||
|             msg = f'Could not assign n {nvalue}, m {mvalue} values:' +\ | ||||
|                   f' one or several slots are not available' | ||||
|             LOGGER.info(msg) | ||||
|             return False | ||||
|  | ||||
|     def add_service(self, service_id, nb_wl): | ||||
|         """ record service and mark spectrum as occupied | ||||
|         """ | ||||
|         self.service_list.append(service_id) | ||||
|         self.nb_channels += nb_wl | ||||
|  | ||||
| def frequency_to_n(freq, grid=0.00625e12): | ||||
|     """ converts frequency into the n value (ITU grid) | ||||
|     """ | ||||
|     return (int)((freq-193.1e12)/grid) | ||||
|  | ||||
| def nvalue_to_frequency(nvalue, grid=0.00625e12): | ||||
|     """ converts n value into a frequency | ||||
|     """ | ||||
|     return 193.1e12 + nvalue * grid | ||||
|  | ||||
| def mvalue_to_slots(nvalue, mvalue): | ||||
|     """ convert center n an m into start and stop n | ||||
|     """ | ||||
|     startn = nvalue - mvalue | ||||
|     stopn = nvalue + mvalue -1 | ||||
|     return startn, stopn | ||||
|  | ||||
| def slots_to_m(startn, stopn): | ||||
|     """ converts the start and stop n values to the center n and m value | ||||
|     """ | ||||
|     nvalue = (int)((startn+stopn+1)/2) | ||||
|     mvalue = (int)((stopn-startn+1)/2) | ||||
|     return nvalue, mvalue | ||||
|  | ||||
| def m_to_freq(nvalue, mvalue, grid=0.00625e12): | ||||
|     """ converts m into frequency range | ||||
|     """ | ||||
|     startn, stopn = mvalue_to_slots(nvalue, mvalue) | ||||
|     fstart = nvalue_to_frequency(startn, grid) | ||||
|     fstop = nvalue_to_frequency(stopn+1, grid) | ||||
|     return fstart, fstop | ||||
|  | ||||
| def align_grids(oms_list): | ||||
|     """ used to apply same grid to all oms : same starting n, stop n and slot size | ||||
|         out of grid slots are set to 0 | ||||
|     """ | ||||
|     n_min = min([o.spectrum_bitmap.n_min for o in oms_list]) | ||||
|     n_max = max([o.spectrum_bitmap.n_max for o in oms_list]) | ||||
|     for this_o in oms_list: | ||||
|         if (this_o.spectrum_bitmap.n_min - n_min) > 0: | ||||
|             this_o.spectrum_bitmap.insert_left([0] * (this_o.spectrum_bitmap.n_min - n_min)) | ||||
|         if (n_max - this_o.spectrum_bitmap.n_max) > 0: | ||||
|             this_o.spectrum_bitmap.insert_right([0] * (n_max - this_o.spectrum_bitmap.n_max)) | ||||
|     return oms_list | ||||
|  | ||||
| def build_oms_list(network, equipment): | ||||
|     """ initialization of OMS list in the network | ||||
|         an oms is build reading all intermediate nodes between two adjacent ROADMs | ||||
|         each element within the list is being added an oms and oms_id to record the | ||||
|         oms it belongs to. | ||||
|         the function supports different spectrum width and supposes that the whole network | ||||
|         works with the min range among OMSs | ||||
|     """ | ||||
|     oms_id = 0 | ||||
|     oms_list = [] | ||||
|     for node in [n for n in network.nodes() if isinstance(n, Roadm)]: | ||||
|         for edge in network.edges([node]): | ||||
|             if not isinstance(edge[1], Transceiver): | ||||
|                 nd_in = edge[0] # nd_in is a Roadm | ||||
|                 try: | ||||
|                     nd_in.oms_list.append(oms_id) | ||||
|                 except AttributeError: | ||||
|                     nd_in.oms_list = [] | ||||
|                     nd_in.oms_list.append(oms_id) | ||||
|                 nd_out = edge[1] | ||||
|  | ||||
|                 params = {} | ||||
|                 params['oms_id'] = oms_id | ||||
|                 params['el_id_list'] = [] | ||||
|                 params['el_list'] = [] | ||||
|                 oms = OMS(**params) | ||||
|                 oms.add_element(nd_in) | ||||
|                 while not isinstance(nd_out, Roadm): | ||||
|                     oms.add_element(nd_out) | ||||
|                     # add an oms_id in the element | ||||
|                     nd_out.oms_id = oms_id | ||||
|                     nd_out.oms = oms | ||||
|                     n_temp = nd_out | ||||
|                     nd_out = next(n[1] for n in network.edges([n_temp]) if n[1].uid != nd_in.uid) | ||||
|                     nd_in = n_temp | ||||
|  | ||||
|                 oms.add_element(nd_out) | ||||
|                 # nd_out is a Roadm | ||||
|                 try: | ||||
|                     nd_out.oms_list.append(oms_id) | ||||
|                 except AttributeError: | ||||
|                     nd_out.oms_list = [] | ||||
|                     nd_out.oms_list.append(oms_id) | ||||
|  | ||||
|                 oms.update_spectrum(equipment['SI']['default'].f_min, | ||||
|                                     equipment['SI']['default'].f_max, grid=0.00625e12) | ||||
|                 # oms.assign_spectrum(13,7) gives back (193137500000000.0, 193225000000000.0) | ||||
|                 # as in the example in the standard | ||||
|                 # oms.assign_spectrum(13,7) | ||||
|  | ||||
|                 oms_list.append(oms) | ||||
|                 oms_id += 1 | ||||
|     oms_list = align_grids(oms_list) | ||||
|     reversed_oms(oms_list) | ||||
|     return oms_list | ||||
|  | ||||
| def reversed_oms(oms_list): | ||||
|     """ identifies reversed OMS | ||||
|         only applicable for non parallel OMS | ||||
|     """ | ||||
|     for oms in oms_list: | ||||
|         has_reversed = False | ||||
|         for this_o in oms_list: | ||||
|             if (oms.el_id_list[0] == this_o.el_id_list[-1] and | ||||
|                     oms.el_id_list[-1] == this_o.el_id_list[0]): | ||||
|                 oms.reversed_oms = this_o | ||||
|                 has_reversed = True | ||||
|                 break | ||||
|         if not has_reversed: | ||||
|             oms.reversed_oms = None | ||||
|  | ||||
|  | ||||
| def bitmap_sum(band1, band2): | ||||
|     """ a functions that marks occupied bitmap by 0 if the slot is occupied in band1 or in band2 | ||||
|     """ | ||||
|     res = [] | ||||
|     for i, elem in enumerate(band1): | ||||
|         if band2[i] * elem == 0: | ||||
|             res.append(0) | ||||
|         else: | ||||
|             res.append(1) | ||||
|     return res | ||||
|  | ||||
| def spectrum_selection(pth, oms_list, requested_m, requested_n=None): | ||||
|     """ collects spectrum availability and call the select_candidate function | ||||
|     # step 1 collects pth spectrum availability | ||||
|     # step 2 if n is not None try to assign the spectrum | ||||
|     #            if the spectrum is not available then sends back an "error" | ||||
|     #        if n is None selects candidate spectrum | ||||
|     #            select spectrum that fits the policy ( first fit, random, ABP...) | ||||
|     # step3 returns the selection | ||||
|     """ | ||||
|  | ||||
|     # use indexes instead of ITU-T n values | ||||
|     path_oms = [] | ||||
|     for elem in pth: | ||||
|         if not isinstance(elem, Roadm) and not isinstance(elem, Transceiver): | ||||
|             # only edfa, fused and fibers have oms_id attribute | ||||
|             path_oms.append(elem.oms_id) | ||||
|     # remove duplicate oms_id, order is not important | ||||
|     path_oms = list(set(path_oms)) | ||||
|     # assuming all oms have same freq index | ||||
|     if not path_oms: | ||||
|         candidate = (None, None, None) | ||||
|         return candidate, path_oms | ||||
|     freq_index = oms_list[path_oms[0]].spectrum_bitmap.freq_index | ||||
|     freq_index_min = oms_list[path_oms[0]].spectrum_bitmap.freq_index_min | ||||
|     freq_index_max = oms_list[path_oms[0]].spectrum_bitmap.freq_index_max | ||||
|  | ||||
|     freq_availability = oms_list[path_oms[0]].spectrum_bitmap.bitmap | ||||
|     for oms in path_oms[1:]: | ||||
|         freq_availability = bitmap_sum(oms_list[oms].spectrum_bitmap.bitmap, freq_availability) | ||||
|     if requested_n is None: | ||||
|         # avoid slots reserved on the edge 0.15e-12 on both sides -> 24 | ||||
|         candidates = [(freq_index[i]+requested_m, freq_index[i], freq_index[i]+2*requested_m-1) | ||||
|                       for i in range(len(freq_availability)) | ||||
|                       if freq_availability[i:i+2*requested_m] == [1] * (2*requested_m) | ||||
|                       and freq_index[i] >= freq_index_min | ||||
|                       and freq_index[i+2*requested_m-1] <= freq_index_max] | ||||
|  | ||||
|         candidate = select_candidate(candidates, policy='first_fit') | ||||
|     else: | ||||
|         i = oms_list[path_oms[0]].spectrum_bitmap.geti(requested_n) | ||||
|         # print(f'N {requested_n} i {i}') | ||||
|         # print(freq_availability[i-m:i+m] ) | ||||
|         # print(freq_index[i-m:i+m]) | ||||
|         if (freq_availability[i-requested_m:i+requested_m] == [1] * (2*requested_m) and | ||||
|                 freq_index[i-requested_m] >= freq_index_min | ||||
|                       and freq_index[i+requested_m-1] <= freq_index_max): | ||||
|             # candidate is the triplet center_n, startn and stopn | ||||
|             candidate = (requested_n, requested_n-requested_m, requested_n+requested_m-1) | ||||
|         else: | ||||
|             candidate = (None, None, None) | ||||
|         # print("coucou11") | ||||
|         # print(candidate) | ||||
|     # print(freq_availability[321:321+2*m]) | ||||
|     # a = [i+321 for i in range(2*m)] | ||||
|     # print(a) | ||||
|     # print(candidate) | ||||
|     return candidate, path_oms | ||||
|  | ||||
| def select_candidate(candidates, policy): | ||||
|     """ selects a candidate among all available spectrum | ||||
|     """ | ||||
|     if policy == 'first_fit': | ||||
|         if candidates: | ||||
|             return candidates[0] | ||||
|         else: | ||||
|             return (None, None, None) | ||||
|     else: | ||||
|         raise ServiceError('Only first_fit spectrum assignment policy is implemented.') | ||||
|  | ||||
| def pth_assign_spectrum(pths, rqs, oms_list, rpths): | ||||
|     """ basic first fit assignment | ||||
|         if reversed path are provided, means that occupation is bidir | ||||
|     """ | ||||
|     for i, pth in enumerate(pths): | ||||
|         # computes the number of channels required | ||||
|         try: | ||||
|             if rqs[i].blocking_reason: | ||||
|                 rqs[i].blocked = True | ||||
|                 rqs[i].N = 0 | ||||
|                 rqs[i].M = 0 | ||||
|         except AttributeError: | ||||
|             nb_wl = ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) | ||||
|             # computes the total nb of slots according to requested spacing | ||||
|             # TODO : express superchannels | ||||
|             # assumes that all channels must be grouped | ||||
|             # TODO : enables non contiguous reservation in case of blocking | ||||
|             requested_m = ceil(rqs[i].spacing / 0.0125e12) * nb_wl | ||||
|             # concatenate all path and reversed path elements to derive slots availability | ||||
|             (center_n, startn, stopn), path_oms = spectrum_selection(pth + rpths[i], oms_list, requested_m, | ||||
|                                                                      requested_n=None) | ||||
|             # checks that requested_m is fitting startm and stopm | ||||
|             # if not None, center_n and start, stop frequencies are applicable to all oms of pth | ||||
|             # checks that spectrum is not None else indicate blocking reason | ||||
|             if center_n is not None: | ||||
|                 # checks that requested_m is fitting startm and stopm | ||||
|                 if 2 * requested_m > (stopn - startn + 1): | ||||
|                     msg = f'candidate: {(center_n, startn, stopn)} is not consistant ' +\ | ||||
|                           f'with {requested_m}' | ||||
|                     LOGGER.critical(msg) | ||||
|                     raise ValueError(msg) | ||||
|  | ||||
|                 for oms_elem in path_oms: | ||||
|                     oms_list[oms_elem].assign_spectrum(center_n, requested_m) | ||||
|                     oms_list[oms_elem].add_service(rqs[i].request_id, nb_wl) | ||||
|                 rqs[i].blocked = False | ||||
|                 rqs[i].N = center_n | ||||
|                 rqs[i].M = requested_m | ||||
|             else: | ||||
|                 rqs[i].blocked = True | ||||
|                 rqs[i].N = 0 | ||||
|                 rqs[i].M = 0 | ||||
|                 rqs[i].blocking_reason = 'NO_SPECTRUM' | ||||
| @@ -11,8 +11,8 @@ This module contains utility functions that are used with gnpy. | ||||
|  | ||||
| import json | ||||
|  | ||||
| import numpy as np | ||||
| from csv import writer | ||||
| import numpy as np | ||||
| from numpy import pi, cos, sqrt, log10 | ||||
| from scipy import constants | ||||
|  | ||||
| @@ -73,21 +73,19 @@ def c(): | ||||
|     return constants.c | ||||
|  | ||||
|  | ||||
| def itufs(spacing, startf=191.35, stopf=196.10): | ||||
|     """Creates an array of frequencies whose default range is | ||||
|     191.35-196.10 THz | ||||
| def arrange_frequencies(length, start, stop): | ||||
|     """Create an array of frequencies | ||||
|  | ||||
|     :param spacing: Frequency spacing in THz | ||||
|     :param starf: Start frequency in THz | ||||
|     :param stopf: Stop frequency in THz | ||||
|     :type spacing: float | ||||
|     :type startf: float | ||||
|     :type stopf: float | ||||
|     :return an array of frequnecies determined by the spacing parameter | ||||
|     :param length: number of elements | ||||
|     :param star: Start frequency in THz | ||||
|     :param stop: Stop frequency in THz | ||||
|     :type length: integer | ||||
|     :type start: float | ||||
|     :type stop: float | ||||
|     :return an array of frequencies determined by the spacing parameter | ||||
|     :rtype: numpy.ndarray | ||||
|     """ | ||||
|     return np.arange(startf, stopf + spacing / 2, spacing) | ||||
|  | ||||
|     return np.linspace(start, stop, length) | ||||
|  | ||||
| def h(): | ||||
|     """ | ||||
| @@ -185,3 +183,43 @@ def rrc(ffs, baud_rate, alpha): | ||||
|     p_inds = np.where(np.logical_and(np.abs(ffs) > 0, np.abs(ffs) < l_lim)) | ||||
|     hf[p_inds] = 1 | ||||
|     return sqrt(hf) | ||||
|  | ||||
| def merge_amplifier_restrictions(dict1, dict2): | ||||
|     """Updates contents of dicts recursively | ||||
|  | ||||
|     >>> d1 = {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}} | ||||
|     >>> d2 = {'params': {'target_pch_out_db': -20}} | ||||
|     >>> merge_amplifier_restrictions(d1, d2) | ||||
|     {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}, 'target_pch_out_db': -20}} | ||||
|  | ||||
|     >>> d3 = {'params': {'restrictions': {'preamp_variety_list': ['foo'], 'booster_variety_list': ['bar']}}} | ||||
|     >>> merge_amplifier_restrictions(d1, d3) | ||||
|     {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}} | ||||
|     """ | ||||
|  | ||||
|     copy_dict1 = dict1.copy() | ||||
|     for key in dict2: | ||||
|         if key in dict1: | ||||
|             if isinstance(dict1[key], dict): | ||||
|                 copy_dict1[key] = merge_amplifier_restrictions(copy_dict1[key], dict2[key]) | ||||
|         else: | ||||
|             copy_dict1[key] = dict2[key] | ||||
|     return copy_dict1 | ||||
|  | ||||
| def silent_remove(this_list, elem): | ||||
|     """Remove matching elements from a list without raising ValueError | ||||
|  | ||||
|     >>> li = [0, 1] | ||||
|     >>> li = silent_remove(li, 1) | ||||
|     >>> li | ||||
|     [0] | ||||
|     >>> li = silent_remove(li, 1) | ||||
|     >>> li | ||||
|     [0] | ||||
|     """ | ||||
|  | ||||
|     try: | ||||
|         this_list.remove(elem) | ||||
|     except ValueError: | ||||
|         pass | ||||
|     return this_list | ||||
|   | ||||
| @@ -23,7 +23,7 @@ type’][‘subtype’]=object** | ||||
| Every equipment type is defined in JSON root with according name and | ||||
| array of parameters as value. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: none | ||||
|  | ||||
|     {"Edfa": [...], | ||||
|     "Fiber": [...] | ||||
| @@ -40,7 +40,7 @@ object contains **"type_variety":”type name”** name:value combination, | ||||
| if only one subtype exists **"type_variety"** name is not mandatory and | ||||
| it will be marked with **”default”** value. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json | ||||
|  | ||||
|     {"Edfa": [{ | ||||
|                 "type_variety": "std_medium_gain", | ||||
| @@ -88,7 +88,7 @@ location is in **transmission_main_example.py** folder: | ||||
|    gain/noise figure ripple. **"advanced_config_from_json"** value | ||||
|    contains filename. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json-object | ||||
|  | ||||
|     "Edfa":[{ | ||||
|             "type_variety": "high_detail_model_example", | ||||
| @@ -104,7 +104,7 @@ location is in **transmission_main_example.py** folder: | ||||
| -  Variable gain – with JSON file describing gain figure tilt and gain/noise | ||||
|    figure ripple. **”default_edfa_config.json”** as source file. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json-object | ||||
|  | ||||
|     "Edfa":[{ | ||||
|             "type_variety": "std_medium_gain", | ||||
| @@ -122,7 +122,7 @@ location is in **transmission_main_example.py** folder: | ||||
| -  Fixed gain – with JSON file describing gain figure tilt and gain/noise | ||||
|    figure ripple. **”default_edfa_config.json”** as source file. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json-object | ||||
|  | ||||
|     "Edfa":[{ | ||||
|             "type_variety": "std_fixed_gain", | ||||
| @@ -138,7 +138,7 @@ location is in **transmission_main_example.py** folder: | ||||
| - openroadm – with JSON file describing gain figure tilt and gain/noise | ||||
|    figure ripple. **”default_edfa_config.json”** as source file.  | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json-object | ||||
|  | ||||
|     "Edfa":[{ | ||||
|             "type_variety": "low_noise", | ||||
| @@ -156,7 +156,7 @@ location is in **transmission_main_example.py** folder: | ||||
|  | ||||
| Fiber element with its parameters: | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json-object | ||||
|  | ||||
|     "Fiber":[{ | ||||
|             "type_variety": "SSMF", | ||||
| @@ -165,12 +165,58 @@ Fiber element with its parameters: | ||||
|             } | ||||
|         ] | ||||
|  | ||||
| RamanFiber element | ||||
| ****************** | ||||
|  | ||||
| A special variant of the regular ``Fiber`` where the simulation engine accounts for the Raman effect. | ||||
| The newly added parameters are nested in the ``raman_efficiency`` dictionary. | ||||
| Its shape corresponds to typical properties of silica. | ||||
| More details are available from :cite:`curri_merit_2016`. | ||||
|  | ||||
| The ``cr`` property is the normailzed Raman efficiency, so it is is (almost) independent of the fiber type, while the coefficient actually giving Raman gain is g_R=C_R/Aeff. | ||||
|  | ||||
| The ``frequency_offset`` represents the spectral difference between the pumping photon and the one receiving energy. | ||||
|  | ||||
| .. code-block:: json-object | ||||
|  | ||||
|     "RamanFiber":[{ | ||||
|       "type_variety": "SSMF", | ||||
|       "dispersion": 1.67e-05, | ||||
|       "gamma": 0.00127, | ||||
|       "raman_efficiency": { | ||||
|         "cr":[ | ||||
|             0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119, | ||||
|             0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469, | ||||
|             0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826, | ||||
|             0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05, | ||||
|             7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05, | ||||
|             2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05, | ||||
|             2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05, | ||||
|             1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05, | ||||
|             1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07, | ||||
|             2E-07, 1E-07 | ||||
|         ], | ||||
|         "frequency_offset":[ | ||||
|           0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12, | ||||
|           7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12, | ||||
|           13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12, | ||||
|           17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12, | ||||
|           22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12, | ||||
|           28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12, | ||||
|           35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12, | ||||
|           41.5e12, 42e12 | ||||
|         ] | ||||
|         } | ||||
|       } | ||||
|     ] | ||||
|  | ||||
|  | ||||
| 1.2.3 Roadm element | ||||
| ******************* | ||||
|  | ||||
| Roadm element with its parameters: | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json-object | ||||
|  | ||||
|       "Roadms":[{ | ||||
|             "gain_mode_default_loss": 20, | ||||
| @@ -184,7 +230,7 @@ Roadm element with its parameters: | ||||
|  | ||||
| Spans element with its parameters: | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json-object | ||||
|  | ||||
|     "Spans":[{ | ||||
|             "power_mode":true, | ||||
| @@ -205,7 +251,7 @@ Spans element with its parameters: | ||||
|  | ||||
| Spectral information with its parameters: | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json-object | ||||
|  | ||||
|     "SI":[{ | ||||
|             "f_min": 191.3e12, | ||||
| @@ -227,7 +273,9 @@ Spectral information with its parameters: | ||||
| Transceiver element with its parameters. **”mode”** can contain multiple | ||||
| Transceiver operation formats. | ||||
|  | ||||
| .. code-block:: | ||||
| Note that ``OSNR`` parameter refers to the receiver's minimal OSNR threshold for a given mode. | ||||
|  | ||||
| .. code-block:: json-object | ||||
|  | ||||
|     "Transceiver":[{ | ||||
|                     "frequency":{ | ||||
| @@ -288,7 +336,7 @@ Network description JSON file root consist of three unordered parts: | ||||
|  | ||||
| -  connections – contains array of unidirectional connection objects | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: none | ||||
|  | ||||
|     {"network_name": "Example Network", | ||||
|     "elements": [{...}, | ||||
| @@ -317,10 +365,10 @@ obligatory. | ||||
|  | ||||
| Transceiver element with its parameters. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json | ||||
|  | ||||
|     {"uid": "trx Site_A", | ||||
|     “metadata": { | ||||
|     "metadata": { | ||||
|                 "location": { | ||||
|                             "city": "Site_A", | ||||
|                             "region": "", | ||||
| @@ -339,7 +387,7 @@ Transceiver element with its parameters. | ||||
| ROADM element with its parameters. **“params”** is optional, if not used | ||||
| default loss value of 20dB is used. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json | ||||
|  | ||||
|     {"uid": "roadm Site_A", | ||||
|     "metadata": { | ||||
| @@ -363,12 +411,12 @@ default loss value of 20dB is used. | ||||
| Fused element with its parameters. **“params”** is optional, if not used | ||||
| default loss value of 1dB is used. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json | ||||
|  | ||||
|     {"uid": "ingress fused spans in Site_B", | ||||
|     "metadata": { | ||||
|                 "location": { | ||||
|                             “city": "Site_B", | ||||
|                             "city": "Site_B", | ||||
|                             "region": "", | ||||
|                             "latitude": 0, | ||||
|                             "longitude": 0 | ||||
| @@ -386,7 +434,7 @@ default loss value of 1dB is used. | ||||
|  | ||||
| Fiber element with its parameters. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json | ||||
|  | ||||
|     {"uid": "fiber (Site_A \\u2192 Site_B)", | ||||
|     "metadata": { | ||||
| @@ -406,13 +454,56 @@ Fiber element with its parameters. | ||||
|                 } | ||||
|     } | ||||
|  | ||||
| 2.2.5. RamanFiber element | ||||
| ************************* | ||||
|  | ||||
| 2.2.5. EDFA element | ||||
| .. code-block:: json | ||||
|  | ||||
|     { | ||||
|       "uid": "Span1", | ||||
|       "type": "RamanFiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "operational": { | ||||
|         "temperature": 283, | ||||
|         "raman_pumps": [ | ||||
|           { | ||||
|             "power": 200e-3, | ||||
|             "frequency": 205e12, | ||||
|             "propagation_direction": "counterprop" | ||||
|           }, | ||||
|           { | ||||
|             "power": 206e-3, | ||||
|             "frequency": 201e12, | ||||
|             "propagation_direction": "counterprop" | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       "params": { | ||||
|         "type_variety": "SSMF", | ||||
|         "length": 80.0, | ||||
|         "loss_coef": 0.2, | ||||
|         "length_units": "km", | ||||
|         "att_in": 0, | ||||
|         "con_in": 0.5, | ||||
|         "con_out": 0.5 | ||||
|       }, | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1, | ||||
|           "longitude": 0, | ||||
|           "city": null, | ||||
|           "region": "" | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|  | ||||
| 2.2.6. EDFA element | ||||
| ******************** | ||||
|  | ||||
| EDFA element with its parameters. | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json | ||||
|  | ||||
|     {"uid": "Edfa1", | ||||
|     "type": "Edfa", | ||||
| @@ -438,8 +529,31 @@ Each unidirectional connection object in connections array consist of | ||||
| two unordered **”from_node”** and **”to_node”** name pair with values | ||||
| corresponding to element **”uid”** | ||||
|  | ||||
| .. code-block:: | ||||
| .. code-block:: json | ||||
|  | ||||
|     {"from_node": "roadm Site_C", | ||||
|     "to_node": "trx Site_C" | ||||
|     } | ||||
|  | ||||
| ************************ | ||||
| 3. Simulation Parameters | ||||
| ************************ | ||||
|  | ||||
| Additional details of the simulation are controlled via ``sim_params.json``: | ||||
|  | ||||
| .. code-block:: json | ||||
|  | ||||
|   { | ||||
|     "raman_computed_channels": [1, 18, 37, 56, 75], | ||||
|     "raman_parameters": { | ||||
|       "flag_raman": true, | ||||
|       "space_resolution": 10e3, | ||||
|       "tolerance": 1e-8 | ||||
|     }, | ||||
|     "nli_parameters": { | ||||
|       "nli_method_name": "ggn_spectrally_separated", | ||||
|       "wdm_grid_size": 50e9, | ||||
|       "dispersion_tolerance": 1, | ||||
|       "phase_shift_tollerance": 0.1 | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -1,40 +1,62 @@ | ||||
| { | ||||
|   "paths": [ | ||||
|   "response": [ | ||||
|     { | ||||
|       "path": { | ||||
|         "path-id": null, | ||||
|       "response-id": null, | ||||
|       "path-properties": { | ||||
|         "path-metric": [ | ||||
|           { | ||||
|               "metric-type": null, | ||||
|             "metric-type": "SNR@bandwidth", | ||||
|             "accumulative-value": null | ||||
|           }, | ||||
|           { | ||||
|             "metric-type": "SNR@0.1nm", | ||||
|             "accumulative-value": null | ||||
|           }, | ||||
|           { | ||||
|             "metric-type": "OSNR@bandwidth", | ||||
|             "accumulative-value": null | ||||
|           }, | ||||
|           { | ||||
|             "metric-type": "OSNR@0.1nm", | ||||
|             "accumulative-value": null | ||||
|           }, | ||||
|           { | ||||
|             "metric-type": "reference_power", | ||||
|             "accumulative-value": null | ||||
|           }, | ||||
|           { | ||||
|             "metric-type": "path_bandwidth", | ||||
|             "accumulative-value": null | ||||
|           } | ||||
|         ], | ||||
|           "path-srlgs": { | ||||
|             "usage": "not used yet", | ||||
|             "values": ["not used yet"] | ||||
|           }, | ||||
|         "path-route-objects": [ | ||||
|           { | ||||
|             "path-route-object": { | ||||
|                 "index": null, | ||||
|                 "unnumbered-hop": { | ||||
|               "index": 0, | ||||
|               "num-unnum-hop": { | ||||
|                 "node-id": null, | ||||
|                   "link-tp-id": null, | ||||
|                   "hop-type": null, | ||||
|                   "direction": "not used" | ||||
|                 "link-tp-id": null | ||||
|               } | ||||
|             } | ||||
|           }, | ||||
|                 "label-hop": { | ||||
|                   "te-label": { | ||||
|                     "generic": "not used yet", | ||||
|                     "direction": "not used yet" | ||||
|           { | ||||
|             "path-route-object": { | ||||
|               "index": 1, | ||||
|               "transponder": { | ||||
|                 "transponder-type": null, | ||||
|                 "transponder-mode": null | ||||
|               } | ||||
|             } | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|           }, | ||||
|           { | ||||
|             "path-route-object": { | ||||
|               "index": 2, | ||||
|               "num-unnum-hop": { | ||||
|                 "node-id": null, | ||||
|                 "link-tp-id": null | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
| } | ||||
|       } | ||||
|     },       | ||||
| @@ -1,2 +1,2 @@ | ||||
| [pytest] | ||||
| addopts = -p no:warnings | ||||
| addopts = --doctest-modules | ||||
|   | ||||
| @@ -1,44 +1,14 @@ | ||||
| alabaster==0.7.12 | ||||
| appdirs==1.4.3 | ||||
| atomicwrites==1.2.1 | ||||
| attrs==18.2.0 | ||||
| Babel==2.6.0 | ||||
| black==18.9b0 | ||||
| certifi==2018.10.15 | ||||
| chardet==3.0.4 | ||||
| Click==7.0 | ||||
| cycler==0.10.0 | ||||
| decorator==4.3.0 | ||||
| docutils==0.14 | ||||
| idna==2.7 | ||||
| imagesize==1.1.0 | ||||
| Jinja2==2.10 | ||||
| kiwisolver==1.0.1 | ||||
| latexcodec==1.0.5 | ||||
| MarkupSafe==1.0 | ||||
| matplotlib==3.0.0 | ||||
| more-itertools==4.3.0 | ||||
| networkx==2.2 | ||||
| numpy==1.15.2 | ||||
| oset==0.1.3 | ||||
| packaging==18.0 | ||||
| pluggy==0.7.1 | ||||
| py==1.7.0 | ||||
| pybtex==0.21 | ||||
| pybtex-docutils==0.2.1 | ||||
| Pygments==2.2.0 | ||||
| pyparsing==2.2.2 | ||||
| pytest==3.8.2 | ||||
| python-dateutil==2.7.3 | ||||
| pytz==2018.5 | ||||
| PyYAML==3.13 | ||||
| requests==2.19.1 | ||||
| scipy==1.1.0 | ||||
| six==1.11.0 | ||||
| snowballstemmer==1.2.1 | ||||
| Sphinx==1.8.1 | ||||
| sphinxcontrib-bibtex==0.4.0 | ||||
| sphinxcontrib-websupport==1.1.0 | ||||
| toml==0.10.0 | ||||
| urllib3==1.23 | ||||
| xlrd==1.1.0 | ||||
| alabaster>=0.7.12,<1 | ||||
| docutils==0.15.2 | ||||
| flask==1.0.2 | ||||
| flask-restful==0.3.7 | ||||
| matplotlib>=3.1.0,<4 | ||||
| networkx>=2.3,<3 | ||||
| numpy>=1.16.1,<2 | ||||
| pandas==0.24.2 | ||||
| Pygments>=2.4.2,<3 | ||||
| pytest>=4.0.0,<5 | ||||
| scipy>=1.3.0,<2 | ||||
| Sphinx>=2.1.1,<3 | ||||
| sphinxcontrib-bibtex>=0.4.2,<1 | ||||
| xlrd>=1.2.0,<2 | ||||
|   | ||||
| @@ -6,54 +6,71 @@ | ||||
|       "destination": null, | ||||
|       "src-tp-id": null, | ||||
|       "dst-tp-id": null, | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": null, | ||||
|             "index": null, | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": null, | ||||
|               "link-tp-id": null, | ||||
|               "hop-type": null | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": null, | ||||
|             "index": null, | ||||
|             "label-hop": { | ||||
|               "N": null, | ||||
|               "M": null | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": null, | ||||
|             "index": null, | ||||
|             "transponder": { | ||||
|               "transponder-type": null, | ||||
|               "transponder-mode": null | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": null, | ||||
|             "index": null, | ||||
|             "regenerator": { | ||||
|               "regenerator-id": null, | ||||
|               "transponder-type": null, | ||||
|               "transponder-mode": null | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       },       | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": null, | ||||
|           "trx_mode": null, | ||||
|           "trx_type": "name of the tsp type_variety as listed in the library", | ||||
|           "trx_mode": "optional, name of the mode as listed in the tsp type_variety", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": null, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": null | ||||
|           "spacing": mandatory decimal Hz, | ||||
|           "max-nb-of-channel": optional integer, | ||||
|           "output-power": optional decimal W, | ||||
|           "path_bandwidth": optional bit/s | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": { | ||||
|           "route-object-include-object": [ | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "synchronization": [   list of disjunctions, optional | ||||
|     { | ||||
|             "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-id": null, | ||||
|       "synchronization-id": "3", | ||||
|       "svec": { | ||||
|         "relaxable": "True", | ||||
|         "link-diverse": "False", | ||||
|         "node-diverse": "False", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|          null ] | ||||
|          null, null ] | ||||
|         }, | ||||
|     } | ||||
|   ]   | ||||
|   | ||||
							
								
								
									
										4
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								setup.py
									
									
									
									
									
								
							| @@ -11,13 +11,13 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f: | ||||
|  | ||||
| setup( | ||||
|     name='gnpy', | ||||
|     version='1.2.0', | ||||
|     version='2.1', | ||||
|     description='route planning and optimization tool for mesh optical networks', | ||||
|     long_description=long_description, | ||||
|     long_description_content_type='text/x-rst; charset=UTF-8', | ||||
|     url='https://github.com/Telecominfraproject/gnpy', | ||||
|     author='Telecom Infra Project', | ||||
|     author_email='james.powell@telecominfraproject.com', | ||||
|     author_email='jan.kundrat@telecominfraproject.com', | ||||
|     classifiers=[ | ||||
|         'Development Status :: 3 - Alpha', | ||||
|         'Intended Audience :: Developers', | ||||
|   | ||||
| @@ -205,6 +205,36 @@ | ||||
|                     "longitude": 0 | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|         "uid": "Att_B", | ||||
|         "type": "Fused", | ||||
|         "params":{ | ||||
|             "loss":16 | ||||
|         }, | ||||
|         "metadata": { | ||||
|             "location": { | ||||
|               "latitude": 2.0, | ||||
|               "longitude": 1.0, | ||||
|               "city": "Corlay", | ||||
|               "region": "RLD" | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|         "uid": "Att_F", | ||||
|         "type": "Fused", | ||||
|         "params":{ | ||||
|             "loss":16 | ||||
|         }, | ||||
|         "metadata": { | ||||
|             "location": { | ||||
|               "latitude": 2.0, | ||||
|               "longitude": 1.0, | ||||
|               "city": "Corlay", | ||||
|               "region": "RLD" | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   ], | ||||
| @@ -247,6 +277,10 @@ | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Edfa5", | ||||
|       "to_node": "Att_F" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Att_F", | ||||
|       "to_node": "trx F" | ||||
|     }, | ||||
|     { | ||||
| @@ -255,6 +289,10 @@ | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Edfa1", | ||||
|       "to_node": "Att_B" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "Att_B", | ||||
|       "to_node": "trx B" | ||||
|     } | ||||
|   ] | ||||
|   | ||||
| @@ -77,6 +77,9 @@ def compare_networks(expected, actual): | ||||
| def compare_services(expected, actual): | ||||
|     requests = compare(expected['path-request'], actual['path-request'], | ||||
|                        key=lambda el: el['request-id']) | ||||
|     synchronizations = compare(expected['path-request'], expected['path-request'], | ||||
|                                key=lambda el: el['request-id']) | ||||
|     if 'synchronization' in expected.keys(): | ||||
|         synchronizations = compare(expected['synchronization'], actual['synchronization'], | ||||
|                                    key=lambda el: el['synchronization-id']) | ||||
|     return ServicesResults(requests, synchronizations) | ||||
|   | ||||
							
								
								
									
										196470
									
								
								tests/data/CORONET_Global_Topology_auto_design_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196470
									
								
								tests/data/CORONET_Global_Topology_auto_design_expected.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,9 +1,11 @@ | ||||
| {     "Edfa":[{ | ||||
|             "type_variety": "CienaDB_medium_gain", | ||||
|             "type_def": "advanced_model", | ||||
|             "gain_flatmax": 25, | ||||
|             "gain_min": 15, | ||||
|             "p_max": 21, | ||||
|             "advanced_config_from_json": "std_medium_gain_advanced_config.json", | ||||
|             "out_voa_auto": false, | ||||
|             "allowed_for_design": true | ||||
|             }, | ||||
|             { | ||||
| @@ -14,6 +16,7 @@ | ||||
|             "p_max": 21, | ||||
|             "nf_min": 6, | ||||
|             "nf_max": 10, | ||||
|             "out_voa_auto": false, | ||||
|             "allowed_for_design": true | ||||
|             }, | ||||
|             { | ||||
| @@ -24,6 +27,7 @@ | ||||
|             "p_max": 21, | ||||
|             "nf_min": 7, | ||||
|             "nf_max": 11, | ||||
|             "out_voa_auto": false, | ||||
|             "allowed_for_design": true | ||||
|             }, | ||||
|             { | ||||
| @@ -34,6 +38,7 @@ | ||||
|             "p_max": 21, | ||||
|             "nf_min": 5.8, | ||||
|             "nf_max": 10, | ||||
|             "out_voa_auto": false, | ||||
|             "allowed_for_design": true | ||||
|             }, | ||||
|             { | ||||
| @@ -44,6 +49,15 @@ | ||||
|             "p_max": 21, | ||||
|             "nf0": 5, | ||||
|             "allowed_for_design": true | ||||
|             }, | ||||
|             { | ||||
|             "type_variety": "std_booster", | ||||
|             "type_def": "fixed_gain", | ||||
|             "gain_flatmax": 21, | ||||
|             "gain_min": 20, | ||||
|             "p_max": 21, | ||||
|             "nf0": 5, | ||||
|             "allowed_for_design": false | ||||
|             }             | ||||
|       ], | ||||
|       "Fiber":[{ | ||||
| @@ -52,9 +66,11 @@ | ||||
|             "gamma": 0.00127 | ||||
|             } | ||||
|       ], | ||||
|       "Spans":[{ | ||||
|             "power_mode": true, | ||||
|             "delta_power_range_db": [0,0,1], | ||||
|       "Span":[{ | ||||
|             "power_mode":true, | ||||
|             "delta_power_range_db": [0,0,0.5], | ||||
|             "max_fiber_lineic_loss_for_raman": 0.25, | ||||
|             "target_extended_gain": 2.5, | ||||
|             "max_length": 150, | ||||
|             "length_units": "km", | ||||
|             "max_loss": 28, | ||||
| @@ -64,10 +80,13 @@ | ||||
|             "con_out": 0 | ||||
|             } | ||||
|       ], | ||||
|       "Roadms":[{ | ||||
|             "gain_mode_default_loss": 20, | ||||
|             "power_mode_pout_target": -20, | ||||
|             "add_drop_osnr": 100 | ||||
|       "Roadm":[{ | ||||
|             "target_pch_out_db": -20, | ||||
|             "add_drop_osnr": 38, | ||||
|             "restrictions": { | ||||
|                             "preamp_variety_list":[], | ||||
|                             "booster_variety_list":[] | ||||
|                             }     | ||||
|             }], | ||||
|       "SI":[{ | ||||
|             "f_min": 191.3e12, | ||||
| @@ -75,7 +94,7 @@ | ||||
|             "baud_rate": 32e9, | ||||
|             "spacing": 50e9, | ||||
|             "power_dbm": 0, | ||||
|             "power_range_db": [0,0.5,0.5], | ||||
|             "power_range_db": [0,0,0.5], | ||||
|             "roll_off": 0.15, | ||||
|             "tx_osnr": 100, | ||||
|             "sys_margins": 0             | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| @@ -1,698 +0,0 @@ | ||||
| { | ||||
|   "elements": [ | ||||
|     { | ||||
|       "uid": "trx Lannion_CAS", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Vannes_KBE", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Vannes_KBE", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Rennes_STA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Rennes_STA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Brest_KLA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Brest_KLA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Lannion_CAS", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Vannes_KBE", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Vannes_KBE", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Rennes_STA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Rennes_STA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Brest_KLA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Brest_KLA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west fused spans in Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Corlay", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west fused spans in Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Loudeac", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east fused spans in Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Corlay", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east fused spans in Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Loudeac", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lannion_CAS → Corlay)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 20.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Corlay → Loudeac)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 50.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Loudeac → Lorient_KMA)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lorient_KMA → Vannes_KBE)-F01", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 10.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lannion_CAS → Stbrieuc)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Stbrieuc → Rennes_STA)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 65.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lannion_CAS → Morlaix)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 40.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Morlaix → Brest_KLA)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 35.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Corlay → Lannion_CAS)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 20.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Loudeac → Corlay)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 50.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lorient_KMA → Loudeac)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Vannes_KBE → Lorient_KMA)-F01", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 10.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Stbrieuc → Lannion_CAS)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Rennes_STA → Stbrieuc)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 65.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Morlaix → Lannion_CAS)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 40.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Brest_KLA → Morlaix)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 35.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lannion_CAS to Morlaix", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "test", | ||||
|       "operational": { | ||||
|         "gain_target": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lannion_CAS to Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "test", | ||||
|       "operational": { | ||||
|         "gain_target": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lannion_CAS to Stbrieuc", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "test", | ||||
|       "operational": { | ||||
|         "gain_target": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Corlay to Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Corlay", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "test", | ||||
|       "operational": { | ||||
|         "gain_target": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "connections": [ | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "east edfa in Lannion_CAS to Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lannion_CAS to Corlay", | ||||
|       "to_node": "fiber (Lannion_CAS → Corlay)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Corlay → Lannion_CAS)-", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "east edfa in Lannion_CAS to Stbrieuc" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lannion_CAS to Stbrieuc", | ||||
|       "to_node": "fiber (Lannion_CAS → Stbrieuc)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Stbrieuc → Lannion_CAS)-", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "east edfa in Lannion_CAS to Morlaix" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lannion_CAS to Morlaix", | ||||
|       "to_node": "fiber (Lannion_CAS → Morlaix)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Morlaix → Lannion_CAS)-", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Corlay)-", | ||||
|       "to_node": "west fused spans in Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west fused spans in Corlay", | ||||
|       "to_node": "fiber (Corlay → Loudeac)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Loudeac → Corlay)-", | ||||
|       "to_node": "east fused spans in Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east fused spans in Corlay", | ||||
|       "to_node": "fiber (Corlay → Lannion_CAS)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Corlay → Loudeac)-", | ||||
|       "to_node": "west fused spans in Loudeac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west fused spans in Loudeac", | ||||
|       "to_node": "fiber (Loudeac → Lorient_KMA)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lorient_KMA → Loudeac)-", | ||||
|       "to_node": "east fused spans in Loudeac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east fused spans in Loudeac", | ||||
|       "to_node": "fiber (Loudeac → Corlay)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "fiber (Lorient_KMA → Loudeac)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Loudeac → Lorient_KMA)-", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F01" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F01", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Vannes_KBE", | ||||
|       "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F01" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F01", | ||||
|       "to_node": "roadm Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Stbrieuc)-", | ||||
|       "to_node": "fiber (Stbrieuc → Rennes_STA)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Rennes_STA → Stbrieuc)-", | ||||
|       "to_node": "fiber (Stbrieuc → Lannion_CAS)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Rennes_STA", | ||||
|       "to_node": "fiber (Rennes_STA → Stbrieuc)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Stbrieuc → Rennes_STA)-", | ||||
|       "to_node": "roadm Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Morlaix)-", | ||||
|       "to_node": "fiber (Morlaix → Brest_KLA)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Brest_KLA → Morlaix)-", | ||||
|       "to_node": "fiber (Morlaix → Lannion_CAS)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Brest_KLA", | ||||
|       "to_node": "fiber (Brest_KLA → Morlaix)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Morlaix → Brest_KLA)-", | ||||
|       "to_node": "roadm Brest_KLA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Lannion_CAS", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "trx Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Lorient_KMA", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "trx Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Vannes_KBE", | ||||
|       "to_node": "roadm Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Vannes_KBE", | ||||
|       "to_node": "trx Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Rennes_STA", | ||||
|       "to_node": "roadm Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Rennes_STA", | ||||
|       "to_node": "trx Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Brest_KLA", | ||||
|       "to_node": "roadm Brest_KLA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Brest_KLA", | ||||
|       "to_node": "trx Brest_KLA" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -1,84 +0,0 @@ | ||||
| { | ||||
|   "path-request": [ | ||||
|     { | ||||
|       "request-id": "0", | ||||
|       "source": "BREST_KLA", | ||||
|       "destination": "Vannes_KBE", | ||||
|       "src-tp-id": "trx BREST_KLA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager_16QAM", | ||||
|           "trx_mode": "16QAM", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "1", | ||||
|       "source": "Lorient_KMA", | ||||
|       "destination": "Vannes_KBE", | ||||
|       "src-tp-id": "trx Lorient_KMA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager_16QAM", | ||||
|           "trx_mode": "16QAM", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "synchronization": [ | ||||
|     { | ||||
|       "synchronization-id": "0", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "request-id-number": [ | ||||
|           "0", | ||||
|           "1" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "synchronization-id": "1", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "request-id-number": [ | ||||
|           "1", | ||||
|           "0" | ||||
|         ] | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										97
									
								
								tests/data/expected_results_science_utils.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								tests/data/expected_results_science_utils.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| ,signal,ase,nli | ||||
| 0,0.0002869472910750076,3.829243751386179e-08,2.157043502374111e-07 | ||||
| 1,0.000284426444181902,3.8108068606265256e-08,2.1799950841472648e-07 | ||||
| 2,0.0002819286625240274,3.7925434667811625e-08,2.2023841125044652e-07 | ||||
| 3,0.0002794537215642205,3.774451238936698e-08,2.224218994135113e-07 | ||||
| 4,0.0002756243295734432,3.739256063612741e-08,2.2343448272114653e-07 | ||||
| 5,0.0002718482755003954,3.7044477620123535e-08,2.2437826192962217e-07 | ||||
| 6,0.0002681247979313455,3.6700201831013766e-08,2.2525495466695055e-07 | ||||
| 7,0.0002644507001383656,3.635953568122817e-08,2.2606415187870565e-07 | ||||
| 8,0.0002608253488031495,3.602242321653821e-08,2.268074852150968e-07 | ||||
| 9,0.00025690468888571607,3.564391587795796e-08,2.2718285844824803e-07 | ||||
| 10,0.0002530414048173237,3.5269661038482016e-08,2.2749429758476786e-07 | ||||
| 11,0.0002492279873568786,3.4899736994459975e-08,2.277374766526846e-07 | ||||
| 12,0.0002454639458992114,3.4534068616323406e-08,2.2791414400784552e-07 | ||||
| 13,0.00024174879168999762,3.417258192135115e-08,2.280260208417629e-07 | ||||
| 14,0.00023798746912556782,3.3802278288721e-08,2.2798420759779948e-07 | ||||
| 15,0.00023427697848575827,3.3436265380528345e-08,2.2788101592690985e-07 | ||||
| 16,0.00023061678363205047,3.30744682841412e-08,2.2771816297652923e-07 | ||||
| 17,0.00022700656967542085,3.271682680678683e-08,2.2749755602884014e-07 | ||||
| 18,0.0002234457948096593,3.236326805537296e-08,2.236182244259085e-07 | ||||
| 19,0.0002195336193536736,3.195819496314336e-08,2.193976173454328e-07 | ||||
| 20,0.00021568313139087874,3.155821230359698e-08,2.1524945887103656e-07 | ||||
| 21,0.00021189361260563733,3.116322489050993e-08,2.1117277567390236e-07 | ||||
| 22,0.00020816423698459606,3.0773141693336075e-08,2.0716649124094935e-07 | ||||
| 23,0.0002044941867087381,3.038787321635763e-08,2.032295417993187e-07 | ||||
| 24,0.00020116081520673765,3.00440338127331e-08,1.9963693210324778e-07 | ||||
| 25,0.00019787569461895006,2.9704199888387147e-08,1.9610141536963145e-07 | ||||
| 26,0.00019463824873065924,2.9368302916351224e-08,1.9262221997372471e-07 | ||||
| 27,0.0001914486066928752,2.903632427420397e-08,1.8919927457565086e-07 | ||||
| 28,0.00018830616497930887,2.870819640079397e-08,1.858317840670677e-07 | ||||
| 29,0.00018521032563368435,2.838385281897912e-08,1.8251896218718178e-07 | ||||
| 30,0.00018216049720979434,2.8063228018898468e-08,1.7926003240909075e-07 | ||||
| 31,0.0001791561867005718,2.7746255438682553e-08,1.76054318231933e-07 | ||||
| 32,0.00017619680881744213,2.7432871709278503e-08,1.7290105534292413e-07 | ||||
| 33,0.00017328178390236163,2.7123014438128492e-08,1.6979948820364567e-07 | ||||
| 34,0.00017049664136784971,2.6828118382010868e-08,1.668331233176527e-07 | ||||
| 35,0.0001677518922618999,2.6536524600591003e-08,1.639139770351797e-07 | ||||
| 36,0.00016504703499520338,2.6248178236430935e-08,1.6104139135571758e-07 | ||||
| 37,0.0001623826677977635,2.596311344676757e-08,1.579538179464147e-07 | ||||
| 38,0.0001597582427278653,2.5681275450827438e-08,1.549209871570718e-07 | ||||
| 39,0.0001571732182028194,2.5402610321183817e-08,1.5194201541886346e-07 | ||||
| 40,0.00015462705891566638,2.512706495768609e-08,1.490160317195833e-07 | ||||
| 41,0.00015212101646392648,2.4854546722771583e-08,1.4614388817377845e-07 | ||||
| 42,0.00014965447757986727,2.4585006051161647e-08,1.4332463586636234e-07 | ||||
| 43,0.00014722683809507942,2.4318394065447274e-08,1.4055734193947907e-07 | ||||
| 44,0.0001447164668892396,2.4034548127308286e-08,1.3772590008270512e-07 | ||||
| 45,0.00014224784112375704,2.3753926686114635e-08,1.3494914625939818e-07 | ||||
| 46,0.00013982028367499942,2.3476475779461364e-08,1.3222606385780792e-07 | ||||
| 47,0.00013743418748445304,2.3202244204140228e-08,1.2955665313419502e-07 | ||||
| 48,0.00013508884015386575,2.2931178307200807e-08,1.269398709602497e-07 | ||||
| 49,0.00013278354172499636,2.2663225269637508e-08,1.243746944213211e-07 | ||||
| 50,0.0001305176041972383,2.2398333101097452e-08,1.2186012017916144e-07 | ||||
| 51,0.00012829168984639723,2.2136419884279648e-08,1.1939640981690787e-07 | ||||
| 52,0.00012610506317956035,2.1877436733290284e-08,1.169825203056231e-07 | ||||
| 53,0.000123957002859191,2.1621335420785434e-08,1.1461743054419468e-07 | ||||
| 54,0.00012180241033649304,2.1360152817604167e-08,1.1225922783038433e-07 | ||||
| 55,0.00011968650905779935,2.1101906890578305e-08,1.0994951537259513e-07 | ||||
| 56,0.000117608577762061,2.0846548870078847e-08,1.0757395097864581e-07 | ||||
| 57,0.00011556891128259058,2.0594151467353748e-08,1.0524972555992308e-07 | ||||
| 58,0.00011356676177301841,2.0344667169015006e-08,1.0297570549831857e-07 | ||||
| 59,0.00011160139690545192,2.00980493433389e-08,1.0075078305548045e-07 | ||||
| 60,0.00010967209909252646,1.985425227516509e-08,9.857387536569511e-08 | ||||
| 61,0.00010777915187087522,1.9613208260272527e-08,9.644480679616336e-08 | ||||
| 62,0.00010592181397175155,1.937487453011716e-08,9.436248424611683e-08 | ||||
| 63,0.00010409936038610526,1.913920913597429e-08,9.23258408012148e-08 | ||||
| 64,0.00010246447558375888,1.8936226281729442e-08,9.046927135291653e-08 | ||||
| 65,0.00010085803630104006,1.87354387522902e-08,8.865067925960373e-08 | ||||
| 66,9.927950010553608e-05,1.853681852284204e-08,8.686925127146881e-08 | ||||
| 67,9.772837346090978e-05,1.834034443508121e-08,8.512422533827548e-08 | ||||
| 68,9.620413430112097e-05,1.8145990199784238e-08,8.341482250639003e-08 | ||||
| 69,9.470627135913274e-05,1.795373041706864e-08,8.174028142913882e-08 | ||||
| 70,9.323428359797426e-05,1.776354066998682e-08,8.009985766376296e-08 | ||||
| 71,9.178813743816942e-05,1.7575386852678668e-08,7.849321446941785e-08 | ||||
| 72,9.03673300948529e-05,1.7389247191220127e-08,7.691961625609547e-08 | ||||
| 73,8.897136946427622e-05,1.7205101122769978e-08,7.537834446342857e-08 | ||||
| 74,8.760740745800998e-05,1.7025337039390582e-08,7.387513417420477e-08 | ||||
| 75,8.626710469266086e-05,1.684760610568072e-08,7.274492099363918e-08 | ||||
| 76,8.495000573672162e-05,1.6671894857242002e-08,7.163427447510873e-08 | ||||
| 77,8.365569697520994e-05,1.649819993412593e-08,7.054284583689279e-08 | ||||
| 78,8.238374036674246e-05,1.6326513144182658e-08,6.947026569965565e-08 | ||||
| 79,8.113370706498376e-05,1.6156829499842502e-08,6.841617243780552e-08 | ||||
| 80,7.990517700269747e-05,1.5989147949913657e-08,6.738021182874466e-08 | ||||
| 81,7.86978423091888e-05,1.5823469853370494e-08,6.636212425984957e-08 | ||||
| 82,7.751129541079691e-05,1.5659805288834794e-08,6.536156604375694e-08 | ||||
| 83,7.634513730458643e-05,1.549817228640182e-08,6.4378200720386e-08 | ||||
| 84,7.530262080974352e-05,1.5364274253504764e-08,6.349909645089537e-08 | ||||
| 85,7.427675504203847e-05,1.523236211656126e-08,6.263403294276386e-08 | ||||
| 86,7.326723873728748e-05,1.5102509684796054e-08,6.17827561543225e-08 | ||||
| 87,7.227232864621635e-05,1.497407531211962e-08,6.094379608688325e-08 | ||||
| 88,7.129179755315639e-05,1.4847053209180731e-08,6.011696114034632e-08 | ||||
| 89,7.032542203609286e-05,1.4721438007057792e-08,5.930206291361871e-08 | ||||
| 90,6.937298231674387e-05,1.4597224779058979e-08,5.8498916078193026e-08 | ||||
| 91,6.843339696762452e-05,1.4474430063551042e-08,5.7706608718023995e-08 | ||||
| 92,6.750649045006184e-05,1.435304906112738e-08,5.692499280974924e-08 | ||||
| 93,6.659208967850971e-05,1.4233077472549144e-08,5.615392239861094e-08 | ||||
| 94,6.554258932109723e-05,1.4075047005202515e-08,5.5268928972034715e-08 | ||||
| 95,6.450957734109015e-05,1.3918652473373596e-08,5.439783940505763e-08 | ||||
| 
 | 
										
											Binary file not shown.
										
									
								
							| @@ -1,806 +0,0 @@ | ||||
| { | ||||
|   "elements": [ | ||||
|     { | ||||
|       "uid": "trx Lannion_CAS", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Vannes_KBE", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Vannes_KBE", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 4.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Rennes_STA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Rennes_STA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Brest_KLA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Brest_KLA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 4.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx toto", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "toto", | ||||
|           "region": "", | ||||
|           "latitude": 6.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx tata", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "tata", | ||||
|           "region": "", | ||||
|           "latitude": 7.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Lannion_CAS", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Vannes_KBE", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Vannes_KBE", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 4.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Rennes_STA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Rennes_STA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Brest_KLA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Brest_KLA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 4.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm toto", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "toto", | ||||
|           "region": "", | ||||
|           "latitude": 6.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm tata", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "tata", | ||||
|           "region": "", | ||||
|           "latitude": 7.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west fused spans in Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Corlay", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west fused spans in Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Loudeac", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west fused spans in Morlaix", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Morlaix", | ||||
|           "region": "RLD", | ||||
|           "latitude": 3.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east fused spans in Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Corlay", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east fused spans in Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Loudeac", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east fused spans in Morlaix", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Morlaix", | ||||
|           "region": "RLD", | ||||
|           "latitude": 3.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lannion_CAS → Corlay)-F061", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 20.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Corlay → Loudeac)-F010", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 50.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Loudeac → Lorient_KMA)-F054", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lorient_KMA → Vannes_KBE)-F055", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 10.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lannion_CAS → Stbrieuc)-F056", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Stbrieuc → Rennes_STA)-F057", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 65.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lannion_CAS → Morlaix)-F059", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 40.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Morlaix → Brest_KLA)-F060", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 3.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 35.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (toto → tata)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 6.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 80.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Corlay → Lannion_CAS)-F061", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 20.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Loudeac → Corlay)-F010", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 50.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lorient_KMA → Loudeac)-F054", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Vannes_KBE → Lorient_KMA)-F055", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 10.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Stbrieuc → Lannion_CAS)-F056", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Rennes_STA → Stbrieuc)-F057", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 65.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Morlaix → Lannion_CAS)-F059", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 40.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Brest_KLA → Morlaix)-F060", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 3.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 35.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (tata → toto)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 6.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 80.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lannion_CAS to Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "test", | ||||
|       "operational": { | ||||
|         "gain_target": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east edfa in Lorient_KMA to Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Edfa", | ||||
|       "type_variety": "test", | ||||
|       "operational": { | ||||
|         "gain_target": 0, | ||||
|         "tilt_target": 0, | ||||
|         "out_voa": 0 | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "connections": [ | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "east edfa in Lannion_CAS to Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lannion_CAS to Corlay", | ||||
|       "to_node": "fiber (Lannion_CAS → Corlay)-F061" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Corlay → Lannion_CAS)-F061", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Stbrieuc → Lannion_CAS)-F056", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "fiber (Lannion_CAS → Morlaix)-F059" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Morlaix → Lannion_CAS)-F059", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Corlay)-F061", | ||||
|       "to_node": "west fused spans in Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west fused spans in Corlay", | ||||
|       "to_node": "fiber (Corlay → Loudeac)-F010" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Loudeac → Corlay)-F010", | ||||
|       "to_node": "east fused spans in Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east fused spans in Corlay", | ||||
|       "to_node": "fiber (Corlay → Lannion_CAS)-F061" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Corlay → Loudeac)-F010", | ||||
|       "to_node": "west fused spans in Loudeac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west fused spans in Loudeac", | ||||
|       "to_node": "fiber (Loudeac → Lorient_KMA)-F054" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lorient_KMA → Loudeac)-F054", | ||||
|       "to_node": "east fused spans in Loudeac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east fused spans in Loudeac", | ||||
|       "to_node": "fiber (Loudeac → Corlay)-F010" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "east edfa in Lorient_KMA to Loudeac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east edfa in Lorient_KMA to Loudeac", | ||||
|       "to_node": "fiber (Lorient_KMA → Loudeac)-F054" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Loudeac → Lorient_KMA)-F054", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Vannes_KBE", | ||||
|       "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055", | ||||
|       "to_node": "roadm Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056", | ||||
|       "to_node": "fiber (Stbrieuc → Rennes_STA)-F057" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Rennes_STA → Stbrieuc)-F057", | ||||
|       "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Rennes_STA", | ||||
|       "to_node": "fiber (Rennes_STA → Stbrieuc)-F057" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Stbrieuc → Rennes_STA)-F057", | ||||
|       "to_node": "roadm Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Morlaix)-F059", | ||||
|       "to_node": "west fused spans in Morlaix" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west fused spans in Morlaix", | ||||
|       "to_node": "fiber (Morlaix → Brest_KLA)-F060" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Brest_KLA → Morlaix)-F060", | ||||
|       "to_node": "east fused spans in Morlaix" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east fused spans in Morlaix", | ||||
|       "to_node": "fiber (Morlaix → Lannion_CAS)-F059" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Brest_KLA", | ||||
|       "to_node": "fiber (Brest_KLA → Morlaix)-F060" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Morlaix → Brest_KLA)-F060", | ||||
|       "to_node": "roadm Brest_KLA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm toto", | ||||
|       "to_node": "fiber (toto → tata)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (tata → toto)-", | ||||
|       "to_node": "roadm toto" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm tata", | ||||
|       "to_node": "fiber (tata → toto)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (toto → tata)-", | ||||
|       "to_node": "roadm tata" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Lannion_CAS", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "trx Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Lorient_KMA", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "trx Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Vannes_KBE", | ||||
|       "to_node": "roadm Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Vannes_KBE", | ||||
|       "to_node": "trx Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Rennes_STA", | ||||
|       "to_node": "roadm Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Rennes_STA", | ||||
|       "to_node": "trx Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Brest_KLA", | ||||
|       "to_node": "roadm Brest_KLA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Brest_KLA", | ||||
|       "to_node": "trx Brest_KLA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx toto", | ||||
|       "to_node": "roadm toto" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm toto", | ||||
|       "to_node": "trx toto" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx tata", | ||||
|       "to_node": "roadm tata" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm tata", | ||||
|       "to_node": "trx tata" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -1,224 +0,0 @@ | ||||
| { | ||||
|   "path-request": [ | ||||
|     { | ||||
|       "request-id": "0", | ||||
|       "source": "Lorient_KMA", | ||||
|       "destination": "Vannes_KBE", | ||||
|       "src-tp-id": "trx Lorient_KMA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager_16QAM", | ||||
|           "trx_mode": "16QAM", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "1", | ||||
|       "source": "Brest_KLA", | ||||
|       "destination": "Vannes_KBE", | ||||
|       "src-tp-id": "trx Brest_KLA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager_16QAM", | ||||
|           "trx_mode": "16QAM", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|           { | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|               "node-id": "Lannion_CAS", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "index": 1, | ||||
|             "unnumbered-hop": { | ||||
|               "node-id": "Lorient_KMA", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "3", | ||||
|       "source": "Lannion_CAS", | ||||
|       "destination": "Rennes_STA", | ||||
|       "src-tp-id": "trx Lannion_CAS", | ||||
|       "dst-tp-id": "trx Rennes_STA", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "vendorA_trx-type1", | ||||
|           "trx_mode": "PS_SP64_1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "4", | ||||
|       "source": "Rennes_STA", | ||||
|       "destination": "Lannion_CAS", | ||||
|       "src-tp-id": "trx Rennes_STA", | ||||
|       "dst-tp-id": "trx Lannion_CAS", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "vendorA_trx-type1", | ||||
|           "trx_mode": "PS_SP64_2", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 75000000000.0, | ||||
|           "max-nb-of-channel": 64, | ||||
|           "output-power": 0.0019952623149688794, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "5", | ||||
|       "source": "Lorient_KMA", | ||||
|       "destination": "Lannion_CAS", | ||||
|       "src-tp-id": "trx Lorient_KMA", | ||||
|       "dst-tp-id": "trx Lannion_CAS", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager_16QAM", | ||||
|           "trx_mode": "16QAM", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|           { | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|               "node-id": "toto", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "synchronization": [ | ||||
|     { | ||||
|       "synchronization-id": "0", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "request-id-number": [ | ||||
|           "0", | ||||
|           "0" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "synchronization-id": "3", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "request-id-number": [ | ||||
|           "3", | ||||
|           "4" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "synchronization-id": "5", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "request-id-number": [ | ||||
|           "5", | ||||
|           "0" | ||||
|         ] | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -1,762 +0,0 @@ | ||||
| { | ||||
|   "elements": [ | ||||
|     { | ||||
|       "uid": "trx Lannion_CAS", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Vannes_KBE", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Vannes_KBE", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 4.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Rennes_STA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Rennes_STA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx Brest_KLA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Brest_KLA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 4.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx toto", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "toto", | ||||
|           "region": "", | ||||
|           "latitude": 6.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx tata", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "tata", | ||||
|           "region": "", | ||||
|           "latitude": 7.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Lannion_CAS", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lannion_CAS", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Lorient_KMA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Lorient_KMA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Vannes_KBE", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Vannes_KBE", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 4.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Rennes_STA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Rennes_STA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm Brest_KLA", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Brest_KLA", | ||||
|           "region": "RLD", | ||||
|           "latitude": 4.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm toto", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "toto", | ||||
|           "region": "", | ||||
|           "latitude": 6.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm tata", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "tata", | ||||
|           "region": "", | ||||
|           "latitude": 7.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west fused spans in Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Corlay", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west fused spans in Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Loudeac", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "west fused spans in Morlaix", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Morlaix", | ||||
|           "region": "RLD", | ||||
|           "latitude": 3.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east fused spans in Corlay", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Corlay", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east fused spans in Loudeac", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Loudeac", | ||||
|           "region": "RLD", | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "east fused spans in Morlaix", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "Morlaix", | ||||
|           "region": "RLD", | ||||
|           "latitude": 3.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fused" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lannion_CAS → Corlay)-F061", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 20.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Corlay → Loudeac)-F010", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 50.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Loudeac → Lorient_KMA)-F054", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lorient_KMA → Vannes_KBE)-F055", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 10.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lannion_CAS → Stbrieuc)-F056", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Stbrieuc → Rennes_STA)-F057", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 65.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lannion_CAS → Morlaix)-F059", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 40.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Morlaix → Brest_KLA)-F060", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 3.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 35.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (toto → tata)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 6.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 80.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Corlay → Lannion_CAS)-F061", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 0.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 20.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Loudeac → Corlay)-F010", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 1.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 50.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Lorient_KMA → Loudeac)-F054", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 2.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Vannes_KBE → Lorient_KMA)-F055", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.0, | ||||
|           "longitude": 3.5 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 10.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Stbrieuc → Lannion_CAS)-F056", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 1.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Rennes_STA → Stbrieuc)-F057", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 65.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Morlaix → Lannion_CAS)-F059", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 2.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 40.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (Brest_KLA → Morlaix)-F060", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 3.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 35.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (tata → toto)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 6.5, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 80.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "connections": [ | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "fiber (Lannion_CAS → Corlay)-F061" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Corlay → Lannion_CAS)-F061", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Stbrieuc → Lannion_CAS)-F056", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "fiber (Lannion_CAS → Morlaix)-F059" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Morlaix → Lannion_CAS)-F059", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Corlay)-F061", | ||||
|       "to_node": "west fused spans in Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west fused spans in Corlay", | ||||
|       "to_node": "fiber (Corlay → Loudeac)-F010" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Loudeac → Corlay)-F010", | ||||
|       "to_node": "east fused spans in Corlay" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east fused spans in Corlay", | ||||
|       "to_node": "fiber (Corlay → Lannion_CAS)-F061" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Corlay → Loudeac)-F010", | ||||
|       "to_node": "west fused spans in Loudeac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west fused spans in Loudeac", | ||||
|       "to_node": "fiber (Loudeac → Lorient_KMA)-F054" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lorient_KMA → Loudeac)-F054", | ||||
|       "to_node": "east fused spans in Loudeac" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east fused spans in Loudeac", | ||||
|       "to_node": "fiber (Loudeac → Corlay)-F010" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "fiber (Lorient_KMA → Loudeac)-F054" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Loudeac → Lorient_KMA)-F054", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Vannes_KBE", | ||||
|       "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055", | ||||
|       "to_node": "roadm Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056", | ||||
|       "to_node": "fiber (Stbrieuc → Rennes_STA)-F057" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Rennes_STA → Stbrieuc)-F057", | ||||
|       "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Rennes_STA", | ||||
|       "to_node": "fiber (Rennes_STA → Stbrieuc)-F057" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Stbrieuc → Rennes_STA)-F057", | ||||
|       "to_node": "roadm Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Lannion_CAS → Morlaix)-F059", | ||||
|       "to_node": "west fused spans in Morlaix" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "west fused spans in Morlaix", | ||||
|       "to_node": "fiber (Morlaix → Brest_KLA)-F060" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Brest_KLA → Morlaix)-F060", | ||||
|       "to_node": "east fused spans in Morlaix" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "east fused spans in Morlaix", | ||||
|       "to_node": "fiber (Morlaix → Lannion_CAS)-F059" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Brest_KLA", | ||||
|       "to_node": "fiber (Brest_KLA → Morlaix)-F060" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (Morlaix → Brest_KLA)-F060", | ||||
|       "to_node": "roadm Brest_KLA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm toto", | ||||
|       "to_node": "fiber (toto → tata)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (tata → toto)-", | ||||
|       "to_node": "roadm toto" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm tata", | ||||
|       "to_node": "fiber (tata → toto)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (toto → tata)-", | ||||
|       "to_node": "roadm tata" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Lannion_CAS", | ||||
|       "to_node": "roadm Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lannion_CAS", | ||||
|       "to_node": "trx Lannion_CAS" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Lorient_KMA", | ||||
|       "to_node": "roadm Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Lorient_KMA", | ||||
|       "to_node": "trx Lorient_KMA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Vannes_KBE", | ||||
|       "to_node": "roadm Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Vannes_KBE", | ||||
|       "to_node": "trx Vannes_KBE" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Rennes_STA", | ||||
|       "to_node": "roadm Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Rennes_STA", | ||||
|       "to_node": "trx Rennes_STA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx Brest_KLA", | ||||
|       "to_node": "roadm Brest_KLA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm Brest_KLA", | ||||
|       "to_node": "trx Brest_KLA" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx toto", | ||||
|       "to_node": "roadm toto" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm toto", | ||||
|       "to_node": "trx toto" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx tata", | ||||
|       "to_node": "roadm tata" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm tata", | ||||
|       "to_node": "trx tata" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -1,224 +0,0 @@ | ||||
| { | ||||
|   "path-request": [ | ||||
|     { | ||||
|       "request-id": "0", | ||||
|       "source": "Lorient_KMA", | ||||
|       "destination": "Vannes_KBE", | ||||
|       "src-tp-id": "trx Lorient_KMA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager_16QAM", | ||||
|           "trx_mode": "16QAM", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "1", | ||||
|       "source": "Brest_KLA", | ||||
|       "destination": "Vannes_KBE", | ||||
|       "src-tp-id": "trx Brest_KLA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager_16QAM", | ||||
|           "trx_mode": "16QAM", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|           { | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|               "node-id": "Lannion_CAS", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "index": 1, | ||||
|             "unnumbered-hop": { | ||||
|               "node-id": "Lorient_KMA", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "3", | ||||
|       "source": "Lannion_CAS", | ||||
|       "destination": "Rennes_STA", | ||||
|       "src-tp-id": "trx Lannion_CAS", | ||||
|       "dst-tp-id": "trx Rennes_STA", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "vendorA_trx-type1", | ||||
|           "trx_mode": "PS_SP64_1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "4", | ||||
|       "source": "Rennes_STA", | ||||
|       "destination": "Lannion_CAS", | ||||
|       "src-tp-id": "trx Rennes_STA", | ||||
|       "dst-tp-id": "trx Lannion_CAS", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "vendorA_trx-type1", | ||||
|           "trx_mode": "PS_SP64_2", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 75000000000.0, | ||||
|           "max-nb-of-channel": 64, | ||||
|           "output-power": 0.0019952623149688794, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "5", | ||||
|       "source": "Lorient_KMA", | ||||
|       "destination": "Lannion_CAS", | ||||
|       "src-tp-id": "trx Lorient_KMA", | ||||
|       "dst-tp-id": "trx Lannion_CAS", | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager_16QAM", | ||||
|           "trx_mode": "16QAM", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "n": "null", | ||||
|               "m": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|           { | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|               "node-id": "toto", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "synchronization": [ | ||||
|     { | ||||
|       "synchronization-id": "0", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "request-id-number": [ | ||||
|           "0", | ||||
|           "0" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "synchronization-id": "3", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "request-id-number": [ | ||||
|           "3", | ||||
|           "4" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "synchronization-id": "5", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "request-id-number": [ | ||||
|           "5", | ||||
|           "0" | ||||
|         ] | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -1,730 +0,0 @@ | ||||
| { | ||||
|   "elements": [ | ||||
|     { | ||||
|       "uid": "trx a", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "a", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx b", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "b", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx c", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "c", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx d", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "d", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx e", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "e", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx f", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "f", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx g", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "g", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "trx h", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "h", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Transceiver" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm a", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "a", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm b", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "b", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm c", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "c", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm d", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "d", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm e", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "e", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm f", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "f", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm g", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "g", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "roadm h", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "city": "h", | ||||
|           "region": "", | ||||
|           "latitude": 0, | ||||
|           "longitude": 0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Roadm" | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (a \u2192 b)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 30.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (a \u2192 c)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 30.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (c \u2192 d)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 50.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (c \u2192 f)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (b \u2192 f)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 70.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (e \u2192 d)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 80.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (e \u2192 g)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 90.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (f \u2192 h)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 100.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (h \u2192 g)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 110.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (b \u2192 a)-F061", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 30.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (c \u2192 a)-F010", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 30.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (d \u2192 c)-F054", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 50.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (f \u2192 c)-F055", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 60.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (f \u2192 b)-F056", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 70.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (d \u2192 e)-F057", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 80.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (g \u2192 e)-F059", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 90.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (h \u2192 f)-F060", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 100.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "uid": "fiber (g \u2192 h)-", | ||||
|       "metadata": { | ||||
|         "location": { | ||||
|           "latitude": 0.0, | ||||
|           "longitude": 0.0 | ||||
|         } | ||||
|       }, | ||||
|       "type": "Fiber", | ||||
|       "type_variety": "SSMF", | ||||
|       "params": { | ||||
|         "length": 110.0, | ||||
|         "length_units": "km", | ||||
|         "loss_coef": 0.2, | ||||
|         "con_in": null, | ||||
|         "con_out": null | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "connections": [ | ||||
|     { | ||||
|       "from_node": "roadm a", | ||||
|       "to_node": "fiber (a \u2192 b)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (b \u2192 a)-F061", | ||||
|       "to_node": "roadm a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm a", | ||||
|       "to_node": "fiber (a \u2192 c)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (c \u2192 a)-F010", | ||||
|       "to_node": "roadm a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm b", | ||||
|       "to_node": "fiber (b \u2192 a)-F061" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (a \u2192 b)-", | ||||
|       "to_node": "roadm b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm b", | ||||
|       "to_node": "fiber (b \u2192 f)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (f \u2192 b)-F056", | ||||
|       "to_node": "roadm b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm c", | ||||
|       "to_node": "fiber (c \u2192 a)-F010" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (a \u2192 c)-", | ||||
|       "to_node": "roadm c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm c", | ||||
|       "to_node": "fiber (c \u2192 d)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (d \u2192 c)-F054", | ||||
|       "to_node": "roadm c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm c", | ||||
|       "to_node": "fiber (c \u2192 f)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (f \u2192 c)-F055", | ||||
|       "to_node": "roadm c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm d", | ||||
|       "to_node": "fiber (d \u2192 c)-F054" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (c \u2192 d)-", | ||||
|       "to_node": "roadm d" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm d", | ||||
|       "to_node": "fiber (d \u2192 e)-F057" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (e \u2192 d)-", | ||||
|       "to_node": "roadm d" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm e", | ||||
|       "to_node": "fiber (e \u2192 d)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (d \u2192 e)-F057", | ||||
|       "to_node": "roadm e" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm e", | ||||
|       "to_node": "fiber (e \u2192 g)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (g \u2192 e)-F059", | ||||
|       "to_node": "roadm e" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm f", | ||||
|       "to_node": "fiber (f \u2192 c)-F055" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (c \u2192 f)-", | ||||
|       "to_node": "roadm f" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm f", | ||||
|       "to_node": "fiber (f \u2192 b)-F056" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (b \u2192 f)-", | ||||
|       "to_node": "roadm f" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm f", | ||||
|       "to_node": "fiber (f \u2192 h)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (h \u2192 f)-F060", | ||||
|       "to_node": "roadm f" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm g", | ||||
|       "to_node": "fiber (g \u2192 e)-F059" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (e \u2192 g)-", | ||||
|       "to_node": "roadm g" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm g", | ||||
|       "to_node": "fiber (g \u2192 h)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (h \u2192 g)-", | ||||
|       "to_node": "roadm g" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm h", | ||||
|       "to_node": "fiber (h \u2192 f)-F060" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (f \u2192 h)-", | ||||
|       "to_node": "roadm h" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm h", | ||||
|       "to_node": "fiber (h \u2192 g)-" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "fiber (g \u2192 h)-", | ||||
|       "to_node": "roadm h" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx a", | ||||
|       "to_node": "roadm a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm a", | ||||
|       "to_node": "trx a" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx b", | ||||
|       "to_node": "roadm b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm b", | ||||
|       "to_node": "trx b" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx c", | ||||
|       "to_node": "roadm c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm c", | ||||
|       "to_node": "trx c" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx d", | ||||
|       "to_node": "roadm d" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm d", | ||||
|       "to_node": "trx d" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx e", | ||||
|       "to_node": "roadm e" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm e", | ||||
|       "to_node": "trx e" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx f", | ||||
|       "to_node": "roadm f" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm f", | ||||
|       "to_node": "trx f" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx g", | ||||
|       "to_node": "roadm g" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm g", | ||||
|       "to_node": "trx g" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "trx h", | ||||
|       "to_node": "roadm h" | ||||
|     }, | ||||
|     { | ||||
|       "from_node": "roadm h", | ||||
|       "to_node": "trx h" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										223
									
								
								tests/data/raman_fiber_config.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								tests/data/raman_fiber_config.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,223 @@ | ||||
| { | ||||
|   "uid": "Span1", | ||||
|   "params": { | ||||
|     "length": 80, | ||||
|     "loss_coef": 0.2, | ||||
|     "length_units": "km", | ||||
|     "att_in": 0, | ||||
|     "con_in": 0.5, | ||||
|     "con_out": 0.5, | ||||
|     "type_variety": "SSMF", | ||||
|     "dispersion": 0.0000167, | ||||
|     "gamma": 0.00127, | ||||
|     "raman_efficiency": { | ||||
|       "cr": [ | ||||
|         0, | ||||
|         0.0000094, | ||||
|         0.0000292, | ||||
|         0.0000488, | ||||
|         0.0000682, | ||||
|         0.0000831, | ||||
|         0.000094, | ||||
|         0.0001014, | ||||
|         0.0001069, | ||||
|         0.0001119, | ||||
|         0.0001217, | ||||
|         0.0001268, | ||||
|         0.0001365, | ||||
|         0.000149, | ||||
|         0.000165, | ||||
|         0.000181, | ||||
|         0.0001977, | ||||
|         0.0002192, | ||||
|         0.0002469, | ||||
|         0.0002749, | ||||
|         0.0002999, | ||||
|         0.0003206, | ||||
|         0.0003405, | ||||
|         0.0003592, | ||||
|         0.000374, | ||||
|         0.0003826, | ||||
|         0.0003841, | ||||
|         0.0003826, | ||||
|         0.0003802, | ||||
|         0.0003756, | ||||
|         0.0003549, | ||||
|         0.0003795, | ||||
|         0.000344, | ||||
|         0.0002933, | ||||
|         0.0002024, | ||||
|         0.0001158, | ||||
|         0.0000846, | ||||
|         0.0000714, | ||||
|         0.0000686, | ||||
|         0.000085, | ||||
|         0.0000893, | ||||
|         0.0000901, | ||||
|         0.0000815, | ||||
|         0.0000667, | ||||
|         0.0000437, | ||||
|         0.0000328, | ||||
|         0.0000296, | ||||
|         0.0000265, | ||||
|         0.0000257, | ||||
|         0.0000281, | ||||
|         0.0000308, | ||||
|         0.0000367, | ||||
|         0.0000585, | ||||
|         0.0000663, | ||||
|         0.0000636, | ||||
|         0.000055, | ||||
|         0.0000406, | ||||
|         0.0000277, | ||||
|         0.0000242, | ||||
|         0.0000187, | ||||
|         0.000016, | ||||
|         0.000014, | ||||
|         0.0000113, | ||||
|         0.0000105, | ||||
|         0.0000098, | ||||
|         0.0000098, | ||||
|         0.0000113, | ||||
|         0.0000164, | ||||
|         0.0000195, | ||||
|         0.0000238, | ||||
|         0.0000226, | ||||
|         0.0000203, | ||||
|         0.0000148, | ||||
|         0.0000109, | ||||
|         0.0000098, | ||||
|         0.0000105, | ||||
|         0.0000117, | ||||
|         0.0000125, | ||||
|         0.0000121, | ||||
|         0.0000109, | ||||
|         0.0000098, | ||||
|         0.0000082, | ||||
|         0.0000066, | ||||
|         0.0000047, | ||||
|         0.0000027, | ||||
|         0.0000019, | ||||
|         0.0000012, | ||||
|         4e-7, | ||||
|         2e-7, | ||||
|         1e-7 | ||||
|       ], | ||||
|       "frequency_offset": [ | ||||
|         0, | ||||
|         500000000000, | ||||
|         1000000000000, | ||||
|         1500000000000, | ||||
|         2000000000000, | ||||
|         2500000000000, | ||||
|         3000000000000, | ||||
|         3500000000000, | ||||
|         4000000000000, | ||||
|         4500000000000, | ||||
|         5000000000000, | ||||
|         5500000000000, | ||||
|         6000000000000, | ||||
|         6500000000000, | ||||
|         7000000000000, | ||||
|         7500000000000, | ||||
|         8000000000000, | ||||
|         8500000000000, | ||||
|         9000000000000, | ||||
|         9500000000000, | ||||
|         10000000000000, | ||||
|         10500000000000, | ||||
|         11000000000000, | ||||
|         11500000000000, | ||||
|         12000000000000, | ||||
|         12500000000000, | ||||
|         12750000000000, | ||||
|         13000000000000, | ||||
|         13250000000000, | ||||
|         13500000000000, | ||||
|         14000000000000, | ||||
|         14500000000000, | ||||
|         14750000000000, | ||||
|         15000000000000, | ||||
|         15500000000000, | ||||
|         16000000000000, | ||||
|         16500000000000, | ||||
|         17000000000000, | ||||
|         17500000000000, | ||||
|         18000000000000, | ||||
|         18250000000000, | ||||
|         18500000000000, | ||||
|         18750000000000, | ||||
|         19000000000000, | ||||
|         19500000000000, | ||||
|         20000000000000, | ||||
|         20500000000000, | ||||
|         21000000000000, | ||||
|         21500000000000, | ||||
|         22000000000000, | ||||
|         22500000000000, | ||||
|         23000000000000, | ||||
|         23500000000000, | ||||
|         24000000000000, | ||||
|         24500000000000, | ||||
|         25000000000000, | ||||
|         25500000000000, | ||||
|         26000000000000, | ||||
|         26500000000000, | ||||
|         27000000000000, | ||||
|         27500000000000, | ||||
|         28000000000000, | ||||
|         28500000000000, | ||||
|         29000000000000, | ||||
|         29500000000000, | ||||
|         30000000000000, | ||||
|         30500000000000, | ||||
|         31000000000000, | ||||
|         31500000000000, | ||||
|         32000000000000, | ||||
|         32500000000000, | ||||
|         33000000000000, | ||||
|         33500000000000, | ||||
|         34000000000000, | ||||
|         34500000000000, | ||||
|         35000000000000, | ||||
|         35500000000000, | ||||
|         36000000000000, | ||||
|         36500000000000, | ||||
|         37000000000000, | ||||
|         37500000000000, | ||||
|         38000000000000, | ||||
|         38500000000000, | ||||
|         39000000000000, | ||||
|         39500000000000, | ||||
|         40000000000000, | ||||
|         40500000000000, | ||||
|         41000000000000, | ||||
|         41500000000000, | ||||
|         42000000000000 | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   "operational": { | ||||
|     "temperature": 283, | ||||
|     "raman_pumps": [ | ||||
|       { | ||||
|         "power": 0.2, | ||||
|         "frequency": 205000000000000, | ||||
|         "propagation_direction": "counterprop" | ||||
|       }, | ||||
|       { | ||||
|         "power": 0.206, | ||||
|         "frequency": 201000000000000, | ||||
|         "propagation_direction": "counterprop" | ||||
|       } | ||||
|     ] | ||||
|   }, | ||||
|   "metadata": { | ||||
|     "location": { | ||||
|       "latitude": 1, | ||||
|       "longitude": 0, | ||||
|       "city": null, | ||||
|       "region": "" | ||||
|     } | ||||
|   } | ||||
| } | ||||
							
								
								
									
										14
									
								
								tests/data/sim_params.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/data/sim_params.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| { | ||||
|   "raman_computed_channels": [1, 18, 37, 56, 75], | ||||
|   "raman_parameters": { | ||||
|     "flag_raman": true, | ||||
|     "space_resolution": 10e3, | ||||
|     "tolerance": 1e-8 | ||||
|   }, | ||||
|   "nli_parameters": { | ||||
|   	"nli_method_name": "ggn_spectrally_separated", | ||||
|   	"wdm_grid_size": 50e9, | ||||
|   	"dispersion_tolerance": 1, | ||||
|   	"phase_shift_tollerance": 0.1 | ||||
|   } | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								tests/data/testService.xls
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/data/testService.xls
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										79
									
								
								tests/data/testService_services_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								tests/data/testService_services_expected.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| { | ||||
|   "path-request": [ | ||||
|     { | ||||
|       "request-id": "0", | ||||
|       "source": "trx Lorient_KMA", | ||||
|       "destination": "trx Vannes_KBE", | ||||
|       "src-tp-id": "trx Lorient_KMA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager", | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 100000000000.0 | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "1", | ||||
|       "source": "trx Brest_KLA", | ||||
|       "destination": "trx Vannes_KBE", | ||||
|       "src-tp-id": "trx Brest_KLA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager", | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 10000000000.0 | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "3", | ||||
|       "source": "trx Lannion_CAS", | ||||
|       "destination": "trx Rennes_STA", | ||||
|       "src-tp-id": "trx Lannion_CAS", | ||||
|       "dst-tp-id": "trx Rennes_STA", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "vendorA_trx-type1", | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 60000000000.0 | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								tests/data/testTopology.xls
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/data/testTopology.xls
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										3357
									
								
								tests/data/testTopology_auto_design_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3357
									
								
								tests/data/testTopology_auto_design_expected.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1781
									
								
								tests/data/testTopology_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1781
									
								
								tests/data/testTopology_expected.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1471
									
								
								tests/data/testTopology_response.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1471
									
								
								tests/data/testTopology_response.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										8
									
								
								tests/data/testTopology_response_expected.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								tests/data/testTopology_response_expected.csv
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,OSNR-0.1nm,SNR-0.1nm,SNR-bandwidth,baud rate (Gbaud),input power (dBm),path,"spectrum (N,M)",reversed path OSNR-0.1nm,reversed path SNR-0.1nm,reversed path SNR-bandwidth | ||||
| 0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,30.84,30.84,26.75,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE,"-284, 4" | ||||
| 1,trx Brest_KLA,trx Vannes_KBE,10.0,True,1,1,Voyager,mode 1,22.65,22.11,18.03,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | Edfa0_roadm Brest_KLA | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | Edfa0_fiber (Loudeac → Lorient_KMA)-F054 | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE,"-276, 4" | ||||
| 3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,28.29,25.85,21.77,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | Edfa0_fiber (Stbrieuc → Rennes_STA)-F057 | roadm Rennes_STA | trx Rennes_STA,"-284, 4" | ||||
| 4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,22.27,22.15,15.05,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | Edfa1_roadm Rennes_STA | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | Edfa0_fiber (Ploermel → Vannes_KBE)- | roadm Vannes_KBE | Edfa0_roadm Vannes_KBE | fiber (Vannes_KBE → Lorient_KMA)-F055 | Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055 | roadm Lorient_KMA | Edfa0_roadm Lorient_KMA | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS,"-266, 6" | ||||
| 5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,30.79,28.77,21.68,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | Edfa0_roadm Rennes_STA | fiber (Rennes_STA → Stbrieuc)-F057 | Edfa0_fiber (Rennes_STA → Stbrieuc)-F057 | fiber (Stbrieuc → Lannion_CAS)-F056 | Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056 | roadm Lannion_CAS | trx Lannion_CAS,"-274, 6" | ||||
| 6,,,,NO_PATH,,,,,,,,,,, | ||||
|  | ||||
| Can't render this file because it has a wrong number of fields in line 2. | 
							
								
								
									
										218
									
								
								tests/data/testTopology_services_expected.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								tests/data/testTopology_services_expected.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,218 @@ | ||||
| { | ||||
|   "path-request": [ | ||||
|     { | ||||
|       "request-id": "0", | ||||
|       "source": "trx Lorient_KMA", | ||||
|       "destination": "trx Vannes_KBE", | ||||
|       "src-tp-id": "trx Lorient_KMA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager", | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 100000000000.0 | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "1", | ||||
|       "source": "trx Brest_KLA", | ||||
|       "destination": "trx Vannes_KBE", | ||||
|       "src-tp-id": "trx Brest_KLA", | ||||
|       "dst-tp-id": "trx Vannes_KBE", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "Voyager", | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 10000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 0, | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm Brest_KLA", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 1, | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm Lannion_CAS", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 2, | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm Lorient_KMA", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 3, | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm Vannes_KBE", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "3", | ||||
|       "source": "trx Lannion_CAS", | ||||
|       "destination": "trx Rennes_STA", | ||||
|       "src-tp-id": "trx Lannion_CAS", | ||||
|       "dst-tp-id": "trx Rennes_STA", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "vendorA_trx-type1", | ||||
|           "trx_mode": "mode 1", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 50000000000.0, | ||||
|           "max-nb-of-channel": 80, | ||||
|           "output-power": 0.0012589254117941673, | ||||
|           "path_bandwidth": 60000000000.0 | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "4", | ||||
|       "source": "trx Rennes_STA", | ||||
|       "destination": "trx Lannion_CAS", | ||||
|       "src-tp-id": "trx Rennes_STA", | ||||
|       "dst-tp-id": "trx Lannion_CAS", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "vendorA_trx-type1", | ||||
|           "trx_mode": "mode 2", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 75000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 150000000000.0 | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "5", | ||||
|       "source": "trx Rennes_STA", | ||||
|       "destination": "trx Lannion_CAS", | ||||
|       "src-tp-id": "trx Rennes_STA", | ||||
|       "dst-tp-id": "trx Lannion_CAS", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "vendorA_trx-type1", | ||||
|           "trx_mode": "mode 2", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 75000000000.0, | ||||
|           "max-nb-of-channel": 63, | ||||
|           "output-power": 0.0019952623149688794, | ||||
|           "path_bandwidth": 20000000000.0 | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "6", | ||||
|       "source": "trx Lannion_CAS", | ||||
|       "destination": "trx a", | ||||
|       "src-tp-id": "trx Lannion_CAS", | ||||
|       "dst-tp-id": "trx a", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
|           "trx_type": "vendorA_trx-type1", | ||||
|           "trx_mode": "mode 2", | ||||
|           "effective-freq-slot": [ | ||||
|             { | ||||
|               "N": "null", | ||||
|               "M": "null" | ||||
|             } | ||||
|           ], | ||||
|           "spacing": 75000000000.0, | ||||
|           "max-nb-of-channel": null, | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 100000000000.0 | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
|   "synchronization": [ | ||||
|     { | ||||
|       "synchronization-id": "3", | ||||
|       "svec": { | ||||
|         "relaxable": "false", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "3", | ||||
|           "1" | ||||
|         ] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "synchronization-id": "4", | ||||
|       "svec": { | ||||
|         "relaxable": "false", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "4", | ||||
|           "5" | ||||
|         ] | ||||
|       } | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| @@ -2,10 +2,11 @@ | ||||
|   "path-request": [ | ||||
|     { | ||||
|       "request-id": "1", | ||||
|       "source": "a", | ||||
|       "destination": "g", | ||||
|       "source": "trx a", | ||||
|       "destination": "trx g", | ||||
|       "src-tp-id": "trx a", | ||||
|       "dst-tp-id": "trx g", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -22,17 +23,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "2a", | ||||
|       "source": "a", | ||||
|       "destination": "h", | ||||
|       "source": "trx a", | ||||
|       "destination": "trx h", | ||||
|       "src-tp-id": "trx a", | ||||
|       "dst-tp-id": "trx h", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -49,17 +48,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "3", | ||||
|       "source": "f", | ||||
|       "destination": "b", | ||||
|       "source": "trx f", | ||||
|       "destination": "trx b", | ||||
|       "src-tp-id": "trx f", | ||||
|       "dst-tp-id": "trx b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -76,17 +73,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "ee", | ||||
|       "source": "c", | ||||
|       "destination": "f", | ||||
|       "source": "trx c", | ||||
|       "destination": "trx f", | ||||
|       "src-tp-id": "trx c", | ||||
|       "dst-tp-id": "trx f", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -104,36 +99,23 @@ | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm e", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 1, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm g", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
| @@ -141,10 +123,11 @@ | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "ff", | ||||
|       "source": "c", | ||||
|       "destination": "f", | ||||
|       "source": "trx c", | ||||
|       "destination": "trx f", | ||||
|       "src-tp-id": "trx c", | ||||
|       "dst-tp-id": "trx f", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -161,17 +144,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "10", | ||||
|       "source": "a", | ||||
|       "destination": "g", | ||||
|       "source": "trx a", | ||||
|       "destination": "trx g", | ||||
|       "src-tp-id": "trx a", | ||||
|       "dst-tp-id": "trx g", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -188,17 +169,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "11", | ||||
|       "source": "a", | ||||
|       "destination": "h", | ||||
|       "source": "trx a", | ||||
|       "destination": "trx h", | ||||
|       "src-tp-id": "trx a", | ||||
|       "dst-tp-id": "trx h", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -216,21 +195,15 @@ | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "bb", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
| @@ -238,10 +211,11 @@ | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "12", | ||||
|       "source": "f", | ||||
|       "destination": "b", | ||||
|       "source": "trx f", | ||||
|       "destination": "trx b", | ||||
|       "src-tp-id": "trx f", | ||||
|       "dst-tp-id": "trx b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -259,21 +233,15 @@ | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "trx b", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
| @@ -281,10 +249,11 @@ | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "13", | ||||
|       "source": "c", | ||||
|       "destination": "f", | ||||
|       "source": "trx c", | ||||
|       "destination": "trx f", | ||||
|       "src-tp-id": "trx c", | ||||
|       "dst-tp-id": "trx f", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -301,17 +270,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "14", | ||||
|       "source": "c", | ||||
|       "destination": "f", | ||||
|       "source": "trx c", | ||||
|       "destination": "trx f", | ||||
|       "src-tp-id": "trx c", | ||||
|       "dst-tp-id": "trx f", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -329,36 +296,23 @@ | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm e", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 1, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm g", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
| @@ -366,10 +320,11 @@ | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "e:1# /", | ||||
|       "source": "a", | ||||
|       "destination": "g", | ||||
|       "source": "trx a", | ||||
|       "destination": "trx g", | ||||
|       "src-tp-id": "trx a", | ||||
|       "dst-tp-id": "trx g", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -386,17 +341,15 @@ | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "b-2a", | ||||
|       "source": "a", | ||||
|       "destination": "h", | ||||
|       "source": "trx a", | ||||
|       "destination": "trx h", | ||||
|       "src-tp-id": "trx a", | ||||
|       "dst-tp-id": "trx h", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -413,17 +366,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "3a;?", | ||||
|       "source": "f", | ||||
|       "destination": "b", | ||||
|       "source": "trx f", | ||||
|       "destination": "trx b", | ||||
|       "src-tp-id": "trx f", | ||||
|       "dst-tp-id": "trx b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -440,17 +391,15 @@ | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "ee-s", | ||||
|       "source": "c", | ||||
|       "destination": "f", | ||||
|       "source": "trx c", | ||||
|       "destination": "trx f", | ||||
|       "src-tp-id": "trx c", | ||||
|       "dst-tp-id": "trx f", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -468,36 +417,23 @@ | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [ | ||||
|       "explicit-route-objects": { | ||||
|         "route-object-include-exclude": [ | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 0, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm e", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           }, | ||||
|           { | ||||
|             "explicit-route-usage": "route-include-ero", | ||||
|             "index": 1, | ||||
|             "unnumbered-hop": { | ||||
|             "num-unnum-hop": { | ||||
|               "node-id": "roadm g", | ||||
|               "link-tp-id": "link-tp-id is not used", | ||||
|               "hop-type": "loose", | ||||
|               "direction": "direction is not used" | ||||
|             }, | ||||
|             "label-hop": { | ||||
|               "te-label": { | ||||
|                 "generic": "generic is not used", | ||||
|                 "direction": "direction is not used" | ||||
|               } | ||||
|               "hop-type": "LOOSE" | ||||
|             } | ||||
|           } | ||||
|         ] | ||||
| @@ -505,10 +441,11 @@ | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "ff-b", | ||||
|       "source": "c", | ||||
|       "destination": "f", | ||||
|       "source": "trx c", | ||||
|       "destination": "trx f", | ||||
|       "src-tp-id": "trx c", | ||||
|       "dst-tp-id": "trx f", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -525,17 +462,15 @@ | ||||
|           "output-power": 0.001, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "10-z", | ||||
|       "source": "a", | ||||
|       "destination": "g", | ||||
|       "source": "trx a", | ||||
|       "destination": "trx g", | ||||
|       "src-tp-id": "trx a", | ||||
|       "dst-tp-id": "trx g", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -552,17 +487,15 @@ | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "11 g", | ||||
|       "source": "a", | ||||
|       "destination": "h", | ||||
|       "source": "trx a", | ||||
|       "destination": "trx h", | ||||
|       "src-tp-id": "trx a", | ||||
|       "dst-tp-id": "trx h", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -579,17 +512,15 @@ | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": 300000000000.0 | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "12<", | ||||
|       "source": "f", | ||||
|       "destination": "b", | ||||
|       "source": "trx f", | ||||
|       "destination": "trx b", | ||||
|       "src-tp-id": "trx f", | ||||
|       "dst-tp-id": "trx b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -606,17 +537,15 @@ | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": null | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "request-id": "12>", | ||||
|       "source": "f", | ||||
|       "destination": "b", | ||||
|       "source": "trx f", | ||||
|       "destination": "trx b", | ||||
|       "src-tp-id": "trx f", | ||||
|       "dst-tp-id": "trx b", | ||||
|       "bidirectional": false, | ||||
|       "path-constraints": { | ||||
|         "te-bandwidth": { | ||||
|           "technology": "flexi-grid", | ||||
| @@ -633,9 +562,6 @@ | ||||
|           "output-power": null, | ||||
|           "path_bandwidth": null | ||||
|         } | ||||
|       }, | ||||
|       "optimizations": { | ||||
|         "explicit-route-include-objects": [] | ||||
|       } | ||||
|     } | ||||
|   ], | ||||
| @@ -644,8 +570,7 @@ | ||||
|       "synchronization-id": "1", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "1", | ||||
|           "2a" | ||||
| @@ -656,8 +581,7 @@ | ||||
|       "synchronization-id": "3", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "3", | ||||
|           "1" | ||||
| @@ -668,8 +592,7 @@ | ||||
|       "synchronization-id": "ff", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "ff", | ||||
|           "13" | ||||
| @@ -680,8 +603,7 @@ | ||||
|       "synchronization-id": "13", | ||||
|       "svec": { | ||||
|         "relaxable": "False", | ||||
|         "link-diverse": "True", | ||||
|         "node-diverse": "True", | ||||
|         "disjointness": "node link", | ||||
|         "request-id-number": [ | ||||
|           "13", | ||||
|           "14" | ||||
| @@ -78,6 +78,21 @@ | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|         "uid": "Att_B", | ||||
|         "type": "Fused", | ||||
|         "params":{ | ||||
|             "loss":16 | ||||
|         },  | ||||
|         "metadata": { | ||||
|             "location": { | ||||
|               "latitude": 2.0, | ||||
|               "longitude": 1.0, | ||||
|               "city": "Corlay", | ||||
|               "region": "RLD" | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         { | ||||
|             "uid": "Site_B", | ||||
|             "type": "Transceiver", | ||||
| @@ -110,6 +125,10 @@ | ||||
|         },         | ||||
|         { | ||||
|             "from_node": "Edfa2", | ||||
|             "to_node": "Att_B" | ||||
|         }, | ||||
|         { | ||||
|             "from_node": "Att_B", | ||||
|             "to_node": "Site_B" | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -9,8 +9,8 @@ from json import load | ||||
| from gnpy.core.elements import Transceiver, Fiber, Edfa | ||||
| from gnpy.core.utils import lin2db, db2lin | ||||
| from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref | ||||
| from gnpy.core.equipment import load_equipment, automatic_fmax | ||||
| from gnpy.core.network import build_network, load_network, set_roadm_loss | ||||
| from gnpy.core.equipment import load_equipment, automatic_fmax, automatic_nch | ||||
| from gnpy.core.network import build_network, load_network | ||||
| from pathlib import Path | ||||
| import pytest | ||||
|  | ||||
| @@ -79,7 +79,7 @@ def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si): | ||||
|     pin = pin/db2lin(gain) | ||||
|     baud_rates = array([c.baud_rate for c in si.carriers]) | ||||
|     edfa.operational.gain_target = gain | ||||
|     pref = Pref(0, -gain) | ||||
|     pref = Pref(0, -gain, lin2db(len(frequencies))) | ||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) | ||||
|     result = edfa.nf | ||||
|     assert pytest.approx(nf_expected, abs=0.01) == result[0] | ||||
| @@ -93,7 +93,7 @@ def test_fixed_gain_nf(gain, nf_expected, setup_edfa_fixed_gain, si): | ||||
|     pin = pin/db2lin(gain) | ||||
|     baud_rates = array([c.baud_rate for c in si.carriers]) | ||||
|     edfa.operational.gain_target = gain | ||||
|     pref = Pref(0, -gain) | ||||
|     pref = Pref(0, -gain, lin2db(len(frequencies))) | ||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) | ||||
|  | ||||
|     assert pytest.approx(nf_expected, abs=0.01) == edfa.nf[0] | ||||
| @@ -118,7 +118,7 @@ def test_compare_nf_models(gain, setup_edfa_variable_gain, si): | ||||
|     pin = pin/db2lin(gain) | ||||
|     baud_rates = array([c.baud_rate for c in si.carriers]) | ||||
|     edfa.operational.gain_target = gain | ||||
|     pref = Pref(0, -gain) | ||||
|     pref = Pref(0, -gain, lin2db(len(frequencies))) | ||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) | ||||
|     nf_model = edfa.nf[0] | ||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) | ||||
| @@ -137,7 +137,7 @@ def test_ase_noise(gain, si, setup_edfa_variable_gain, setup_trx, bw): | ||||
|     pin = array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers]) | ||||
|     baud_rates = array([c.baud_rate for c in si.carriers]) | ||||
|     edfa.operational.gain_target = gain | ||||
|     pref = Pref(0, 0) | ||||
|     pref = Pref(0, 0, lin2db(len(frequencies))) | ||||
|     edfa.interpol_params(frequencies, pin, baud_rates, pref) | ||||
|     nf = edfa.nf | ||||
|     pin = lin2db(pin[0]*1e3) | ||||
|   | ||||
| @@ -18,16 +18,14 @@ from pathlib import Path | ||||
| import pytest | ||||
| from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | ||||
| from gnpy.core.network import load_network, build_network | ||||
| from examples.path_requests_run import (requests_from_json , correct_route_list , | ||||
|                                         load_requests , disjunctions_from_json) | ||||
| from gnpy.core.request import (compute_path_dsjctn, isdisjoint , find_reversed_path, | ||||
|                                propagate,propagate_and_optimize_mode)  | ||||
| from examples.path_requests_run import requests_from_json, correct_route_list, load_requests | ||||
| from gnpy.core.request import compute_path_dsjctn, propagate, propagate_and_optimize_mode | ||||
| from gnpy.core.utils import db2lin, lin2db | ||||
| from gnpy.core.elements import Roadm | ||||
|  | ||||
| network_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy.json' | ||||
| service_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_services.json' | ||||
| result_file_name  = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_results.json' | ||||
| network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json' | ||||
| service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json' | ||||
| result_file_name  = Path(__file__).parent.parent / 'tests/data/testTopology_testresults.json' | ||||
| eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' | ||||
|  | ||||
| @pytest.mark.parametrize("net",[network_file_name]) | ||||
| @@ -35,7 +33,7 @@ eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' | ||||
| @pytest.mark.parametrize("serv",[service_file_name]) | ||||
| @pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 2', 'mode 1', 'mode 2', 'nok']]) | ||||
| def test_automaticmodefeature(net,eqpt,serv,expected_mode): | ||||
|     data = load_requests(serv,eqpt) | ||||
|     data = load_requests(serv, eqpt, bidir=False) | ||||
|     equipment = load_equipment(eqpt) | ||||
|     network = load_network(net,equipment) | ||||
|  | ||||
| @@ -72,7 +70,7 @@ def test_automaticmodefeature(net,eqpt,serv,expected_mode): | ||||
|         if pathreq.baud_rate is not None: | ||||
|             print(pathreq.format) | ||||
|             path_res_list.append(pathreq.format) | ||||
|             total_path = propagate(total_path,pathreq,equipment, show=False) | ||||
|             total_path = propagate(total_path,pathreq,equipment) | ||||
|         else: | ||||
|             total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment) | ||||
|             # if no baudrate satisfies spacing, no mode is returned and an empty path is returned | ||||
|   | ||||
| @@ -19,20 +19,20 @@ from examples.path_requests_run import (requests_from_json , correct_route_list | ||||
| from gnpy.core.request import compute_path_dsjctn, isdisjoint , find_reversed_path | ||||
| from gnpy.core.utils import db2lin, lin2db | ||||
| from gnpy.core.elements import Roadm | ||||
| from gnpy.core.spectrum_assignment import build_oms_list | ||||
|  | ||||
| network_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy.json' | ||||
| service_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_services.json' | ||||
| result_file_name  = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_results.json' | ||||
| network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json' | ||||
| service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json' | ||||
| result_file_name  = Path(__file__).parent.parent / 'tests/data/testTopology_testresults.json' | ||||
| eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' | ||||
|  | ||||
| @pytest.mark.parametrize("net",[network_file_name]) | ||||
| @pytest.mark.parametrize("eqpt", [eqpt_library_name]) | ||||
| @pytest.mark.parametrize("serv",[service_file_name]) | ||||
| def test_disjunction(net,eqpt,serv): | ||||
|     data = load_requests(serv,eqpt) | ||||
|     data = load_requests(serv, eqpt, bidir=False) | ||||
|     equipment = load_equipment(eqpt) | ||||
|     network = load_network(net,equipment) | ||||
|  | ||||
|     # Build the network once using the default power defined in SI in eqpt config | ||||
|     # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||
|     # spacing, f_min and f_max  | ||||
| @@ -41,6 +41,7 @@ def test_disjunction(net,eqpt,serv): | ||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||
|     build_network(network, equipment, p_db, p_total_db) | ||||
|     build_oms_list(network, equipment) | ||||
|  | ||||
|     rqs = requests_from_json(data, equipment) | ||||
|     rqs = correct_route_list(network, rqs) | ||||
| @@ -56,7 +57,7 @@ def test_disjunction(net,eqpt,serv): | ||||
|         rqs_id_list = [r.request_id for r in rqs] | ||||
|         p1 = pths[rqs_id_list.index(e[0])][1:-1] | ||||
|         p2 = pths[rqs_id_list.index(e[1])][1:-1] | ||||
|         if isdisjoint(p1,p2) + isdisjoint(p1,find_reversed_path(p2, network)) > 0: | ||||
|         if isdisjoint(p1, p2) + isdisjoint(p1, find_reversed_path(p2)) > 0: | ||||
|             test = False | ||||
|             print(f'Computed path (roadms):{[e.uid for e in p1  if isinstance(e, Roadm)]}\n') | ||||
|             print(f'Computed path (roadms):{[e.uid for e in p2  if isinstance(e, Roadm)]}\n') | ||||
| @@ -68,7 +69,7 @@ def test_disjunction(net,eqpt,serv): | ||||
| @pytest.mark.parametrize("eqpt", [eqpt_library_name]) | ||||
| @pytest.mark.parametrize("serv",[service_file_name]) | ||||
| def test_does_not_loop_back(net,eqpt,serv): | ||||
|     data = load_requests(serv,eqpt) | ||||
|     data = load_requests(serv, eqpt, bidir=False) | ||||
|     equipment = load_equipment(eqpt) | ||||
|     network = load_network(net,equipment) | ||||
|  | ||||
| @@ -80,6 +81,7 @@ def test_does_not_loop_back(net,eqpt,serv): | ||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||
|     build_network(network, equipment, p_db, p_total_db) | ||||
|     build_oms_list(network, equipment) | ||||
|  | ||||
|     rqs = requests_from_json(data, equipment) | ||||
|     rqs = correct_route_list(network, rqs) | ||||
|   | ||||
| @@ -3,43 +3,57 @@ | ||||
| # @Author: Esther Le Rouzic | ||||
| # @Date:   2018-06-15 | ||||
|  | ||||
| from gnpy.core.elements import Edfa | ||||
| import numpy as np | ||||
| """ Adding tests to check the parser non regression | ||||
|     convention of naming of test files: | ||||
|     - ..._expected.json for the reference output | ||||
|     tests: | ||||
|     - generation of topology json | ||||
|     - reading of Eqpt sheet w and W/ power mode | ||||
|     - consistency of autodesign | ||||
|     - generation of service list based on service sheet | ||||
|     - writing of results in csv | ||||
|     - writing of results in json (same keys) | ||||
| """ | ||||
|  | ||||
| from json import load | ||||
| from pathlib import Path | ||||
| from os import unlink | ||||
| from pandas import read_csv | ||||
| import pytest | ||||
| from gnpy.core import network_from_json | ||||
| from gnpy.core.elements import Transceiver, Fiber, Edfa | ||||
| from gnpy.core.utils import lin2db, db2lin | ||||
| from gnpy.core.info import SpectralInformation, Channel, Power | ||||
| from tests.compare import compare_networks, compare_services | ||||
| from copy import deepcopy | ||||
| from gnpy.core.utils import lin2db | ||||
| from gnpy.core.network import save_network, build_network | ||||
| from gnpy.core.convert import convert_file | ||||
| from gnpy.core.service_sheet import convert_service_sheet | ||||
| from pathlib import Path | ||||
| import filecmp | ||||
| from os import unlink | ||||
| from gnpy.core.equipment import load_equipment, automatic_nch | ||||
| from gnpy.core.network import load_network | ||||
| from gnpy.core.request import (jsontocsv, requests_aggregation, | ||||
|                                compute_path_dsjctn, Result_element) | ||||
| from gnpy.core.spectrum_assignment import build_oms_list, pth_assign_spectrum | ||||
| from gnpy.core.exceptions import ServiceError | ||||
| from examples.path_requests_run import (requests_from_json, disjunctions_from_json, | ||||
|                                         correct_route_list, correct_disjn, | ||||
|                                         compute_path_with_disjunction) | ||||
|  | ||||
| TEST_DIR = Path(__file__).parent | ||||
| DATA_DIR = TEST_DIR / 'data' | ||||
| eqpt_filename = DATA_DIR / 'eqpt_config.json' | ||||
|  | ||||
| # adding tests to check the parser non regression | ||||
| # convention of naming of test files: | ||||
| # | ||||
| #    - ..._expected.json for the reference output | ||||
|  | ||||
| @pytest.mark.parametrize('xls_input,expected_json_output', { | ||||
|     DATA_DIR / 'excelTestFile.xls':             DATA_DIR / 'excelTestFile_expected.json', | ||||
|     DATA_DIR / 'CORONET_Global_Topology.xls':   DATA_DIR / 'CORONET_Global_Topology_expected.json', | ||||
|     DATA_DIR / 'meshTopologyExampleV2.xls':     DATA_DIR / 'meshTopologyExampleV2_expected.json', | ||||
|     DATA_DIR / 'meshTopologyExampleV2Eqpt.xls': DATA_DIR / 'meshTopologyExampleV2Eqpt_expected.json', | ||||
|     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_expected.json', | ||||
|     }.items()) | ||||
| def test_excel_json_generation(xls_input, expected_json_output): | ||||
|     """ tests generation of topology json | ||||
|     """ | ||||
|     convert_file(xls_input) | ||||
|  | ||||
|     actual_json_output = xls_input.with_suffix('.json') | ||||
|     with open(actual_json_output, encoding='utf-8') as f: | ||||
|         actual = load(f) | ||||
|     #unlink(actual_json_output) | ||||
|     unlink(actual_json_output) | ||||
|  | ||||
|     with open(expected_json_output, encoding='utf-8') as f: | ||||
|         expected = load(f) | ||||
| @@ -52,16 +66,97 @@ def test_excel_json_generation(xls_input, expected_json_output): | ||||
|     assert not results.connections.extra | ||||
|     assert not results.connections.different | ||||
|  | ||||
| # assume json entries | ||||
| # test that the build network gives correct results | ||||
| # TODO !! | ||||
| # assume xls entries | ||||
| # test that the build network gives correct results in gain mode | ||||
|  | ||||
| @pytest.mark.parametrize('xls_input,expected_json_output', | ||||
|                          {DATA_DIR / 'CORONET_Global_Topology.xls':\ | ||||
|                           DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json', | ||||
|                           DATA_DIR / 'testTopology.xls':\ | ||||
|                           DATA_DIR / 'testTopology_auto_design_expected.json', | ||||
|                          }.items()) | ||||
| def test_auto_design_generation_fromxlsgainmode(xls_input, expected_json_output): | ||||
|     """ tests generation of topology json | ||||
|         test that the build network gives correct results in gain mode | ||||
|     """ | ||||
|     equipment = load_equipment(eqpt_filename) | ||||
|     network = load_network(xls_input, equipment) | ||||
|     # in order to test the Eqpt sheet and load gain target, | ||||
|     # change the power-mode to False (to be in gain mode) | ||||
|     equipment['Span']['default'].power_mode = False | ||||
|     # Build the network once using the default power defined in SI in eqpt config | ||||
|  | ||||
|     p_db = equipment['SI']['default'].power_dbm | ||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||
|     build_network(network, equipment, p_db, p_total_db) | ||||
|     save_network(xls_input, network) | ||||
|  | ||||
|     actual_json_output = f'{str(xls_input)[0:len(str(xls_input))-4]}_auto_design.json' | ||||
|  | ||||
|     with open(actual_json_output, encoding='utf-8') as f: | ||||
|         actual = load(f) | ||||
|     unlink(actual_json_output) | ||||
|  | ||||
|     with open(expected_json_output, encoding='utf-8') as f: | ||||
|         expected = load(f) | ||||
|  | ||||
|     results = compare_networks(expected, actual) | ||||
|     assert not results.elements.missing | ||||
|     assert not results.elements.extra | ||||
|     assert not results.elements.different | ||||
|     assert not results.connections.missing | ||||
|     assert not results.connections.extra | ||||
|     assert not results.connections.different | ||||
|  | ||||
| #test that autodesign creates same file as an input file already autodesigned | ||||
| @pytest.mark.parametrize('json_input,expected_json_output', | ||||
|                          {DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json':\ | ||||
|                           DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json', | ||||
|                           DATA_DIR / 'testTopology_auto_design_expected.json':\ | ||||
|                           DATA_DIR / 'testTopology_auto_design_expected.json', | ||||
|                          }.items()) | ||||
| def test_auto_design_generation_fromjson(json_input, expected_json_output): | ||||
|     """test that autodesign creates same file as an input file already autodesigned | ||||
|     """ | ||||
|     equipment = load_equipment(eqpt_filename) | ||||
|     network = load_network(json_input, equipment) | ||||
|     # in order to test the Eqpt sheet and load gain target, | ||||
|     # change the power-mode to False (to be in gain mode) | ||||
|     equipment['Span']['default'].power_mode = False | ||||
|     # Build the network once using the default power defined in SI in eqpt config | ||||
|  | ||||
|     p_db = equipment['SI']['default'].power_dbm | ||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||
|     build_network(network, equipment, p_db, p_total_db) | ||||
|     save_network(json_input, network) | ||||
|  | ||||
|     actual_json_output = f'{str(json_input)[0:len(str(json_input))-5]}_auto_design.json' | ||||
|  | ||||
|     with open(actual_json_output, encoding='utf-8') as f: | ||||
|         actual = load(f) | ||||
|     unlink(actual_json_output) | ||||
|  | ||||
|     with open(expected_json_output, encoding='utf-8') as f: | ||||
|         expected = load(f) | ||||
|  | ||||
|     results = compare_networks(expected, actual) | ||||
|     assert not results.elements.missing | ||||
|     assert not results.elements.extra | ||||
|     assert not results.elements.different | ||||
|     assert not results.connections.missing | ||||
|     assert not results.connections.extra | ||||
|     assert not results.connections.different | ||||
|  | ||||
| # test services creation | ||||
| @pytest.mark.parametrize('xls_input,expected_json_output', { | ||||
|     DATA_DIR / 'excelTestFile.xls':             DATA_DIR / 'excelTestFile_services_expected.json', | ||||
|     DATA_DIR / 'meshTopologyExampleV2.xls':     DATA_DIR / 'meshTopologyExampleV2_services_expected.json', | ||||
|     DATA_DIR / 'meshTopologyExampleV2Eqpt.xls': DATA_DIR / 'meshTopologyExampleV2Eqpt_services_expected.json', | ||||
| }.items()) | ||||
|     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_services_expected.json', | ||||
|     DATA_DIR / 'testService.xls':     DATA_DIR / 'testService_services_expected.json' | ||||
|     }.items()) | ||||
| def test_excel_service_json_generation(xls_input, expected_json_output): | ||||
|     """ test services creation | ||||
|     """ | ||||
|     convert_service_sheet(xls_input, eqpt_filename) | ||||
|  | ||||
|     actual_json_output = f'{str(xls_input)[:-4]}_services.json' | ||||
| @@ -79,3 +174,173 @@ def test_excel_service_json_generation(xls_input, expected_json_output): | ||||
|     assert not results.synchronizations.missing | ||||
|     assert not results.synchronizations.extra | ||||
|     assert not results.synchronizations.different | ||||
|  | ||||
|     # TODO verify that requested bandwidth is not zero ! | ||||
|  | ||||
| # test xls answers creation | ||||
| @pytest.mark.parametrize('json_input, csv_output', { | ||||
|     DATA_DIR / 'testTopology_response.json':     DATA_DIR / 'testTopology_response', | ||||
| }.items()) | ||||
| def test_csv_response_generation(json_input, csv_output): | ||||
|     """ tests if generated csv is consistant with expected generation | ||||
|         same columns (order not important) | ||||
|     """ | ||||
|     with open(json_input) as jsonfile: | ||||
|         json_data = load(jsonfile) | ||||
|     equipment = load_equipment(eqpt_filename) | ||||
|     csv_filename = str(csv_output)+'.csv' | ||||
|     with open(csv_filename, 'w', encoding='utf-8') as fcsv: | ||||
|         jsontocsv(json_data, equipment, fcsv) | ||||
|  | ||||
|     expected_csv_filename = str(csv_output)+'_expected.csv' | ||||
|  | ||||
|     # expected header | ||||
|     # csv_header = \ | ||||
|     # [ | ||||
|     #  'response-id', | ||||
|     #  'source', | ||||
|     #  'destination', | ||||
|     #  'path_bandwidth', | ||||
|     #  'Pass?', | ||||
|     #  'nb of tsp pairs', | ||||
|     #  'total cost', | ||||
|     #  'transponder-type', | ||||
|     #  'transponder-mode', | ||||
|     #  'OSNR-0.1nm', | ||||
|     #  'SNR-0.1nm', | ||||
|     #  'SNR-bandwidth', | ||||
|     #  'baud rate (Gbaud)', | ||||
|     #  'input power (dBm)', | ||||
|     #  'path', | ||||
|     #  'spectrum (N,M)', | ||||
|     #  'reversed path OSNR-0.1nm', | ||||
|     #  'reversed path SNR-0.1nm', | ||||
|     #  'reversed path SNR-bandwidth' | ||||
|     # ] | ||||
|  | ||||
|     resp = read_csv(csv_filename) | ||||
|     print(resp) | ||||
|     unlink(csv_filename) | ||||
|     expected_resp = read_csv(expected_csv_filename) | ||||
|     print(expected_resp) | ||||
|     resp_header = list(resp.head(0)) | ||||
|     expected_resp_header = list(expected_resp.head(0)) | ||||
|     # check that headers are the same | ||||
|     resp_header.sort() | ||||
|     expected_resp_header.sort() | ||||
|     print('headers are differents') | ||||
|     print(resp_header) | ||||
|     print(expected_resp_header) | ||||
|     assert resp_header == expected_resp_header | ||||
|  | ||||
|     # for each header checks that the output are as expected | ||||
|     resp.sort_values(by=['response-id']) | ||||
|     expected_resp.sort_values(by=['response-id']) | ||||
|  | ||||
|     for column in expected_resp: | ||||
|         assert list(resp[column].fillna('')) == list(expected_resp[column].fillna('')) | ||||
|         print('results are different') | ||||
|         print(list(resp[column])) | ||||
|         print(list(expected_resp[column])) | ||||
|         print(type(list(resp[column])[-1])) | ||||
|  | ||||
| def compare_response(exp_resp, act_resp): | ||||
|     """ False if the keys are different in the nested dicts as well | ||||
|     """ | ||||
|     print(exp_resp) | ||||
|     print(act_resp) | ||||
|     test = True | ||||
|     for key in act_resp.keys(): | ||||
|         if not key in exp_resp.keys(): | ||||
|             print(f'{key} is not expected') | ||||
|             return False | ||||
|         if isinstance(act_resp[key], dict): | ||||
|             test = compare_response(exp_resp[key], act_resp[key]) | ||||
|     if test: | ||||
|         for key in exp_resp.keys(): | ||||
|             if not key in act_resp.keys(): | ||||
|                 print(f'{key} is expected') | ||||
|                 return False | ||||
|             if isinstance(exp_resp[key], dict): | ||||
|                 test = compare_response(exp_resp[key], act_resp[key]) | ||||
|  | ||||
|     # at this point exp_resp and act_resp have the same keys. Check if their values are the same | ||||
|     for key in act_resp.keys(): | ||||
|         if not isinstance(act_resp[key], dict): | ||||
|             if exp_resp[key] != act_resp[key]: | ||||
|                 print(f'expected value :{exp_resp[key]}\n actual value: {act_resp[key]}') | ||||
|                 return False | ||||
|     return test | ||||
|  | ||||
|  | ||||
| # test json answers creation | ||||
| @pytest.mark.parametrize('xls_input, expected_response_file', { | ||||
|     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_response.json', | ||||
| }.items()) | ||||
| def test_json_response_generation(xls_input, expected_response_file): | ||||
|     """ tests if json response is correctly generated for all combinations of requests | ||||
|     """ | ||||
|     data = convert_service_sheet(xls_input, eqpt_filename) | ||||
|     # change one of the request with bidir option to cover bidir case as well | ||||
|     data['path-request'][2]['bidirectional'] = True | ||||
|  | ||||
|     equipment = load_equipment(eqpt_filename) | ||||
|     network = load_network(xls_input, equipment) | ||||
|     p_db = equipment['SI']['default'].power_dbm | ||||
|  | ||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||
|     build_network(network, equipment, p_db, p_total_db) | ||||
|     oms_list = build_oms_list(network, equipment) | ||||
|     rqs = requests_from_json(data, equipment) | ||||
|     rqs = correct_route_list(network, rqs) | ||||
|     dsjn = disjunctions_from_json(data) | ||||
|     dsjn = correct_disjn(dsjn) | ||||
|     rqs, dsjn = requests_aggregation(rqs, dsjn) | ||||
|     pths = compute_path_dsjctn(network, equipment, rqs, dsjn) | ||||
|     propagatedpths, reversed_pths, reversed_propagatedpths = \ | ||||
|         compute_path_with_disjunction(network, equipment, rqs, pths) | ||||
|     pth_assign_spectrum(pths, rqs, oms_list, reversed_pths) | ||||
|  | ||||
|     result = [] | ||||
|     for i, pth in enumerate(propagatedpths): | ||||
|         # test ServiceError handling : when M is zero at this point, the | ||||
|         # json result should not be created if there is no blocking reason | ||||
|         if i == 1: | ||||
|             my_rq = deepcopy(rqs[i]) | ||||
|             my_rq.M = 0 | ||||
|             with pytest.raises(ServiceError): | ||||
|                 Result_element(my_rq, pth, reversed_propagatedpths[i]).json | ||||
|  | ||||
|             my_rq.blocking_reason = 'NO_SPECTRUM' | ||||
|             Result_element(my_rq, pth, reversed_propagatedpths[i]).json | ||||
|  | ||||
|         result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) | ||||
|  | ||||
|     temp = { | ||||
|         'response': [n.json for n in result] | ||||
|     } | ||||
|     # load expected result and compare keys and values | ||||
|  | ||||
|     with open(expected_response_file) as jsonfile: | ||||
|         expected = load(jsonfile) | ||||
|         # since we changes bidir attribute of request#2, need to add the corresponding | ||||
|         # metric in response | ||||
|  | ||||
|     for i, response in enumerate(temp['response']): | ||||
|         if i == 2: | ||||
|             # compare response must be False because z-a metric is missing | ||||
|             # (request with bidir option to cover bidir case) | ||||
|             assert not compare_response(expected['response'][i], response) | ||||
|             print(f'response {response["response-id"]} should not match') | ||||
|             expected['response'][2]['path-properties']['z-a-path-metric'] = [ | ||||
|                 {'metric-type': 'SNR-bandwidth', 'accumulative-value': 22.809999999999999}, | ||||
|                 {'metric-type': 'SNR-0.1nm', 'accumulative-value': 26.890000000000001}, | ||||
|                 {'metric-type': 'OSNR-bandwidth', 'accumulative-value': 26.239999999999998}, | ||||
|                 {'metric-type': 'OSNR-0.1nm', 'accumulative-value': 30.32}, | ||||
|                 {'metric-type': 'reference_power', 'accumulative-value': 0.0012589254117941673}, | ||||
|                 {'metric-type': 'path_bandwidth', 'accumulative-value': 60000000000.0}] | ||||
|             # test should be OK now | ||||
|         else: | ||||
|             assert compare_response(expected['response'][i], response) | ||||
|             print(f'response {response["response-id"]} is not correct') | ||||
|   | ||||
| @@ -18,6 +18,8 @@ from numpy import mean | ||||
|  | ||||
| #network_file_name = 'tests/test_network.json' | ||||
| network_file_name = Path(__file__).parent.parent / 'tests/LinkforTest.json' | ||||
| #TODO: note that this json entries has a weird topology since EDfa1 has a possible branch on a receiver B | ||||
| # this might not pass future tests/ code updates | ||||
| #network_file_name = Path(__file__).parent.parent / 'examples/edfa_example_network.json' | ||||
| eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' | ||||
|  | ||||
| @@ -47,12 +49,8 @@ def propagation(input_power, con_in, con_out,dest): | ||||
|  | ||||
|     p = input_power | ||||
|     p = db2lin(p) * 1e-3 | ||||
|     spacing = 0.05 # THz | ||||
|     si = SpectralInformation() # SI units: W, Hz | ||||
|     si = si.update(carriers=[ | ||||
|         Channel(f, (191.3 + spacing * f) * 1e12, 32e9, 0.15, Power(p, 0, 0)) | ||||
|         for f in range(1,80) | ||||
|     ]) | ||||
|     spacing = 50e9 # THz | ||||
|     si = create_input_spectral_information(191.3e12, 191.3e12+79*spacing, 0.15, 32e9, p, spacing) | ||||
|     source = next(transceivers[uid] for uid in transceivers if uid == 'trx A') | ||||
|     sink = next(transceivers[uid] for uid in transceivers if uid == dest) | ||||
|     path = dijkstra_path(network, source, sink) | ||||
|   | ||||
							
								
								
									
										203
									
								
								tests/test_roadm_restrictions.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										203
									
								
								tests/test_roadm_restrictions.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,203 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
| # @Author: Esther Le Rouzic | ||||
| # @Date:   2019-05-22 | ||||
| """ | ||||
| @author: esther.lerouzic | ||||
| checks that fused placed in amp type is correctly converted to a fused element instead of an edfa | ||||
| and that no additional amp is added. | ||||
| checks that restrictions in roadms are correctly applied during autodesign | ||||
|  | ||||
| """ | ||||
|  | ||||
| from pathlib import Path | ||||
| import pytest | ||||
| from gnpy.core.utils import lin2db, load_json | ||||
| from gnpy.core.elements import Fused, Roadm, Edfa | ||||
| from gnpy.core.equipment import load_equipment, Amp, automatic_nch | ||||
| from gnpy.core.network import network_from_json, build_network | ||||
|  | ||||
|  | ||||
| TEST_DIR = Path(__file__).parent | ||||
| EQPT_LIBRARY_NAME = TEST_DIR / 'data/eqpt_config.json' | ||||
| NETWORK_FILE_NAME = TEST_DIR / 'data/testTopology_expected.json' | ||||
| # adding tests to check the roadm restrictions | ||||
|  | ||||
| # mark node_uid amps as fused for testing purpose | ||||
| @pytest.mark.parametrize("node_uid", ['east edfa in Lannion_CAS to Stbrieuc']) | ||||
| def test_no_amp_feature(node_uid): | ||||
|     ''' Check that booster is not placed on a roadm if fused is specified | ||||
|         test_parser covers partly this behaviour. This test should guaranty that the | ||||
|         feature is preserved even if convert is changed | ||||
|     ''' | ||||
|     equipment = load_equipment(EQPT_LIBRARY_NAME) | ||||
|     json_network = load_json(NETWORK_FILE_NAME) | ||||
|  | ||||
|     for elem in json_network['elements']: | ||||
|         if elem['uid'] == node_uid: | ||||
|             #replace edfa node by a fused node in the topology | ||||
|             elem['type'] = 'Fused' | ||||
|             elem.pop('type_variety') | ||||
|             elem.pop('operational') | ||||
|             elem['params'] = {'loss': 0} | ||||
|  | ||||
|             next_node_uid = next(conn['to_node'] for conn in json_network['connections'] \ | ||||
|                                  if conn['from_node'] == node_uid) | ||||
|             previous_node_uid = next(conn['from_node'] for conn in json_network['connections'] \ | ||||
|                                  if conn['to_node'] == node_uid) | ||||
|  | ||||
|     network = network_from_json(json_network, equipment) | ||||
|     # Build the network once using the default power defined in SI in eqpt config | ||||
|     # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||
|     # spacing, f_min and f_max | ||||
|     p_db = equipment['SI']['default'].power_dbm | ||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||
|  | ||||
|     build_network(network, equipment, p_db, p_total_db) | ||||
|  | ||||
|     node = next(nd for nd in network.nodes() if nd.uid == node_uid) | ||||
|     next_node = next(network.successors(node)) | ||||
|     previous_node = next(network.predecessors(node)) | ||||
|  | ||||
|     if not isinstance(node, Fused): | ||||
|         raise AssertionError() | ||||
|     if not node.params.loss == 0.0: | ||||
|         raise AssertionError() | ||||
|     if not next_node_uid == next_node.uid: | ||||
|         raise AssertionError() | ||||
|     if not previous_node_uid == previous_node.uid: | ||||
|         raise AssertionError() | ||||
|  | ||||
| @pytest.fixture() | ||||
| def equipment(): | ||||
|     """init transceiver class to access snr and osnr calculations""" | ||||
|     equipment = load_equipment(EQPT_LIBRARY_NAME) | ||||
|     # define some booster and preamps | ||||
|     restrictions_list = [ | ||||
|         { | ||||
|             'type_variety': 'booster_medium_gain', | ||||
|             'type_def': 'variable_gain', | ||||
|             'gain_flatmax': 25, | ||||
|             'gain_min': 15, | ||||
|             'p_max': 21, | ||||
|             'nf_min': 5.8, | ||||
|             'nf_max': 10, | ||||
|             'out_voa_auto': False, | ||||
|             'allowed_for_design': False | ||||
|         }, | ||||
|         { | ||||
|             'type_variety': 'preamp_medium_gain', | ||||
|             'type_def': 'variable_gain', | ||||
|             'gain_flatmax': 26, | ||||
|             'gain_min': 15, | ||||
|             'p_max': 23, | ||||
|             'nf_min': 6, | ||||
|             'nf_max': 10, | ||||
|             'out_voa_auto': False, | ||||
|             'allowed_for_design': False | ||||
|         }, | ||||
|         { | ||||
|             'type_variety': 'preamp_high_gain', | ||||
|             'type_def': 'variable_gain', | ||||
|             'gain_flatmax': 35, | ||||
|             'gain_min': 25, | ||||
|             'p_max': 21, | ||||
|             'nf_min': 5.5, | ||||
|             'nf_max': 7, | ||||
|             'out_voa_auto': False, | ||||
|             'allowed_for_design': False | ||||
|         }, | ||||
|         { | ||||
|             'type_variety': 'preamp_low_gain', | ||||
|             'type_def': 'variable_gain', | ||||
|             'gain_flatmax': 16, | ||||
|             'gain_min': 8, | ||||
|             'p_max': 23, | ||||
|             'nf_min': 6.5, | ||||
|             'nf_max': 11, | ||||
|             'out_voa_auto': False, | ||||
|             'allowed_for_design': False | ||||
|         }] | ||||
|     # add them to the library | ||||
|     for entry in restrictions_list: | ||||
|         equipment['Edfa'][entry['type_variety']] = Amp.from_json(EQPT_LIBRARY_NAME, **entry) | ||||
|     return equipment | ||||
|  | ||||
|  | ||||
| @pytest.mark.parametrize("restrictions", [ | ||||
|     { | ||||
|         'preamp_variety_list':[], | ||||
|         'booster_variety_list':[] | ||||
|     }, | ||||
|     { | ||||
|         'preamp_variety_list':[], | ||||
|         'booster_variety_list':['booster_medium_gain'] | ||||
|     }, | ||||
|     { | ||||
|         'preamp_variety_list':['preamp_medium_gain', 'preamp_high_gain', 'preamp_low_gain'], | ||||
|         'booster_variety_list':[] | ||||
|     }]) | ||||
| def test_restrictions(restrictions, equipment): | ||||
|     ''' test that restriction is correctly applied if provided in eqpt_config and if no Edfa type | ||||
|     were provided in the network json | ||||
|     ''' | ||||
|     # add restrictions | ||||
|     equipment['Roadm']['default'].restrictions = restrictions | ||||
|     # build network | ||||
|     json_network = load_json(NETWORK_FILE_NAME) | ||||
|     network = network_from_json(json_network, equipment) | ||||
|  | ||||
|     amp_nodes_nobuild_uid = [nd.uid for nd in network.nodes() \ | ||||
|         if isinstance(nd, Edfa) and isinstance(next(network.predecessors(nd)), Roadm)] | ||||
|     preamp_nodes_nobuild_uid = [nd.uid for nd in network.nodes() \ | ||||
|         if isinstance(nd, Edfa) and isinstance(next(network.successors(nd)), Roadm)] | ||||
|     amp_nodes_nobuild = {nd.uid : nd for nd in network.nodes() \ | ||||
|         if isinstance(nd, Edfa) and isinstance(next(network.predecessors(nd)), Roadm)} | ||||
|     preamp_nodes_nobuild = {nd.uid : nd for nd in network.nodes() \ | ||||
|         if isinstance(nd, Edfa) and isinstance(next(network.successors(nd)), Roadm)} | ||||
|     # roadm dict with restrictions before build | ||||
|     roadms = {nd.uid: nd for nd in network.nodes() if isinstance(nd, Roadm)} | ||||
|     # Build the network once using the default power defined in SI in eqpt config | ||||
|     # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||
|     # spacing, f_min and f_max | ||||
|     p_db = equipment['SI']['default'].power_dbm | ||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||
|  | ||||
|     build_network(network, equipment, p_db, p_total_db) | ||||
|  | ||||
|     amp_nodes = [nd for nd in network.nodes() \ | ||||
|         if isinstance(nd, Edfa) and isinstance(next(network.predecessors(nd)), Roadm)\ | ||||
|            and next(network.predecessors(nd)).restrictions['booster_variety_list']] | ||||
|  | ||||
|     preamp_nodes = [nd for nd in network.nodes() \ | ||||
|         if isinstance(nd, Edfa) and isinstance(next(network.successors(nd)), Roadm)\ | ||||
|            and next(network.successors(nd)).restrictions['preamp_variety_list']] | ||||
|  | ||||
|     # check that previously existing amp are not changed | ||||
|     for amp in amp_nodes: | ||||
|         if amp.uid in amp_nodes_nobuild_uid: | ||||
|             print(amp.uid, amp.params.type_variety) | ||||
|             if not amp.params.type_variety == amp_nodes_nobuild[amp.uid].params.type_variety: | ||||
|                 raise AssertionError() | ||||
|     for amp in preamp_nodes: | ||||
|         if amp.uid in preamp_nodes_nobuild_uid: | ||||
|             if not amp.params.type_variety == preamp_nodes_nobuild[amp.uid].params.type_variety: | ||||
|                 raise AssertionError() | ||||
|     # check that restrictions are correctly applied | ||||
|     for amp in amp_nodes: | ||||
|         if amp.uid not in amp_nodes_nobuild_uid: | ||||
|             # and if roadm had no restrictions before build: | ||||
|             if restrictions['booster_variety_list'] and \ | ||||
|                not roadms[next(network.predecessors(amp)).uid]\ | ||||
|                          .restrictions['booster_variety_list']: | ||||
|                 if not amp.params.type_variety in restrictions['booster_variety_list']: | ||||
|  | ||||
|                     raise AssertionError() | ||||
|     for amp in preamp_nodes: | ||||
|         if amp.uid not in preamp_nodes_nobuild_uid: | ||||
|             if restrictions['preamp_variety_list'] and\ | ||||
|             not roadms[next(network.successors(amp)).uid].restrictions['preamp_variety_list']: | ||||
|                 if not amp.params.type_variety in restrictions['preamp_variety_list']: | ||||
|                     raise AssertionError() | ||||
							
								
								
									
										49
									
								
								tests/test_science_utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tests/test_science_utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| #!/usr/bin/env python3 | ||||
| # -*- coding: utf-8 -*- | ||||
| # @Author: Alessio Ferrari | ||||
| """ | ||||
| checks that RamanFiber propagates properly the spectral information. In this way, also the RamanSolver and the NliSolver | ||||
|  are tested. | ||||
| """ | ||||
|  | ||||
| import json | ||||
| from pandas import read_csv | ||||
| from numpy.testing import assert_allclose | ||||
| from gnpy.core.info import create_input_spectral_information | ||||
| from gnpy.core.elements import RamanFiber | ||||
| from gnpy.core.network import load_sim_params | ||||
| from pathlib import Path | ||||
| TEST_DIR = Path(__file__).parent | ||||
|  | ||||
| def test_raman_fiber(): | ||||
|     """ Test the accuracy of propagating the RamanFiber. | ||||
|     """ | ||||
|     # spectral information generation | ||||
|     power = 1e-3 | ||||
|     with open(TEST_DIR / 'data' / 'eqpt_config.json', 'r') as file: | ||||
|         eqpt_params = json.load(file) | ||||
|     spectral_info_params = eqpt_params['SI'][0] | ||||
|     spectral_info_params.pop('power_dbm') | ||||
|     spectral_info_params.pop('power_range_db') | ||||
|     spectral_info_params.pop('tx_osnr') | ||||
|     spectral_info_params.pop('sys_margins') | ||||
|     spectral_info_input = create_input_spectral_information(power=power, **spectral_info_params) | ||||
|  | ||||
|     # RamanFiber | ||||
|     with open(TEST_DIR / 'data' / 'raman_fiber_config.json', 'r') as file: | ||||
|         raman_fiber_params = json.load(file) | ||||
|     sim_params = load_sim_params(TEST_DIR / 'data' / 'sim_params.json') | ||||
|     fiber = RamanFiber(**raman_fiber_params) | ||||
|     fiber.sim_params = sim_params | ||||
|  | ||||
|     # propagation | ||||
|     spectral_info_out = fiber(spectral_info_input) | ||||
|  | ||||
|     p_signal = [carrier.power.signal for carrier in spectral_info_out.carriers] | ||||
|     p_ase = [carrier.power.ase for carrier in spectral_info_out.carriers] | ||||
|     p_nli = [carrier.power.nli for carrier in spectral_info_out.carriers] | ||||
|  | ||||
|     expected_results = read_csv(TEST_DIR / 'data' / 'expected_results_science_utils.csv') | ||||
|     assert_allclose(p_signal, expected_results['signal'], rtol=1e-3) | ||||
|     assert_allclose(p_ase, expected_results['ase'], rtol=1e-3) | ||||
|     assert_allclose(p_nli, expected_results['nli'], rtol=1e-3) | ||||
		Reference in New Issue
	
	Block a user