mirror of
				https://github.com/Telecominfraproject/oopt-gnpy.git
				synced 2025-10-31 10:07:57 +00:00 
			
		
		
		
	Compare commits
	
		
			156 Commits
		
	
	
		
			v1.8
			...
			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 | 
| @@ -5,17 +5,16 @@ set -e | |||||||
| IMAGE_NAME=telecominfraproject/oopt-gnpy | IMAGE_NAME=telecominfraproject/oopt-gnpy | ||||||
| IMAGE_TAG=$(git describe --tags) | IMAGE_TAG=$(git describe --tags) | ||||||
|  |  | ||||||
|  | if [[ "${TRAVIS_BRANCH}" == "experimental/2019-summit" ]]; then | ||||||
|  |   IMAGE_NAME=telecominfraproject/oopt-gnpy-experimental | ||||||
|  | fi | ||||||
|  |  | ||||||
| ALREADY_FOUND=0 | ALREADY_FOUND=0 | ||||||
| docker pull ${IMAGE_NAME}:${IMAGE_TAG} && ALREADY_FOUND=1 | docker pull ${IMAGE_NAME}:${IMAGE_TAG} && ALREADY_FOUND=1 | ||||||
|  |  | ||||||
| if [[ $ALREADY_FOUND == 0 ]]; then | if [[ $ALREADY_FOUND == 0 ]]; then | ||||||
|   docker build . -t ${IMAGE_NAME} |   docker build . -t ${IMAGE_NAME} | ||||||
|   docker tag ${IMAGE_NAME} ${IMAGE_NAME}:${IMAGE_TAG} |   docker tag ${IMAGE_NAME} ${IMAGE_NAME}:${IMAGE_TAG} | ||||||
|  |  | ||||||
|   # shared directory setup: do not clobber the real data |  | ||||||
|   mkdir trash |  | ||||||
|   cd trash |  | ||||||
|   docker run -it --rm --volume $(pwd):/shared ${IMAGE_NAME} ./transmission_main_example.py |  | ||||||
| else | else | ||||||
|   echo "Image ${IMAGE_NAME}:${IMAGE_TAG} already available, will just update the other tags" |   echo "Image ${IMAGE_NAME}:${IMAGE_TAG} already available, will just update the other tags" | ||||||
| fi | fi | ||||||
| @@ -43,5 +42,11 @@ if [[ "${TRAVIS_PULL_REQUEST}" == "false" ]]; then | |||||||
|       docker push ${IMAGE_NAME}:${IMAGE_TAG} |       docker push ${IMAGE_NAME}:${IMAGE_TAG} | ||||||
|     fi |     fi | ||||||
|     docker push ${IMAGE_NAME}:stable |     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 | ||||||
| fi | fi | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.zuul.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | --- | ||||||
|  | - project: | ||||||
|  |     check: | ||||||
|  |       jobs: | ||||||
|  |       - noop | ||||||
|  |     gate: | ||||||
|  |       jobs: | ||||||
|  |       - noop | ||||||
| @@ -2,6 +2,7 @@ FROM python:3.7-slim | |||||||
| COPY . /oopt-gnpy | COPY . /oopt-gnpy | ||||||
| WORKDIR /oopt-gnpy | WORKDIR /oopt-gnpy | ||||||
| RUN python setup.py install | RUN python setup.py install | ||||||
| WORKDIR /shared/examples | WORKDIR /shared | ||||||
| ENTRYPOINT ["/oopt-gnpy/.docker-entry.sh"] | ENTRYPOINT ["/oopt-gnpy/.docker-entry.sh"] | ||||||
| CMD ["/bin/bash"] | 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 | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
| `gnpy`: mesh optical network route planning and optimization library | `gnpy`: mesh optical network route planning and optimization library | ||||||
| ==================================================================== | ==================================================================== | ||||||
|  |  | ||||||
| |docs| |build| | |docs| |build| |doi| | ||||||
|  |  | ||||||
| **`gnpy` is an open-source, community-developed library for building route | **`gnpy` is an open-source, community-developed library for building route | ||||||
| planning and optimization tools in real-world mesh optical networks.** | planning and optimization tools in real-world mesh optical networks.** | ||||||
| @@ -27,7 +27,7 @@ Documentation: https://gnpy.readthedocs.io | |||||||
| Get In Touch | Get In Touch | ||||||
| ~~~~~~~~~~~~ | ~~~~~~~~~~~~ | ||||||
|  |  | ||||||
| There are `weekly calls <https://telecominfraproject.workplace.com/events/458339931322799/>`__ about our progress. | There are `weekly calls <https://telecominfraproject.workplace.com/events/702894886867547/>`__ about our progress. | ||||||
| Newcomers, users and telecom operators are especially welcome there. | 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/>`__. | We encourage all interested people outside the TIP to `join the project <https://telecominfraproject.com/apply-for-membership/>`__. | ||||||
|  |  | ||||||
| @@ -601,6 +601,11 @@ implementations. | |||||||
|   :alt: Build Status |   :alt: Build Status | ||||||
|   :scale: 100% |   :scale: 100% | ||||||
|  |  | ||||||
|  | .. |doi| image:: https://zenodo.org/badge/96894149.svg | ||||||
|  |   :target: https://zenodo.org/badge/latestdoi/96894149 | ||||||
|  |   :alt: DOI | ||||||
|  |   :scale: 100% | ||||||
|  |  | ||||||
| TIP OOPT/PSE & PSE WG Charter | TIP OOPT/PSE & PSE WG Charter | ||||||
| ----------------------------- | ----------------------------- | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										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) | ||||||
							
								
								
									
										1033
									
								
								examples/demo.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1033
									
								
								examples/demo.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -2,10 +2,11 @@ | |||||||
|   "path-request": [ |   "path-request": [ | ||||||
|     { |     { | ||||||
|       "request-id": "0", |       "request-id": "0", | ||||||
|       "source": "Lorient_KMA", |       "source": "trx Lorient_KMA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "trx Vannes_KBE", | ||||||
|       "src-tp-id": "trx Lorient_KMA", |       "src-tp-id": "trx Lorient_KMA", | ||||||
|       "dst-tp-id": "trx Vannes_KBE", |       "dst-tp-id": "trx Vannes_KBE", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -13,8 +14,8 @@ | |||||||
|           "trx_mode": null, |           "trx_mode": null, | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -22,17 +23,15 @@ | |||||||
|           "output-power": 0.0012589254117941673, |           "output-power": 0.0012589254117941673, | ||||||
|           "path_bandwidth": 100000000000.0 |           "path_bandwidth": 100000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "1", |       "request-id": "1", | ||||||
|       "source": "Brest_KLA", |       "source": "trx Brest_KLA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "trx Vannes_KBE", | ||||||
|       "src-tp-id": "trx Brest_KLA", |       "src-tp-id": "trx Brest_KLA", | ||||||
|       "dst-tp-id": "trx Vannes_KBE", |       "dst-tp-id": "trx Vannes_KBE", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -40,8 +39,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -50,66 +49,42 @@ | |||||||
|           "path_bandwidth": 200000000000.0 |           "path_bandwidth": 200000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Brest_KLA", |               "node-id": "roadm Brest_KLA", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 1, |             "index": 1, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Lannion_CAS", |               "node-id": "roadm Lannion_CAS", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 2, |             "index": 2, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Lorient_KMA", |               "node-id": "roadm Lorient_KMA", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 3, |             "index": 3, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Vannes_KBE", |               "node-id": "roadm Vannes_KBE", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -117,10 +92,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "3", |       "request-id": "3", | ||||||
|       "source": "Lannion_CAS", |       "source": "trx Lannion_CAS", | ||||||
|       "destination": "Rennes_STA", |       "destination": "trx Rennes_STA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
|       "dst-tp-id": "trx Rennes_STA", |       "dst-tp-id": "trx Rennes_STA", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -128,8 +104,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -137,17 +113,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 60000000000.0 |           "path_bandwidth": 60000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "4", |       "request-id": "4", | ||||||
|       "source": "Rennes_STA", |       "source": "trx Rennes_STA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "trx Lannion_CAS", | ||||||
|       "src-tp-id": "trx Rennes_STA", |       "src-tp-id": "trx Rennes_STA", | ||||||
|       "dst-tp-id": "trx Lannion_CAS", |       "dst-tp-id": "trx Lannion_CAS", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -155,8 +129,8 @@ | |||||||
|           "trx_mode": null, |           "trx_mode": null, | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 75000000000.0, |           "spacing": 75000000000.0, | ||||||
| @@ -164,17 +138,15 @@ | |||||||
|           "output-power": 0.0019952623149688794, |           "output-power": 0.0019952623149688794, | ||||||
|           "path_bandwidth": 150000000000.0 |           "path_bandwidth": 150000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "5", |       "request-id": "5", | ||||||
|       "source": "Rennes_STA", |       "source": "trx Rennes_STA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "trx Lannion_CAS", | ||||||
|       "src-tp-id": "trx Rennes_STA", |       "src-tp-id": "trx Rennes_STA", | ||||||
|       "dst-tp-id": "trx Lannion_CAS", |       "dst-tp-id": "trx Lannion_CAS", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -182,8 +154,8 @@ | |||||||
|           "trx_mode": "mode 2", |           "trx_mode": "mode 2", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 75000000000.0, |           "spacing": 75000000000.0, | ||||||
| @@ -191,17 +163,15 @@ | |||||||
|           "output-power": 0.0019952623149688794, |           "output-power": 0.0019952623149688794, | ||||||
|           "path_bandwidth": 20000000000.0 |           "path_bandwidth": 20000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "6", |       "request-id": "6", | ||||||
|       "source": "Lannion_CAS", |       "source": "trx Lannion_CAS", | ||||||
|       "destination": "Lorient_KMA", |       "destination": "trx Lorient_KMA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
|       "dst-tp-id": "trx Lorient_KMA", |       "dst-tp-id": "trx Lorient_KMA", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -209,8 +179,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -218,17 +188,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "7", |       "request-id": "7", | ||||||
|       "source": "Lannion_CAS", |       "source": "trx Lannion_CAS", | ||||||
|       "destination": "Lorient_KMA", |       "destination": "trx Lorient_KMA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
|       "dst-tp-id": "trx Lorient_KMA", |       "dst-tp-id": "trx Lorient_KMA", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -236,8 +204,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -245,17 +213,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 400000000000.0 |           "path_bandwidth": 400000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "7b", |       "request-id": "7b", | ||||||
|       "source": "Lannion_CAS", |       "source": "trx Lannion_CAS", | ||||||
|       "destination": "Lorient_KMA", |       "destination": "trx Lorient_KMA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
|       "dst-tp-id": "trx Lorient_KMA", |       "dst-tp-id": "trx Lorient_KMA", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -263,8 +229,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 75000000000.0, |           "spacing": 75000000000.0, | ||||||
| @@ -272,9 +238,6 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 400000000000.0 |           "path_bandwidth": 400000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
| @@ -282,9 +245,8 @@ | |||||||
|     { |     { | ||||||
|       "synchronization-id": "3", |       "synchronization-id": "3", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "false", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "3", |           "3", | ||||||
|           "1" |           "1" | ||||||
| @@ -294,9 +256,8 @@ | |||||||
|     { |     { | ||||||
|       "synchronization-id": "4", |       "synchronization-id": "4", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "false", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "4", |           "4", | ||||||
|           "5" |           "5" | ||||||
|   | |||||||
| @@ -18,53 +18,78 @@ from pathlib import Path | |||||||
| from collections import namedtuple | from collections import namedtuple | ||||||
| from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO | from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO | ||||||
| from json import dumps, loads | from json import dumps, loads | ||||||
| from networkx import (draw_networkx_nodes, draw_networkx_edges, |  | ||||||
|                       draw_networkx_labels) |  | ||||||
| from numpy import mean | from numpy import mean | ||||||
| from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element | from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element | ||||||
| from gnpy.core.utils import load_json | from gnpy.core.utils import load_json | ||||||
| from gnpy.core.network import load_network, build_network, save_network | from gnpy.core.network import load_network, build_network, save_network, network_from_json | ||||||
| from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch, automatic_spacing | from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | ||||||
| from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused, Fiber | from gnpy.core.elements import Transceiver, Roadm | ||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
| from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, | from gnpy.core.request import (Path_request, Result_element, | ||||||
|                               propagate, jsontocsv, Disjunction, compute_path_dsjctn, requests_aggregation, |                                propagate, jsontocsv, Disjunction, compute_path_dsjctn, | ||||||
|                               propagate_and_optimize_mode) |                                requests_aggregation, propagate_and_optimize_mode, | ||||||
| from gnpy.core.exceptions import ConfigurationError, EquipmentConfigError, NetworkTopologyError |                                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 | import gnpy.core.ansi_escapes as ansi_escapes | ||||||
|  | from gnpy.core.spectrum_assignment import (build_oms_list, pth_assign_spectrum) | ||||||
| from copy import copy, deepcopy | from copy import copy, deepcopy | ||||||
| from textwrap import dedent | from textwrap import dedent | ||||||
| from math import ceil | from math import ceil | ||||||
|  |  | ||||||
|  | from flask import Flask, jsonify, make_response, request | ||||||
|  | from flask_restful import Api, Resource, reqparse, fields | ||||||
|  |  | ||||||
| #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | ||||||
|  |  | ||||||
| logger = getLogger(__name__) | LOGGER = getLogger(__name__) | ||||||
|  |  | ||||||
| parser = ArgumentParser(description = 'A function that computes performances for a list of services provided in a json file or an excel sheet.') | PARSER = ArgumentParser(description='A function that computes performances for a list of ' + | ||||||
| parser.add_argument('network_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') |                         'services provided in a json file or an excel sheet.') | ||||||
| parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') | PARSER.add_argument('network_filename', nargs='?', type=Path,\ | ||||||
| parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') |                     default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\ | ||||||
| parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') |                     help='input topology file in xls or json') | ||||||
| parser.add_argument('-o', '--output', type = Path) | PARSER.add_argument('service_filename', nargs='?', type=Path,\ | ||||||
|  |                     default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\ | ||||||
|  |                     help='input service file in xls or json') | ||||||
|  | PARSER.add_argument('eqpt_filename', nargs='?', type=Path,\ | ||||||
|  |                     default=Path(__file__).parent / 'eqpt_config.json',\ | ||||||
|  |                     help='input equipment library in json. Default is eqpt_config.json') | ||||||
|  | PARSER.add_argument('-bi', '--bidir', action='store_true',\ | ||||||
|  |                     help='considers that all demands are bidir') | ||||||
|  | PARSER.add_argument('-v', '--verbose', action='count', default=0,\ | ||||||
|  |                     help='increases verbosity for each occurence') | ||||||
|  | PARSER.add_argument('-o', '--output', type=Path) | ||||||
|  | PARSER.add_argument('-r', '--rest', action='count', default=0, help='use the REST API') | ||||||
|  |  | ||||||
|  | NETWORK_FILENAME = 'topoDemov1.json' #'disagregatedTopoDemov1.json' # | ||||||
|  |  | ||||||
|  | APP = Flask(__name__, static_url_path="") | ||||||
|  | API = Api(APP) | ||||||
|  |  | ||||||
| def requests_from_json(json_data, equipment): | def requests_from_json(json_data, equipment): | ||||||
|  |     """ converts the json data into a list of requests elements | ||||||
|  |     """ | ||||||
|     requests_list = [] |     requests_list = [] | ||||||
|  |  | ||||||
|     for req in json_data['path-request']: |     for req in json_data['path-request']: | ||||||
|         # init all params from request |         # init all params from request | ||||||
|         params = {} |         params = {} | ||||||
|         params['request_id'] = req['request-id'] |         params['request_id'] = req['request-id'] | ||||||
|         params['source'] = req['src-tp-id'] |         params['source'] = req['source'] | ||||||
|         params['destination'] = req['dst-tp-id'] |         params['bidir'] = req['bidirectional'] | ||||||
|  |         params['destination'] = req['destination'] | ||||||
|         params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type'] |         params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type'] | ||||||
|         params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode'] |         params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode'] | ||||||
|         params['format'] = params['trx_mode'] |         params['format'] = params['trx_mode'] | ||||||
|         nd_list = req['optimizations']['explicit-route-include-objects'] |  | ||||||
|         params['nodes_list'] = [n['unnumbered-hop']['node-id'] for n in nd_list] |  | ||||||
|         params['loose_list'] = [n['unnumbered-hop']['hop-type'] for n in nd_list] |  | ||||||
|         params['spacing'] = req['path-constraints']['te-bandwidth']['spacing'] |         params['spacing'] = req['path-constraints']['te-bandwidth']['spacing'] | ||||||
|  |         try: | ||||||
|  |             nd_list = req['explicit-route-objects']['route-object-include-exclude'] | ||||||
|  |         except KeyError: | ||||||
|  |             nd_list = [] | ||||||
|  |         params['nodes_list'] = [n['num-unnum-hop']['node-id'] for n in nd_list] | ||||||
|  |         params['loose_list'] = [n['num-unnum-hop']['hop-type'] for n in nd_list] | ||||||
|         # recover trx physical param (baudrate, ...) from type and mode |         # recover trx physical param (baudrate, ...) from type and mode | ||||||
|         # in trx_mode_params optical power is read from equipment['SI']['default'] and |         # in trx_mode_params optical power is read from equipment['SI']['default'] and | ||||||
|         # nb_channel is computed based on min max frequency and spacing |         # nb_channel is computed based on min max frequency and spacing | ||||||
| @@ -73,12 +98,15 @@ def requests_from_json(json_data,equipment): | |||||||
|         # print(trx_params['min_spacing']) |         # print(trx_params['min_spacing']) | ||||||
|         # optical power might be set differently in the request. if it is indicated then the |         # optical power might be set differently in the request. if it is indicated then the | ||||||
|         # params['power'] is updated |         # params['power'] is updated | ||||||
|  |         try: | ||||||
|             if req['path-constraints']['te-bandwidth']['output-power']: |             if req['path-constraints']['te-bandwidth']['output-power']: | ||||||
|                 params['power'] = req['path-constraints']['te-bandwidth']['output-power'] |                 params['power'] = req['path-constraints']['te-bandwidth']['output-power'] | ||||||
|  |         except KeyError: | ||||||
|  |             pass | ||||||
|         # same process for nb-channel |         # same process for nb-channel | ||||||
|         f_min = params['f_min'] |         f_min = params['f_min'] | ||||||
|         f_max_from_si = params['f_max'] |         f_max_from_si = params['f_max'] | ||||||
|  |         try: | ||||||
|             if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None: |             if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None: | ||||||
|                 nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] |                 nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] | ||||||
|                 params['nb_channel'] = nch |                 params['nb_channel'] = nch | ||||||
| @@ -86,7 +114,8 @@ def requests_from_json(json_data,equipment): | |||||||
|                 params['f_max'] = f_min + nch*spacing |                 params['f_max'] = f_min + nch*spacing | ||||||
|             else: |             else: | ||||||
|                 params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing']) |                 params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing']) | ||||||
|  |         except KeyError: | ||||||
|  |             params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing']) | ||||||
|         consistency_check(params, f_max_from_si) |         consistency_check(params, f_max_from_si) | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
| @@ -97,192 +126,251 @@ def requests_from_json(json_data,equipment): | |||||||
|     return requests_list |     return requests_list | ||||||
|  |  | ||||||
| def consistency_check(params, f_max_from_si): | def consistency_check(params, f_max_from_si): | ||||||
|  |     """ checks that the requested parameters are consistant (spacing vs nb channel, | ||||||
|  |         vs transponder mode...) | ||||||
|  |     """ | ||||||
|     f_min = params['f_min'] |     f_min = params['f_min'] | ||||||
|     f_max = params['f_max'] |     f_max = params['f_max'] | ||||||
|     max_recommanded_nb_channels = automatic_nch(f_min,f_max, |     max_recommanded_nb_channels = automatic_nch(f_min, f_max, params['spacing']) | ||||||
|                 params['spacing']) |  | ||||||
|     if params['baud_rate'] is not None: |     if params['baud_rate'] is not None: | ||||||
|         #implicitely means that a mode is defined with min_spacing |         #implicitely means that a mode is defined with min_spacing | ||||||
|         if params['min_spacing'] > params['spacing']: |         if params['min_spacing'] > params['spacing']: | ||||||
|             msg = f'Request {params["request_id"]} has spacing below transponder {params["trx_type"]}'+\ |             msg = f'Request {params["request_id"]} has spacing below transponder ' +\ | ||||||
|                 f' {params["trx_mode"]} min spacing value {params["min_spacing"]*1e-9}GHz.\n'+\ |                   f'{params["trx_type"]} {params["trx_mode"]} min spacing value ' +\ | ||||||
|                 'Computation stopped' |                   f'{params["min_spacing"]*1e-9}GHz.\nComputation stopped' | ||||||
|             print(msg) |             print(msg) | ||||||
|             logger.critical(msg) |             LOGGER.critical(msg) | ||||||
|             exit() |             raise ServiceError(msg) | ||||||
|         if f_max > f_max_from_si: |         if f_max > f_max_from_si: | ||||||
|             msg = dedent(f''' |             msg = dedent(f''' | ||||||
|             Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz  |             Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz  | ||||||
|             is not consistent with frequency range {f_min*1e-12} THz, {f_max*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz. |             is not consistent with frequency range {f_min*1e-12} THz, {f_max*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz. | ||||||
|             max recommanded nb of channels is {max_recommanded_nb_channels} |             max recommanded nb of channels is {max_recommanded_nb_channels} | ||||||
|             Computation stopped.''') |             Computation stopped.''') | ||||||
|             logger.critical(msg) |             LOGGER.critical(msg) | ||||||
|             exit()     |             raise ServiceError(msg) | ||||||
|  |  | ||||||
|  |  | ||||||
| def disjunctions_from_json(json_data): | def disjunctions_from_json(json_data): | ||||||
|  |     """ reads the disjunction requests from the json dict and create the list | ||||||
|  |         of requested disjunctions for this set of requests | ||||||
|  |     """ | ||||||
|     disjunctions_list = [] |     disjunctions_list = [] | ||||||
|  |     try: | ||||||
|  |         temp_test = json_data['synchronization'] | ||||||
|  |     except KeyError: | ||||||
|  |         temp_test = [] | ||||||
|  |     if temp_test: | ||||||
|         for snc in json_data['synchronization']: |         for snc in json_data['synchronization']: | ||||||
|             params = {} |             params = {} | ||||||
|             params['disjunction_id'] = snc['synchronization-id'] |             params['disjunction_id'] = snc['synchronization-id'] | ||||||
|             params['relaxable'] = snc['svec']['relaxable'] |             params['relaxable'] = snc['svec']['relaxable'] | ||||||
|         params['link_diverse'] = snc['svec']['link-diverse'] |             params['link_diverse'] = 'link' in snc['svec']['disjointness'] | ||||||
|         params['node_diverse'] = snc['svec']['node-diverse'] |             params['node_diverse'] = 'node' in snc['svec']['disjointness'] | ||||||
|             params['disjunctions_req'] = snc['svec']['request-id-number'] |             params['disjunctions_req'] = snc['svec']['request-id-number'] | ||||||
|             disjunctions_list.append(Disjunction(**params)) |             disjunctions_list.append(Disjunction(**params)) | ||||||
|  |  | ||||||
|     return disjunctions_list |     return disjunctions_list | ||||||
|  |  | ||||||
|  |  | ||||||
| def load_requests(filename,eqpt_filename): | def load_requests(filename, eqpt_filename, bidir): | ||||||
|  |     """ loads the requests from a json or an excel file into a data string | ||||||
|  |     """ | ||||||
|     if filename.suffix.lower() == '.xls': |     if filename.suffix.lower() == '.xls': | ||||||
|         logger.info('Automatically converting requests from XLS to JSON') |         LOGGER.info('Automatically converting requests from XLS to JSON') | ||||||
|         json_data = convert_service_sheet(filename,eqpt_filename) |         try: | ||||||
|  |             json_data = convert_service_sheet(filename, eqpt_filename, bidir=bidir) | ||||||
|  |         except ServiceError as this_e: | ||||||
|  |             print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||||
|  |             exit(1) | ||||||
|     else: |     else: | ||||||
|         with open(filename, encoding='utf-8') as f: |         with open(filename, encoding='utf-8') as my_f: | ||||||
|             json_data = loads(f.read()) |             json_data = loads(my_f.read()) | ||||||
|     return json_data |     return json_data | ||||||
|  |  | ||||||
| def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): | def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): | ||||||
|      |     """ use a list but a dictionnary might be helpful to find path based on request_id | ||||||
|     # use a list but a dictionnary might be helpful to find path bathsed on request_id |         TODO change all these req, dsjct, res lists into dict ! | ||||||
|     # TODO change all these req, dsjct, res lists into dict ! |     """ | ||||||
|     path_res_list = [] |     path_res_list = [] | ||||||
|  |     reversed_path_res_list = [] | ||||||
|  |     propagated_reversed_path_res_list = [] | ||||||
|  |  | ||||||
|     for i, pathreq in enumerate(pathreqlist): |     for i, pathreq in enumerate(pathreqlist): | ||||||
|  |  | ||||||
|         # use the power specified in requests but might be different from the one specified for design |         # use the power specified in requests but might be different from the one | ||||||
|         # the power is an optional parameter for requests definition |         # specified for design the power is an optional parameter for requests | ||||||
|         # if optional, use the one defines in eqt_config.json |         # definition if optional, use the one defines in eqt_config.json | ||||||
|         p_db = lin2db(pathreq.power*1e3) |         p_db = lin2db(pathreq.power*1e3) | ||||||
|         p_total_db = p_db + lin2db(pathreq.nb_channel) |         p_total_db = p_db + lin2db(pathreq.nb_channel) | ||||||
|         print(f'request {pathreq.request_id}') |         print(f'request {pathreq.request_id}') | ||||||
|         print(f'Computing path from {pathreq.source} to {pathreq.destination}') |         print(f'Computing path from {pathreq.source} to {pathreq.destination}') | ||||||
|         print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output |         # adding first node to be clearer on the output | ||||||
|  |         print(f'with path constraint: {[pathreq.source] + pathreq.nodes_list}') | ||||||
|  |  | ||||||
|         total_path = pathlist[i] |         # pathlist[i] contains the whole path information for request i | ||||||
|         print(f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}\n') |         # last element is a transciver and where the result of the propagation is | ||||||
|  |         # recorded. | ||||||
|  |         # Important Note: since transceivers attached to roadms are actually logical | ||||||
|  |         # elements to simulate performance, several demands having the same destination | ||||||
|  |         # may use the same transponder for the performance simulation. This is why | ||||||
|  |         # we use deepcopy: to ensure that each propagation is recorded and not overwritten | ||||||
|  |         total_path = deepcopy(pathlist[i]) | ||||||
|  |         print(f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}') | ||||||
|         # for debug |         # for debug | ||||||
|         # print(f'{pathreq.baud_rate}   {pathreq.power}   {pathreq.spacing}   {pathreq.nb_channel}') |         # print(f'{pathreq.baud_rate}   {pathreq.power}   {pathreq.spacing}   {pathreq.nb_channel}') | ||||||
|         if total_path: |         if total_path: | ||||||
|             if pathreq.baud_rate is not None: |             if pathreq.baud_rate is not None: | ||||||
|  |                 # 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) |                 total_path = propagate(total_path, pathreq, equipment) | ||||||
|                 # for el in total_path: print(el) |  | ||||||
|                 temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2) |                 temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2) | ||||||
|                 if temp_snr01nm < pathreq.OSNR: |                 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' +\ |                     msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\ | ||||||
|                     f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}\n' |                           f' {pathreq.source} to {pathreq.destination} does not pass with' +\ | ||||||
|  |                           f' {pathreq.tsp_mode}\n\tcomputedSNR in 0.1nm = {temp_snr01nm} ' +\ | ||||||
|  |                           f'- required osnr {pathreq.OSNR}' | ||||||
|                     print(msg) |                     print(msg) | ||||||
|                     logger.warning(msg) |                     LOGGER.warning(msg) | ||||||
|                     total_path = [] |                     pathreq.blocking_reason = 'MODE_NOT_FEASIBLE' | ||||||
|             else: |             else: | ||||||
|                 total_path, mode = propagate_and_optimize_mode(total_path, pathreq, equipment) |                 total_path, mode = propagate_and_optimize_mode(total_path, pathreq, equipment) | ||||||
|                 # if no baudrate satisfies spacing, no mode is returned and an empty path is returned |                 # if no baudrate satisfies spacing, no mode is returned and the last explored mode | ||||||
|                 # a warning is shown in the propagate_and_optimize_mode |                 # a warning is shown in the propagate_and_optimize_mode | ||||||
|                 if mode is not None : |  | ||||||
|                 # propagate_and_optimize_mode function returns the mode with the highest bitrate |                 # propagate_and_optimize_mode function returns the mode with the highest bitrate | ||||||
|                     # that passes. if no mode passes, then 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.baud_rate = mode['baud_rate'] | ||||||
|                     pathreq.tsp_mode = mode['format'] |                     pathreq.tsp_mode = mode['format'] | ||||||
|                     pathreq.format = mode['format'] |                     pathreq.format = mode['format'] | ||||||
|                     pathreq.OSNR = mode['OSNR'] |                     pathreq.OSNR = mode['OSNR'] | ||||||
|                     pathreq.tx_osnr = mode['tx_osnr'] |                     pathreq.tx_osnr = mode['tx_osnr'] | ||||||
|                     pathreq.bit_rate = mode['bit_rate'] |                     pathreq.bit_rate = mode['bit_rate'] | ||||||
|                 else : |  | ||||||
|                     total_path = [] |  | ||||||
|         # 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)) |             # reversed path is needed for correct spectrum assignment | ||||||
|     return path_res_list |             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): | def correct_route_list(network, pathreqlist): | ||||||
|     # prepares the format of route list of nodes to be consistant |     """ prepares the format of route list of nodes to be consistant | ||||||
|     # remove wrong names, remove endpoints |         remove wrong names, remove endpoints | ||||||
|     # also correct source and destination |         also correct source and destination | ||||||
|     anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver) and not isinstance(n, Fiber)] |     """ | ||||||
|     # TODO there is a problem of identification of fibers in case of parallel fibers bitween two adjacent roadms |     anytype = [n.uid for n in network.nodes()] | ||||||
|     # so fiber constraint is not supported |     # TODO there is a problem of identification of fibers in case of parallel fibers | ||||||
|  |     # between two adjacent roadms so fiber constraint is not supported | ||||||
|     transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)] |     transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)] | ||||||
|     for pathreq in pathreqlist: |     for pathreq in pathreqlist: | ||||||
|         for i, n_id in enumerate(pathreq.nodes_list): |         for i, n_id in enumerate(pathreq.nodes_list): | ||||||
|             # replace possibly wrong name with a formated roadm name |             # replace possibly wrong name with a formated roadm name | ||||||
|             # print(n_id) |             # print(n_id) | ||||||
|             if n_id not in anytype: |             if n_id not in anytype: | ||||||
|  |                 # find nodes name that include constraint among all possible names except | ||||||
|  |                 # transponders (not yet supported as constraints). | ||||||
|                 nodes_suggestion = [uid for uid in anytype \ |                 nodes_suggestion = [uid for uid in anytype \ | ||||||
|                     if n_id.lower() in uid.lower()] |                     if n_id.lower() in uid.lower() and uid not in transponders] | ||||||
|                 if pathreq.loose_list[i] == 'loose': |                 if pathreq.loose_list[i] == 'LOOSE': | ||||||
|                     if len(nodes_suggestion) > 0: |                     if len(nodes_suggestion) > 0: | ||||||
|                         new_n = nodes_suggestion[0] |                         new_n = nodes_suggestion[0] | ||||||
|                         print(f'invalid route node specified:\ |                         print(f'invalid route node specified:\ | ||||||
|                         \n\'{n_id}\', replaced with \'{new_n}\'') |                         \n\'{n_id}\', replaced with \'{new_n}\'') | ||||||
|                         pathreq.nodes_list[i] = new_n |                         pathreq.nodes_list[i] = new_n | ||||||
|                     else: |                     else: | ||||||
|                         print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!'+'\x1b[0m') |                         print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\',' +\ | ||||||
|  |                               f' could not use it as constraint, skipped!'+'\x1b[0m') | ||||||
|                         pathreq.nodes_list.remove(n_id) |                         pathreq.nodes_list.remove(n_id) | ||||||
|                         pathreq.loose_list.pop(i) |                         pathreq.loose_list.pop(i) | ||||||
|                 else: |                 else: | ||||||
|                     msg = f'\x1b[1;33;40m'+f'could not find node : {n_id} in network topology. Strict constraint can not be applied.'+'\x1b[0m' |                     msg = f'\x1b[1;33;40m'+f'could not find node: {n_id} in network topology.' +\ | ||||||
|                     logger.critical(msg) |                           f' Strict constraint can not be applied.' + '\x1b[0m' | ||||||
|  |                     LOGGER.critical(msg) | ||||||
|                     raise ValueError(msg) |                     raise ValueError(msg) | ||||||
|         if pathreq.source not in transponders: |         if pathreq.source not in transponders: | ||||||
|             msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.'+'\x1b[0m' |             msg = f'\x1b[1;31;40m' + f'Request: {pathreq.request_id}: could not find' +\ | ||||||
|             logger.critical(msg) |                   f' transponder source: {pathreq.source}.'+'\x1b[0m' | ||||||
|  |             LOGGER.critical(msg) | ||||||
|             print(f'{msg}\nComputation stopped.') |             print(f'{msg}\nComputation stopped.') | ||||||
|             exit() |             raise ServiceError(msg) | ||||||
|  |  | ||||||
|         if pathreq.destination not in transponders: |         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' |             msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find' +\ | ||||||
|             logger.critical(msg) |                   f' transponder destination: {pathreq.destination}.'+'\x1b[0m' | ||||||
|  |             LOGGER.critical(msg) | ||||||
|             print(f'{msg}\nComputation stopped.') |             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 |     return pathreqlist | ||||||
|  |  | ||||||
| def correct_disjn(disjn): | def correct_disjn(disjn): | ||||||
|  |     """ clean disjunctions to remove possible repetition | ||||||
|  |     """ | ||||||
|     local_disjn = disjn.copy() |     local_disjn = disjn.copy() | ||||||
|     for el in local_disjn: |     for elem in local_disjn: | ||||||
|         for d in local_disjn: |         for dis_elem in local_disjn: | ||||||
|             if set(el.disjunctions_req) == set(d.disjunctions_req) and\ |             if set(elem.disjunctions_req) == set(dis_elem.disjunctions_req) and\ | ||||||
|              el.disjunction_id != d.disjunction_id: |              elem.disjunction_id != dis_elem.disjunction_id: | ||||||
|                 local_disjn.remove(d) |                 local_disjn.remove(dis_elem) | ||||||
|     return local_disjn |     return local_disjn | ||||||
|  |  | ||||||
|  |  | ||||||
| def path_result_json(pathresult): | def path_result_json(pathresult): | ||||||
|  |     """ create the response dictionnary | ||||||
|  |     """ | ||||||
|     data = { |     data = { | ||||||
|         'path': [n.json for n in pathresult] |         'response': [n.json for n in pathresult] | ||||||
|     } |     } | ||||||
|     return data |     return data | ||||||
|  |  | ||||||
|  | def compute_requests(network, data, equipment): | ||||||
| if __name__ == '__main__': |     """ Main program calling functions | ||||||
|     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) |  | ||||||
|     try: |  | ||||||
|         data = load_requests(args.service_filename,args.eqpt_filename) |  | ||||||
|         equipment = load_equipment(args.eqpt_filename) |  | ||||||
|         network = load_network(args.network_filename,equipment) |  | ||||||
|     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) |  | ||||||
|  |  | ||||||
|     # Build the network once using the default power defined in SI in eqpt config |     # Build the network once using the default power defined in SI in eqpt config | ||||||
|     # TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by |     # TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||||
|     # spacing, f_min and f_max |     # spacing, f_min and f_max | ||||||
| @@ -291,21 +379,30 @@ if __name__ == '__main__': | |||||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|     build_network(network, equipment, p_db, p_total_db) |     build_network(network, equipment, p_db, p_total_db) | ||||||
|     save_network(args.network_filename, network) |     save_network(ARGS.network_filename, network) | ||||||
|  |  | ||||||
|  |     oms_list = build_oms_list(network, equipment) | ||||||
|  |  | ||||||
|  |     try: | ||||||
|         rqs = requests_from_json(data, equipment) |         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 |     # 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] |     all_ids = [r.request_id for r in rqs] | ||||||
|     if len(all_ids) != len(set(all_ids)): |     if len(all_ids) != len(set(all_ids)): | ||||||
|         for a in list(set(all_ids)): |         for item in list(set(all_ids)): | ||||||
|             all_ids.remove(a) |             all_ids.remove(item) | ||||||
|         msg = f'Requests id {all_ids} are not unique' |         msg = f'Requests id {all_ids} are not unique' | ||||||
|         logger.critical(msg) |         LOGGER.critical(msg) | ||||||
|         exit() |         raise ServiceError(msg) | ||||||
|  |     try: | ||||||
|         rqs = correct_route_list(network, rqs) |         rqs = correct_route_list(network, rqs) | ||||||
|  |     except ServiceError as this_e: | ||||||
|  |         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||||
|  |         raise this_e | ||||||
|  |         #exit(1) | ||||||
|     # pths = compute_path(network, equipment, rqs) |     # pths = compute_path(network, equipment, rqs) | ||||||
|     dsjn = disjunctions_from_json(data) |     dsjn = disjunctions_from_json(data) | ||||||
|  |  | ||||||
| @@ -325,23 +422,50 @@ if __name__ == '__main__': | |||||||
|     print(rqs) |     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) |         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') |     print('\x1b[1;34;40m' + f'Propagating on selected path' + '\x1b[0m') | ||||||
|     propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) |     propagatedpths, reversed_pths, reversed_propagatedpths = \ | ||||||
|  |         compute_path_with_disjunction(network, equipment, rqs, pths) | ||||||
|  |     # Note that deepcopy used in compute_path_with_disjunction returns | ||||||
|  |     # a list of nodes which are not belonging to network (they are copies of the node objects). | ||||||
|  |     # so there can not be propagation on these nodes. | ||||||
|  |  | ||||||
|  |     pth_assign_spectrum(pths, rqs, oms_list, reversed_pths) | ||||||
|  |  | ||||||
|     print('\x1b[1;34;40m'+f'Result summary'+ '\x1b[0m') |     print('\x1b[1;34;40m'+f'Result summary'+ '\x1b[0m') | ||||||
|  |     header = ['req id', '  demand', '  snr@bandwidth A-Z (Z-A)', '  snr@0.1nm A-Z (Z-A)',\ | ||||||
|     header = ['req id', '  demand','  snr@bandwidth','  snr@0.1nm','  Receiver minOSNR', '  mode', '  Gbit/s' , '  nb of tsp pairs'] |               '  Receiver minOSNR', '  mode', '  Gbit/s', '  nb of tsp pairs',\ | ||||||
|  |               'N,M or blocking reason'] | ||||||
|     data = [] |     data = [] | ||||||
|     data.append(header) |     data.append(header) | ||||||
|     for i, p in enumerate(propagatedpths): |     for i, this_p in enumerate(propagatedpths): | ||||||
|         if p: |         rev_pth = reversed_propagatedpths[i] | ||||||
|             line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ |         if rev_pth and this_p: | ||||||
|                 f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ |             psnrb = f'{round(mean(this_p[-1].snr),2)} ({round(mean(rev_pth[-1].snr),2)})' | ||||||
|                 f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}' , f'{round(rqs[i].path_bandwidth * 1e-9,2)}' , f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }'] |             psnr = f'{round(mean(this_p[-1].snr_01nm), 2)}' +\ | ||||||
|  |                    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: |             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) |         data.append(line) | ||||||
|  |  | ||||||
|     col_width = max(len(word) for row in data for word in row[2:])   # padding |     col_width = max(len(word) for row in data for word in row[2:])   # padding | ||||||
| @@ -352,18 +476,94 @@ if __name__ == '__main__': | |||||||
|         secondcol = ''.join(row[1].ljust(secondcol_width)) |         secondcol = ''.join(row[1].ljust(secondcol_width)) | ||||||
|         remainingcols = ''.join(word.center(col_width, ' ') for word in row[2:]) |         remainingcols = ''.join(word.center(col_width, ' ') for word in row[2:]) | ||||||
|         print(f'{firstcol} {secondcol} {remainingcols}') |         print(f'{firstcol} {secondcol} {remainingcols}') | ||||||
|  |     print('\x1b[1;33;40m'+f'Result summary shows mean SNR and OSNR (average over all channels)' +\ | ||||||
|  |           '\x1b[0m') | ||||||
|  |  | ||||||
|  |     return propagatedpths, reversed_propagatedpths, rqs | ||||||
|  |  | ||||||
|  |  | ||||||
|     if args.output : | def launch_cli(network, data, equipment): | ||||||
|  |     """ Compute requests using network, data and equipment with client line interface | ||||||
|  |     """ | ||||||
|  |     propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment) | ||||||
|  |     #Generate the output | ||||||
|  |     if ARGS.output : | ||||||
|         result = [] |         result = [] | ||||||
|         # assumes that list of rqs and list of propgatedpths have same order |         # assumes that list of rqs and list of propgatedpths have same order | ||||||
|         for i,p in enumerate(propagatedpths): |         for i, pth in enumerate(propagatedpths): | ||||||
|             result.append(Result_element(rqs[i],p)) |             result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) | ||||||
|         temp = path_result_json(result) |         temp = path_result_json(result) | ||||||
|         fnamecsv = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.csv' |         fnamecsv = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.csv' | ||||||
|         fnamejson = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.json' |         fnamejson = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.json' | ||||||
|         with open(fnamejson, 'w', encoding='utf-8') as f: |         with open(fnamejson, 'w', encoding='utf-8') as fjson: | ||||||
|             f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) |             fjson.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) | ||||||
|             with open(fnamecsv, "w", encoding='utf-8') as fcsv: |             with open(fnamecsv, "w", encoding='utf-8') as fcsv: | ||||||
|                 jsontocsv(temp, equipment, fcsv) |                 jsontocsv(temp, equipment, fcsv) | ||||||
|                 print('\x1b[1;34;40m'+f'saving in {args.output} and {fnamecsv}'+ '\x1b[0m') |                 print('\x1b[1;34;40m'+f'saving in {ARGS.output} and {fnamecsv}'+ '\x1b[0m') | ||||||
|  |  | ||||||
|  | class GnpyAPI(Resource): | ||||||
|  |     """ Compute requests using network, data and equipment with rest api | ||||||
|  |     """ | ||||||
|  |     def get(self): | ||||||
|  |         return {"ping": True}, 200 | ||||||
|  |  | ||||||
|  |     def post(self): | ||||||
|  |         data = request.get_json() | ||||||
|  |         equipment = load_equipment('examples/2019-demo-equipment.json') | ||||||
|  |         topo_json = load_json('examples/2019-demo-topology.json') | ||||||
|  |         network = network_from_json(topo_json, equipment) | ||||||
|  |         try: | ||||||
|  |             propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment) | ||||||
|  |             # Generate the output | ||||||
|  |             result = [] | ||||||
|  |             #assumes that list of rqs and list of propgatedpths have same order | ||||||
|  |             for i, pth in enumerate(propagatedpths): | ||||||
|  |                 result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) | ||||||
|  |  | ||||||
|  |             return {"result":path_result_json(result)}, 201 | ||||||
|  |         except ServiceError as this_e: | ||||||
|  |             msg = f'Service error: {this_e}' | ||||||
|  |             return {"result": msg}, 400 | ||||||
|  |  | ||||||
|  | API.add_resource(GnpyAPI, '/gnpy-experimental') | ||||||
|  |  | ||||||
|  | def main(args): | ||||||
|  |     """ main function that calls all functions | ||||||
|  |     """ | ||||||
|  |     LOGGER.info(f'Computing path requests {args.service_filename} into JSON format') | ||||||
|  |     print('\x1b[1;34;40m' +\ | ||||||
|  |           f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m') | ||||||
|  |     # for debug | ||||||
|  |     # print( args.eqpt_filename) | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         data = load_requests(args.service_filename, args.eqpt_filename, args.bidir) | ||||||
|  |         equipment = load_equipment(args.eqpt_filename) | ||||||
|  |         network = load_network(args.network_filename, equipment) | ||||||
|  |     except EquipmentConfigError as this_e: | ||||||
|  |         print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {this_e}') | ||||||
|  |         exit(1) | ||||||
|  |     except NetworkTopologyError as this_e: | ||||||
|  |         print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {this_e}') | ||||||
|  |         exit(1) | ||||||
|  |     except ConfigurationError as this_e: | ||||||
|  |         print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {this_e}') | ||||||
|  |         exit(1) | ||||||
|  |     except ServiceError as this_e: | ||||||
|  |         print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}') | ||||||
|  |         exit(1) | ||||||
|  |     # input_str = raw_input("How will you use your program: c:[cli] , a:[api] ?") | ||||||
|  |     # print(input_str) | ||||||
|  |     # | ||||||
|  |     if ((args.rest == 1) and (args.output is None)): | ||||||
|  |         print('you have chosen the rest mode') | ||||||
|  |         APP.run(host='0.0.0.0', port=5000, debug=True) | ||||||
|  |     elif ((args.rest > 1) or ((args.rest == 1) and (args.output is not None))): | ||||||
|  |         print('command is not well formulated') | ||||||
|  |     else: | ||||||
|  |         launch_cli(network, data, equipment) | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     ARGS = PARSER.parse_args() | ||||||
|  |     basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(ARGS.verbose, DEBUG)) | ||||||
|  |     main(ARGS) | ||||||
|   | |||||||
							
								
								
									
										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" | ||||||
|  |         ] | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
							
								
								
									
										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" | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
| @@ -161,7 +161,9 @@ def main(network, equipment, source, destination, sim_params, req=None): | |||||||
|                 print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:') |                 print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:') | ||||||
|             else: |             else: | ||||||
|                 print(f'\nTransmission results:') |                 print(f'\nTransmission results:') | ||||||
|         print(f'  Final SNR total (signal bw): {ansi_escapes.cyan}{mean(destination.snr):.02f} dB{ansi_escapes.reset}') |             print(f'  Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}') | ||||||
|  |         else: | ||||||
|  |             print(path[-1]) | ||||||
|  |  | ||||||
|         #print(f'\n !!!!!!!!!!!!!!!!!     TEST POINT         !!!!!!!!!!!!!!!!!!!!!') |         #print(f'\n !!!!!!!!!!!!!!!!!     TEST POINT         !!!!!!!!!!!!!!!!!!!!!') | ||||||
|         #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}') |         #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}') | ||||||
| @@ -280,6 +282,7 @@ if __name__ == '__main__': | |||||||
|     params['trx_mode'] = '' |     params['trx_mode'] = '' | ||||||
|     params['source'] = source.uid |     params['source'] = source.uid | ||||||
|     params['destination'] = destination.uid |     params['destination'] = destination.uid | ||||||
|  |     params['bidir'] = False | ||||||
|     params['nodes_list'] = [destination.uid] |     params['nodes_list'] = [destination.uid] | ||||||
|     params['loose_list'] = ['strict'] |     params['loose_list'] = ['strict'] | ||||||
|     params['format'] = '' |     params['format'] = '' | ||||||
|   | |||||||
| @@ -32,6 +32,7 @@ from json import dumps | |||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from difflib import get_close_matches | from difflib import get_close_matches | ||||||
| from gnpy.core.utils import silent_remove | from gnpy.core.utils import silent_remove | ||||||
|  | from gnpy.core.exceptions import NetworkTopologyError | ||||||
| import time | import time | ||||||
|  |  | ||||||
| all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) | all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) | ||||||
| @@ -509,9 +510,12 @@ def parse_excel(input_filename): | |||||||
|     all_cities = Counter(n.city for n in nodes) |     all_cities = Counter(n.city for n in nodes) | ||||||
|     if len(all_cities) != len(nodes): |     if len(all_cities) != len(nodes): | ||||||
|         raise ValueError(f'Duplicate city: {all_cities}') |         raise ValueError(f'Duplicate city: {all_cities}') | ||||||
|     if any(ln.from_city not in all_cities or |     bad_links = [] | ||||||
|            ln.to_city   not in all_cities for ln in links): |     for lnk in links: | ||||||
|         raise ValueError(f'Bad link.') |         if lnk.from_city not in all_cities or lnk.to_city not in all_cities: | ||||||
|  |             bad_links.append([lnk.from_city, lnk.to_city]) | ||||||
|  |     if bad_links: | ||||||
|  |         raise NetworkTopologyError(f'Bad link(s): {bad_links}.') | ||||||
|  |  | ||||||
|     return nodes, links, eqpts |     return nodes, links, eqpts | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ from collections import namedtuple | |||||||
|  |  | ||||||
| from gnpy.core.node import Node | from gnpy.core.node import Node | ||||||
| from gnpy.core.units import UNITS | from gnpy.core.units import UNITS | ||||||
| from gnpy.core.utils import lin2db, db2lin, itufs, itufl, snr_sum | from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum | ||||||
| from gnpy.core.science_utils import propagate_raman_fiber, _psi | from gnpy.core.science_utils import propagate_raman_fiber, _psi | ||||||
|  |  | ||||||
| class Transceiver(Node): | class Transceiver(Node): | ||||||
| @@ -118,16 +118,19 @@ class Transceiver(Node): | |||||||
|         self._calc_snr(spectral_info) |         self._calc_snr(spectral_info) | ||||||
|         return spectral_info |         return spectral_info | ||||||
|  |  | ||||||
| RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr restrictions') | RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr restrictions per_degree_target_pch_out_db') | ||||||
|  |  | ||||||
| class Roadm(Node): | class Roadm(Node): | ||||||
|     def __init__(self, *args, params, **kwargs): |     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) |         super().__init__(*args, params=RoadmParams(**params), **kwargs) | ||||||
|         self.loss = 0 #auto-design interest |         self.loss = 0 #auto-design interest | ||||||
|         self.effective_loss = None |         self.effective_loss = None | ||||||
|         self.effective_pch_out_db = self.params.target_pch_out_db |         self.effective_pch_out_db = self.params.target_pch_out_db | ||||||
|         self.passive = True |         self.passive = True | ||||||
|         self.restrictions = self.params.restrictions |         self.restrictions = self.params.restrictions | ||||||
|  |         self.per_degree_target_pch_out_db = self.params.per_degree_target_pch_out_db | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def to_json(self): |     def to_json(self): | ||||||
| @@ -135,7 +138,8 @@ class Roadm(Node): | |||||||
|                 'type'      : type(self).__name__, |                 'type'      : type(self).__name__, | ||||||
|                 'params'    : { |                 'params'    : { | ||||||
|                     'target_pch_out_db' : self.effective_pch_out_db, |                     'target_pch_out_db' : self.effective_pch_out_db, | ||||||
|                     'restrictions'      : self.restrictions |                     'restrictions'      : self.restrictions, | ||||||
|  |                     'per_degree_target_pch_out_db': self.per_degree_target_pch_out_db | ||||||
|                     }, |                     }, | ||||||
|                 'metadata'      : { |                 'metadata'      : { | ||||||
|                     'location': self.metadata['location']._asdict() |                     'location': self.metadata['location']._asdict() | ||||||
| @@ -150,15 +154,26 @@ class Roadm(Node): | |||||||
|                           f'  effective loss (dB):  {self.effective_loss:.2f}', |                           f'  effective loss (dB):  {self.effective_loss:.2f}', | ||||||
|                           f'  pch out (dBm):        {self.effective_pch_out_db!r}']) |                           f'  pch out (dBm):        {self.effective_pch_out_db!r}']) | ||||||
|  |  | ||||||
|     def propagate(self, pref, *carriers): |     def propagate(self, pref, *carriers, degree): | ||||||
|         #pin_target and loss are read from eqpt_config.json['Roadm'] |         #pin_target and loss are read from eqpt_config.json['Roadm'] | ||||||
|         #all ingress channels in xpress are set to this power level |         #all ingress channels in xpress are set to this power level | ||||||
|         #but add channels are not, so we define an effective loss |         #but add channels are not, so we define an effective loss | ||||||
|         #in the case of add channels |         #in the case of add channels | ||||||
|         self.effective_pch_out_db = min(pref.p_spani, self.params.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: | ||||||
|  |             # 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 |         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_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.params.target_pch_out_db, carriers_power)) |         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) |         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)) |         carriers_att = list(map(lambda x: db2lin(x+exceeding_att), carriers_att)) | ||||||
|         for carrier_att, carrier in zip(carriers_att, carriers) : |         for carrier_att, carrier in zip(carriers_att, carriers) : | ||||||
| @@ -171,8 +186,8 @@ class Roadm(Node): | |||||||
|     def update_pref(self, pref): |     def update_pref(self, pref): | ||||||
|         return pref._replace(p_span0=pref.p_span0, p_spani=self.effective_pch_out_db) |         return pref._replace(p_span0=pref.p_span0, p_spani=self.effective_pch_out_db) | ||||||
|  |  | ||||||
|     def __call__(self, spectral_info): |     def __call__(self, spectral_info, degree): | ||||||
|         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) |         carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers, degree=degree)) | ||||||
|         pref = self.update_pref(spectral_info.pref) |         pref = self.update_pref(spectral_info.pref) | ||||||
|         return spectral_info._replace(carriers=carriers, pref=pref) |         return spectral_info._replace(carriers=carriers, pref=pref) | ||||||
|  |  | ||||||
| @@ -615,7 +630,7 @@ class Edfa(Node): | |||||||
|                 self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple |                 self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple | ||||||
|         """ |         """ | ||||||
|         # TODO|jla: read amplifier actual frequencies from additional params in json |         # TODO|jla: read amplifier actual frequencies from additional params in json | ||||||
|         amplifier_freq = itufl(len(self.params.dgt), self.params.f_min, self.params.f_max) # Hz |         amplifier_freq = arrange_frequencies(len(self.params.dgt), self.params.f_min, self.params.f_max) # Hz | ||||||
|         self.channel_freq = frequencies |         self.channel_freq = frequencies | ||||||
|         self.interpol_dgt = interp(self.channel_freq, amplifier_freq, self.params.dgt) |         self.interpol_dgt = interp(self.channel_freq, amplifier_freq, self.params.dgt) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -17,3 +17,13 @@ class EquipmentConfigError(ConfigurationError): | |||||||
|  |  | ||||||
| class NetworkTopologyError(ConfigurationError): | class NetworkTopologyError(ConfigurationError): | ||||||
|     '''Topology of user-provided network is wrong''' |     '''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''' | ||||||
|  |  | ||||||
|   | |||||||
| @@ -233,7 +233,7 @@ def prev_node_generator(network, node): | |||||||
|     except StopIteration: |     except StopIteration: | ||||||
|         raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology') |         raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology') | ||||||
|     # yield and re-iterate |     # yield and re-iterate | ||||||
|     if isinstance(prev_node, Fused) or isinstance(node, Fused): |     if isinstance(prev_node, Fused) or isinstance(node, Fused) and not isinstance(prev_node, Roadm): | ||||||
|         yield prev_node |         yield prev_node | ||||||
|         yield from prev_node_generator(network, prev_node) |         yield from prev_node_generator(network, prev_node) | ||||||
|     else: |     else: | ||||||
| @@ -247,7 +247,7 @@ def next_node_generator(network, node): | |||||||
|     except StopIteration: |     except StopIteration: | ||||||
|         raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology') |         raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology') | ||||||
|     # yield and re-iterate |     # yield and re-iterate | ||||||
|     if isinstance(next_node, Fused) or isinstance(node, Fused): |     if isinstance(next_node, Fused) or isinstance(node, Fused) and not isinstance(next_node, Roadm): | ||||||
|         yield next_node |         yield next_node | ||||||
|         yield from next_node_generator(network, next_node) |         yield from next_node_generator(network, next_node) | ||||||
|     else: |     else: | ||||||
| @@ -315,6 +315,17 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db): | |||||||
|         #     node = find_last_node(next_node) |         #     node = find_last_node(next_node) | ||||||
|         #     next_node = next(n for n in network.successors(node)) |         #     next_node = next(n for n in network.successors(node)) | ||||||
|         #     next_node = find_last_node(next_node) |         #     next_node = find_last_node(next_node) | ||||||
|  |          | ||||||
|  |         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) |             prev_dp = getattr(node.params, 'target_pch_out_db', 0) | ||||||
|         dp = prev_dp |         dp = prev_dp | ||||||
|         prev_voa = 0 |         prev_voa = 0 | ||||||
|   | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -22,6 +22,7 @@ from json import dumps | |||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from gnpy.core.equipment import load_equipment | from gnpy.core.equipment import load_equipment | ||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
|  | from gnpy.core.exceptions import ServiceError | ||||||
|  |  | ||||||
| SERVICES_COLUMN = 12 | SERVICES_COLUMN = 12 | ||||||
| #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' | ||||||
| @@ -43,17 +44,18 @@ class Element: | |||||||
|         return hash((type(self), self.uid)) |         return hash((type(self), self.uid)) | ||||||
|  |  | ||||||
| class Request_element(Element): | class Request_element(Element): | ||||||
|     def __init__(self,Request,eqpt_filename): |     def __init__(self, Request, eqpt_filename, bidir): | ||||||
|         # request_id is str |         # request_id is str | ||||||
|         # excel has automatic number formatting that adds .0 on integer values |         # excel has automatic number formatting that adds .0 on integer values | ||||||
|         # the next lines recover the pure int value, assuming this .0 is unwanted |         # the next lines recover the pure int value, assuming this .0 is unwanted | ||||||
|         self.request_id = correct_xlrd_int_to_str_reading(Request.request_id) |         self.request_id = correct_xlrd_int_to_str_reading(Request.request_id) | ||||||
|         self.source = Request.source |         self.source = f'trx {Request.source}' | ||||||
|         self.destination = Request.destination |         self.destination = f'trx {Request.destination}' | ||||||
|         # TODO: the automatic naming generated by excel parser requires that source and dest name  |         # TODO: the automatic naming generated by excel parser requires that source and dest name  | ||||||
|         # be a string starting with 'trx' : this is manually added here. |         # be a string starting with 'trx' : this is manually added here. | ||||||
|         self.srctpid = f'trx {Request.source}' |         self.srctpid = f'trx {Request.source}' | ||||||
|         self.dsttpid = f'trx {Request.destination}' |         self.dsttpid = f'trx {Request.destination}' | ||||||
|  |         self.bidir = bidir | ||||||
|         # test that trx_type belongs to eqpt_config.json |         # test that trx_type belongs to eqpt_config.json | ||||||
|         # if not replace it with a default |         # if not replace it with a default | ||||||
|         equipment = load_equipment(eqpt_filename) |         equipment = load_equipment(eqpt_filename) | ||||||
| @@ -76,14 +78,14 @@ class Request_element(Element): | |||||||
|             msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Request.mode}\' in eqpt library \nComputation stopped.' |             msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Request.mode}\' in eqpt library \nComputation stopped.' | ||||||
|             #print(msg) |             #print(msg) | ||||||
|             logger.critical(msg) |             logger.critical(msg) | ||||||
|             exit() |             raise ServiceError(msg) | ||||||
|         # excel input are in GHz and dBm |         # excel input are in GHz and dBm | ||||||
|         if Request.spacing is not None: |         if Request.spacing is not None: | ||||||
|             self.spacing = Request.spacing * 1e9 |             self.spacing = Request.spacing * 1e9 | ||||||
|         else: |         else: | ||||||
|             msg = f'Request {self.request_id} missing spacing: spacing is mandatory.\ncomputation stopped' |             msg = f'Request {self.request_id} missing spacing: spacing is mandatory.\ncomputation stopped' | ||||||
|             logger.critical(msg) |             logger.critical(msg) | ||||||
|             exit() |             raise ServiceError(msg) | ||||||
|         if Request.power is not None: |         if Request.power is not None: | ||||||
|             self.power =  db2lin(Request.power) * 1e-3 |             self.power =  db2lin(Request.power) * 1e-3 | ||||||
|         else: |         else: | ||||||
| @@ -120,9 +122,9 @@ class Request_element(Element): | |||||||
|  |  | ||||||
|         # the excel parser applies the same hop-type to all nodes in the route nodes_list. |         # the excel parser applies the same hop-type to all nodes in the route nodes_list. | ||||||
|         # user can change this per node in the generated json |         # user can change this per node in the generated json | ||||||
|         self.loose = 'loose' |         self.loose = 'LOOSE' | ||||||
|         if Request.is_loose == 'no' : |         if Request.is_loose == 'no' : | ||||||
|             self.loose = 'strict' |             self.loose = 'STRICT' | ||||||
|         self.path_bandwidth = None |         self.path_bandwidth = None | ||||||
|         if Request.path_bandwidth is not None: |         if Request.path_bandwidth is not None: | ||||||
|             self.path_bandwidth = Request.path_bandwidth * 1e9 |             self.path_bandwidth = Request.path_bandwidth * 1e9 | ||||||
| @@ -132,46 +134,41 @@ class Request_element(Element): | |||||||
|     uid = property(lambda self: repr(self)) |     uid = property(lambda self: repr(self)) | ||||||
|     @property |     @property | ||||||
|     def pathrequest(self): |     def pathrequest(self): | ||||||
|  |         # Default assumption for bidir is False | ||||||
|         req_dictionnary = { |         req_dictionnary = { | ||||||
|                     'request-id':self.request_id, |                     'request-id':self.request_id, | ||||||
|                     'source':    self.source, |                     'source':    self.source, | ||||||
|                     'destination':  self.destination, |                     'destination':  self.destination, | ||||||
|                     'src-tp-id': self.srctpid, |                     'src-tp-id': self.srctpid, | ||||||
|                     'dst-tp-id': self.dsttpid, |                     'dst-tp-id': self.dsttpid, | ||||||
|  |                     'bidirectional': self.bidir, | ||||||
|                     'path-constraints':{ |                     'path-constraints':{ | ||||||
|                         'te-bandwidth': { |                         'te-bandwidth': { | ||||||
|                             'technology': 'flexi-grid', |                             'technology': 'flexi-grid', | ||||||
|                             'trx_type'  : self.trx_type, |                             'trx_type'  : self.trx_type, | ||||||
|                             'trx_mode'  : self.mode, |                             'trx_mode'  : self.mode, | ||||||
|                             'effective-freq-slot':[{'n': 'null','m': 'null'}] , |                             'effective-freq-slot':[{'N': 'null', 'M': 'null'}], | ||||||
|                             'spacing'   : self.spacing, |                             'spacing'   : self.spacing, | ||||||
|                             'max-nb-of-channel'  : self.nb_channel, |                             'max-nb-of-channel'  : self.nb_channel, | ||||||
|                             'output-power'       : self.power |                             'output-power'       : self.power | ||||||
|                             # 'path_bandwidth'       : self.path_bandwidth  |  | ||||||
|                         } |                         } | ||||||
|                     }, |                     } | ||||||
|                     'optimizations': { |                 } | ||||||
|                         'explicit-route-include-objects': [ |  | ||||||
|                         { |         if self.nodes_list: | ||||||
|  |             req_dictionnary['explicit-route-objects'] = {} | ||||||
|  |             temp = {'route-object-include-exclude' : [ | ||||||
|  |                         {'explicit-route-usage': 'route-include-ero', | ||||||
|                         'index': self.nodes_list.index(node), |                         'index': self.nodes_list.index(node), | ||||||
|                             'unnumbered-hop':{ |                         'num-unnum-hop': { | ||||||
|                             'node-id': f'{node}', |                             'node-id': f'{node}', | ||||||
|                             'link-tp-id': 'link-tp-id is not used', |                             'link-tp-id': 'link-tp-id is not used', | ||||||
|                             'hop-type': f'{self.loose}', |                             '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: |         if self.path_bandwidth is not None: | ||||||
|             req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth |             req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth | ||||||
|              |              | ||||||
| @@ -181,30 +178,41 @@ class Request_element(Element): | |||||||
|         if self.disjoint_from : |         if self.disjoint_from : | ||||||
|             return {'synchronization-id':self.request_id, |             return {'synchronization-id':self.request_id, | ||||||
|                 'svec': { |                 'svec': { | ||||||
|                     'relaxable' : 'False', |                     'relaxable' : 'false', | ||||||
|                     'link-diverse': 'True', |                     'disjointness': 'node link', | ||||||
|                     'node-diverse': 'True', |  | ||||||
|                     'request-id-number': [self.request_id]+ [n for n in self.disjoint_from] |                     'request-id-number': [self.request_id]+ [n for n in self.disjoint_from] | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         else: | ||||||
|  |             return None | ||||||
|         # TO-DO: avoid multiple entries with same synchronisation vectors |         # TO-DO: avoid multiple entries with same synchronisation vectors | ||||||
|     @property |     @property | ||||||
|     def json(self): |     def json(self): | ||||||
|         return self.pathrequest , self.pathsync |         return self.pathrequest , self.pathsync | ||||||
|  |  | ||||||
| def convert_service_sheet(input_filename, eqpt_filename, output_filename='', filter_region=[]): | def convert_service_sheet(input_filename, eqpt_filename, output_filename='', bidir=False, filter_region=None): | ||||||
|  |     """ converts a service sheet into a json structure | ||||||
|  |     """ | ||||||
|  |     if filter_region is None: | ||||||
|  |         filter_region = [] | ||||||
|     service = parse_excel(input_filename) |     service = parse_excel(input_filename) | ||||||
|     req = [Request_element(n,eqpt_filename) for n in service] |     req = [Request_element(n, eqpt_filename, bidir) for n in service] | ||||||
|     # dumps the output into a json file with name |     # dumps the output into a json file with name | ||||||
|     # split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]] |     # split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]] | ||||||
|     if output_filename=='': |     if output_filename=='': | ||||||
|         output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json' |         output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json' | ||||||
|     # for debug |     # for debug | ||||||
|     # print(json_filename) |     # print(json_filename) | ||||||
|  |     # 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 = { |         data = { | ||||||
|             'path-request': [n.json[0] for n in req], |             'path-request': [n.json[0] for n in req], | ||||||
|         'synchronization': [n.json[1] for n in req |             'synchronization': synchro | ||||||
|         if n.json[1] is not None] |         } | ||||||
|  |     else: | ||||||
|  |         data = { | ||||||
|  |             'path-request': [n.json[0] for n in req] | ||||||
|             } |             } | ||||||
|     with open(output_filename, 'w', encoding='utf-8') as f: |     with open(output_filename, 'w', encoding='utf-8') as f: | ||||||
|         f.write(dumps(data, indent=2, ensure_ascii=False)) |         f.write(dumps(data, indent=2, ensure_ascii=False)) | ||||||
| @@ -232,16 +240,20 @@ def parse_excel(input_filename): | |||||||
|     return services |     return services | ||||||
|  |  | ||||||
| def parse_service_sheet(service_sheet): | 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}') |     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 |     # 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 |     # 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 |     # create a service_fieldname independant from the excel column order | ||||||
|     # to be compatible with any version of the sheet |     # to be compatible with any version of the sheet | ||||||
|     # the following dictionnary records the excel field names and the corresponding parameter's name |     # the following dictionnary records the excel field names and the corresponding parameter's name | ||||||
|  |  | ||||||
|         authorized_fieldnames = {'route id':'request_id', 'Source':'source', 'Destination':'destination', \ |     authorized_fieldnames = { | ||||||
|  |         'route id':'request_id', 'Source':'source', 'Destination':'destination', \ | ||||||
|         'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \ |         'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \ | ||||||
|         'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\ |         'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\ | ||||||
|         'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\ |         'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\ | ||||||
|   | |||||||
							
								
								
									
										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' | ||||||
| @@ -73,35 +73,19 @@ def c(): | |||||||
|     return constants.c |     return constants.c | ||||||
|  |  | ||||||
|  |  | ||||||
| def itufs(spacing, startf=191.35, stopf=196.10): | def arrange_frequencies(length, start, stop): | ||||||
|     """Creates an array of frequencies whose default range is |     """Create an array of frequencies | ||||||
|     191.35-196.10 THz |  | ||||||
|  |  | ||||||
|     :param spacing: Frequency spacing in THz |  | ||||||
|     :param 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 |  | ||||||
|     :rtype: numpy.ndarray |  | ||||||
|     """ |  | ||||||
|     return np.arange(startf, stopf + spacing / 2, spacing) |  | ||||||
|  |  | ||||||
| def itufl(length, startf=191.35, stopf=196.10): |  | ||||||
|     """Creates an array of frequencies whose default range is |  | ||||||
|     191.35-196.10 THz |  | ||||||
|  |  | ||||||
|     :param length: number of elements |     :param length: number of elements | ||||||
|     :param starf: Start frequency in THz |     :param star: Start frequency in THz | ||||||
|     :param stopf: Stop frequency in THz |     :param stop: Stop frequency in THz | ||||||
|     :type length: integer |     :type length: integer | ||||||
|     :type startf: float |     :type start: float | ||||||
|     :type stopf: float |     :type stop: float | ||||||
|     :return an array of frequnecies determined by the spacing parameter |     :return an array of frequencies determined by the spacing parameter | ||||||
|     :rtype: numpy.ndarray |     :rtype: numpy.ndarray | ||||||
|     """ |     """ | ||||||
|     return np.linspace(startf, stopf, length) |     return np.linspace(start, stop, length) | ||||||
|  |  | ||||||
| def h(): | def h(): | ||||||
|     """ |     """ | ||||||
|   | |||||||
| @@ -1,40 +1,62 @@ | |||||||
| { | { | ||||||
|   "paths": [ |   "response": [ | ||||||
|     { |     { | ||||||
|       "path": { |       "response-id": null, | ||||||
|         "path-id": null, |  | ||||||
|       "path-properties": { |       "path-properties": { | ||||||
|         "path-metric": [ |         "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 |             "accumulative-value": null | ||||||
|           } |           } | ||||||
|         ], |         ], | ||||||
|           "path-srlgs": { |  | ||||||
|             "usage": "not used yet", |  | ||||||
|             "values": ["not used yet"] |  | ||||||
|           }, |  | ||||||
|         "path-route-objects": [ |         "path-route-objects": [ | ||||||
|           { |           { | ||||||
|             "path-route-object": { |             "path-route-object": { | ||||||
|                 "index": null, |               "index": 0, | ||||||
|                 "unnumbered-hop": { |               "num-unnum-hop": { | ||||||
|                 "node-id": null, |                 "node-id": null, | ||||||
|                   "link-tp-id": null, |                 "link-tp-id": null | ||||||
|                   "hop-type": null, |               } | ||||||
|                   "direction": "not used" |             } | ||||||
|           }, |           }, | ||||||
|                 "label-hop": { |           { | ||||||
|                   "te-label": { |             "path-route-object": { | ||||||
|                     "generic": "not used yet", |               "index": 1, | ||||||
|                     "direction": "not used yet" |               "transponder": { | ||||||
|  |                 "transponder-type": null, | ||||||
|  |                 "transponder-mode": null | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|               } |           }, | ||||||
|             } |           { | ||||||
|           ] |             "path-route-object": { | ||||||
|  |               "index": 2, | ||||||
|  |               "num-unnum-hop": { | ||||||
|  |                 "node-id": null, | ||||||
|  |                 "link-tp-id": null | ||||||
|               } |               } | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
|       } |       } | ||||||
|  |     },       | ||||||
| @@ -1,7 +1,11 @@ | |||||||
| alabaster>=0.7.12,<1 | alabaster>=0.7.12,<1 | ||||||
|  | docutils==0.15.2 | ||||||
|  | flask==1.0.2 | ||||||
|  | flask-restful==0.3.7 | ||||||
| matplotlib>=3.1.0,<4 | matplotlib>=3.1.0,<4 | ||||||
| networkx>=2.3,<3 | networkx>=2.3,<3 | ||||||
| numpy>=1.16.1,<2 | numpy>=1.16.1,<2 | ||||||
|  | pandas==0.24.2 | ||||||
| Pygments>=2.4.2,<3 | Pygments>=2.4.2,<3 | ||||||
| pytest>=4.0.0,<5 | pytest>=4.0.0,<5 | ||||||
| scipy>=1.3.0,<2 | scipy>=1.3.0,<2 | ||||||
|   | |||||||
| @@ -6,54 +6,71 @@ | |||||||
|       "destination": null, |       "destination": null, | ||||||
|       "src-tp-id": null, |       "src-tp-id": null, | ||||||
|       "dst-tp-id": null, |       "dst-tp-id": null, | ||||||
|  |       "explicit-route-objects": { | ||||||
|  |         "route-object-include-exclude": [ | ||||||
|  |           { | ||||||
|  |             "explicit-route-usage": null, | ||||||
|  |             "index": null, | ||||||
|  |             "num-unnum-hop": { | ||||||
|  |               "node-id": null, | ||||||
|  |               "link-tp-id": null, | ||||||
|  |               "hop-type": null | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "explicit-route-usage": null, | ||||||
|  |             "index": null, | ||||||
|  |             "label-hop": { | ||||||
|  |               "N": null, | ||||||
|  |               "M": null | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "explicit-route-usage": null, | ||||||
|  |             "index": null, | ||||||
|  |             "transponder": { | ||||||
|  |               "transponder-type": null, | ||||||
|  |               "transponder-mode": null | ||||||
|  |             } | ||||||
|  |           }, | ||||||
|  |           { | ||||||
|  |             "explicit-route-usage": null, | ||||||
|  |             "index": null, | ||||||
|  |             "regenerator": { | ||||||
|  |               "regenerator-id": null, | ||||||
|  |               "transponder-type": null, | ||||||
|  |               "transponder-mode": null | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         ] | ||||||
|  |       },       | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
|           "trx_type": null, |           "trx_type": "name of the tsp type_variety as listed in the library", | ||||||
|           "trx_mode": null, |           "trx_mode": "optional, name of the mode as listed in the tsp type_variety", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "n": "null", | ||||||
|               "m": "null" |               "m": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": null, |           "spacing": mandatory decimal Hz, | ||||||
|           "max-nb-of-channel": null, |           "max-nb-of-channel": optional integer, | ||||||
|           "output-power": null, |           "output-power": optional decimal W, | ||||||
|           "path_bandwidth": null |           "path_bandwidth": optional bit/s | ||||||
|         } |         } | ||||||
|       }, |       } | ||||||
|       "optimizations": { |     } | ||||||
|         "explicit-route-include-objects": { |   ], | ||||||
|           "route-object-include-object": [ |   "synchronization": [   list of disjunctions, optional | ||||||
|     { |     { | ||||||
|             "index": null, |       "synchronization-id": "3", | ||||||
|             "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, |  | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "True", |         "relaxable": "True", | ||||||
|         "link-diverse": "False", |         "disjointness": "node link", | ||||||
|         "node-diverse": "False", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|          null ] |          null, null ] | ||||||
|         }, |         }, | ||||||
|     } |     } | ||||||
|   ]   |   ]   | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							| @@ -11,7 +11,7 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f: | |||||||
|  |  | ||||||
| setup( | setup( | ||||||
|     name='gnpy', |     name='gnpy', | ||||||
|     version='1.2.0', |     version='2.1', | ||||||
|     description='route planning and optimization tool for mesh optical networks', |     description='route planning and optimization tool for mesh optical networks', | ||||||
|     long_description=long_description, |     long_description=long_description, | ||||||
|     long_description_content_type='text/x-rst; charset=UTF-8', |     long_description_content_type='text/x-rst; charset=UTF-8', | ||||||
|   | |||||||
| @@ -77,6 +77,9 @@ def compare_networks(expected, actual): | |||||||
| def compare_services(expected, actual): | def compare_services(expected, actual): | ||||||
|     requests = compare(expected['path-request'], actual['path-request'], |     requests = compare(expected['path-request'], actual['path-request'], | ||||||
|                        key=lambda el: el['request-id']) |                        key=lambda el: el['request-id']) | ||||||
|  |     synchronizations = compare(expected['path-request'], expected['path-request'], | ||||||
|  |                                key=lambda el: el['request-id']) | ||||||
|  |     if 'synchronization' in expected.keys(): | ||||||
|         synchronizations = compare(expected['synchronization'], actual['synchronization'], |         synchronizations = compare(expected['synchronization'], actual['synchronization'], | ||||||
|                                    key=lambda el: el['synchronization-id']) |                                    key=lambda el: el['synchronization-id']) | ||||||
|     return ServicesResults(requests, synchronizations) |     return ServicesResults(requests, synchronizations) | ||||||
|   | |||||||
							
								
								
									
										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 | ||||||
| 
 | 
							
								
								
									
										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 | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   ] | ||||||
|  | } | ||||||
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										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. | 
| @@ -2,10 +2,11 @@ | |||||||
|   "path-request": [ |   "path-request": [ | ||||||
|     { |     { | ||||||
|       "request-id": "0", |       "request-id": "0", | ||||||
|       "source": "Lorient_KMA", |       "source": "trx Lorient_KMA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "trx Vannes_KBE", | ||||||
|       "src-tp-id": "trx Lorient_KMA", |       "src-tp-id": "trx Lorient_KMA", | ||||||
|       "dst-tp-id": "trx Vannes_KBE", |       "dst-tp-id": "trx Vannes_KBE", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -13,8 +14,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -22,17 +23,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 100000000000.0 |           "path_bandwidth": 100000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "1", |       "request-id": "1", | ||||||
|       "source": "Brest_KLA", |       "source": "trx Brest_KLA", | ||||||
|       "destination": "Vannes_KBE", |       "destination": "trx Vannes_KBE", | ||||||
|       "src-tp-id": "trx Brest_KLA", |       "src-tp-id": "trx Brest_KLA", | ||||||
|       "dst-tp-id": "trx Vannes_KBE", |       "dst-tp-id": "trx Vannes_KBE", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -40,76 +39,52 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
|           "max-nb-of-channel": null, |           "max-nb-of-channel": null, | ||||||
|           "output-power": 0.0012589254117941673, |           "output-power": 0.0012589254117941673, | ||||||
|           "path_bandwidth": 0 |           "path_bandwidth": 10000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Brest_KLA", |               "node-id": "roadm Brest_KLA", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 1, |             "index": 1, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Lannion_CAS", |               "node-id": "roadm Lannion_CAS", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 2, |             "index": 2, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Lorient_KMA", |               "node-id": "roadm Lorient_KMA", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 3, |             "index": 3, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm Vannes_KBE", |               "node-id": "roadm Vannes_KBE", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -117,10 +92,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "3", |       "request-id": "3", | ||||||
|       "source": "Lannion_CAS", |       "source": "trx Lannion_CAS", | ||||||
|       "destination": "Rennes_STA", |       "destination": "trx Rennes_STA", | ||||||
|       "src-tp-id": "trx Lannion_CAS", |       "src-tp-id": "trx Lannion_CAS", | ||||||
|       "dst-tp-id": "trx Rennes_STA", |       "dst-tp-id": "trx Rennes_STA", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -128,8 +104,8 @@ | |||||||
|           "trx_mode": "mode 1", |           "trx_mode": "mode 1", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 50000000000.0, |           "spacing": 50000000000.0, | ||||||
| @@ -137,17 +113,15 @@ | |||||||
|           "output-power": 0.0012589254117941673, |           "output-power": 0.0012589254117941673, | ||||||
|           "path_bandwidth": 60000000000.0 |           "path_bandwidth": 60000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "4", |       "request-id": "4", | ||||||
|       "source": "Rennes_STA", |       "source": "trx Rennes_STA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "trx Lannion_CAS", | ||||||
|       "src-tp-id": "trx Rennes_STA", |       "src-tp-id": "trx Rennes_STA", | ||||||
|       "dst-tp-id": "trx Lannion_CAS", |       "dst-tp-id": "trx Lannion_CAS", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -155,8 +129,8 @@ | |||||||
|           "trx_mode": "mode 2", |           "trx_mode": "mode 2", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 75000000000.0, |           "spacing": 75000000000.0, | ||||||
| @@ -164,17 +138,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 150000000000.0 |           "path_bandwidth": 150000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "5", |       "request-id": "5", | ||||||
|       "source": "Rennes_STA", |       "source": "trx Rennes_STA", | ||||||
|       "destination": "Lannion_CAS", |       "destination": "trx Lannion_CAS", | ||||||
|       "src-tp-id": "trx Rennes_STA", |       "src-tp-id": "trx Rennes_STA", | ||||||
|       "dst-tp-id": "trx Lannion_CAS", |       "dst-tp-id": "trx Lannion_CAS", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -182,8 +154,8 @@ | |||||||
|           "trx_mode": "mode 2", |           "trx_mode": "mode 2", | ||||||
|           "effective-freq-slot": [ |           "effective-freq-slot": [ | ||||||
|             { |             { | ||||||
|               "n": "null", |               "N": "null", | ||||||
|               "m": "null" |               "M": "null" | ||||||
|             } |             } | ||||||
|           ], |           ], | ||||||
|           "spacing": 75000000000.0, |           "spacing": 75000000000.0, | ||||||
| @@ -191,9 +163,31 @@ | |||||||
|           "output-power": 0.0019952623149688794, |           "output-power": 0.0019952623149688794, | ||||||
|           "path_bandwidth": 20000000000.0 |           "path_bandwidth": 20000000000.0 | ||||||
|         } |         } | ||||||
|  |       } | ||||||
|     }, |     }, | ||||||
|       "optimizations": { |     { | ||||||
|         "explicit-route-include-objects": [] |       "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 | ||||||
|  |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
| @@ -201,9 +195,8 @@ | |||||||
|     { |     { | ||||||
|       "synchronization-id": "3", |       "synchronization-id": "3", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "false", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "3", |           "3", | ||||||
|           "1" |           "1" | ||||||
| @@ -213,9 +206,8 @@ | |||||||
|     { |     { | ||||||
|       "synchronization-id": "4", |       "synchronization-id": "4", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "false", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "4", |           "4", | ||||||
|           "5" |           "5" | ||||||
|   | |||||||
| @@ -2,10 +2,11 @@ | |||||||
|   "path-request": [ |   "path-request": [ | ||||||
|     { |     { | ||||||
|       "request-id": "1", |       "request-id": "1", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "g", |       "destination": "trx g", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx g", |       "dst-tp-id": "trx g", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -22,17 +23,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "2a", |       "request-id": "2a", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "h", |       "destination": "trx h", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx h", |       "dst-tp-id": "trx h", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -49,17 +48,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "3", |       "request-id": "3", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -76,17 +73,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "ee", |       "request-id": "ee", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -104,36 +99,23 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm e", |               "node-id": "roadm e", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "hop-type": "LOOSE" | ||||||
|               "hop-type": "loose", |  | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 1, |             "index": 1, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm g", |               "node-id": "roadm g", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -141,10 +123,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "ff", |       "request-id": "ff", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -161,17 +144,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "10", |       "request-id": "10", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "g", |       "destination": "trx g", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx g", |       "dst-tp-id": "trx g", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -188,17 +169,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "11", |       "request-id": "11", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "h", |       "destination": "trx h", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx h", |       "dst-tp-id": "trx h", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -216,21 +195,15 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "bb", |               "node-id": "bb", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -238,10 +211,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "12", |       "request-id": "12", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -259,21 +233,15 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "trx b", |               "node-id": "trx b", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -281,10 +249,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "13", |       "request-id": "13", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -301,17 +270,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "14", |       "request-id": "14", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -329,36 +296,23 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm e", |               "node-id": "roadm e", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "hop-type": "LOOSE" | ||||||
|               "hop-type": "loose", |  | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 1, |             "index": 1, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm g", |               "node-id": "roadm g", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -366,10 +320,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "e:1# /", |       "request-id": "e:1# /", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "g", |       "destination": "trx g", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx g", |       "dst-tp-id": "trx g", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -386,17 +341,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "b-2a", |       "request-id": "b-2a", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "h", |       "destination": "trx h", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx h", |       "dst-tp-id": "trx h", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -413,17 +366,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "3a;?", |       "request-id": "3a;?", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -440,17 +391,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "ee-s", |       "request-id": "ee-s", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -468,36 +417,23 @@ | |||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
|       "optimizations": { |       "explicit-route-objects": { | ||||||
|         "explicit-route-include-objects": [ |         "route-object-include-exclude": [ | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 0, |             "index": 0, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm e", |               "node-id": "roadm e", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "hop-type": "LOOSE" | ||||||
|               "hop-type": "loose", |  | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           }, |           }, | ||||||
|           { |           { | ||||||
|  |             "explicit-route-usage": "route-include-ero", | ||||||
|             "index": 1, |             "index": 1, | ||||||
|             "unnumbered-hop": { |             "num-unnum-hop": { | ||||||
|               "node-id": "roadm g", |               "node-id": "roadm g", | ||||||
|               "link-tp-id": "link-tp-id is not used", |               "link-tp-id": "link-tp-id is not used", | ||||||
|               "hop-type": "loose", |               "hop-type": "LOOSE" | ||||||
|               "direction": "direction is not used" |  | ||||||
|             }, |  | ||||||
|             "label-hop": { |  | ||||||
|               "te-label": { |  | ||||||
|                 "generic": "generic is not used", |  | ||||||
|                 "direction": "direction is not used" |  | ||||||
|               } |  | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         ] |         ] | ||||||
| @@ -505,10 +441,11 @@ | |||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "ff-b", |       "request-id": "ff-b", | ||||||
|       "source": "c", |       "source": "trx c", | ||||||
|       "destination": "f", |       "destination": "trx f", | ||||||
|       "src-tp-id": "trx c", |       "src-tp-id": "trx c", | ||||||
|       "dst-tp-id": "trx f", |       "dst-tp-id": "trx f", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -525,17 +462,15 @@ | |||||||
|           "output-power": 0.001, |           "output-power": 0.001, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "10-z", |       "request-id": "10-z", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "g", |       "destination": "trx g", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx g", |       "dst-tp-id": "trx g", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -552,17 +487,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "11 g", |       "request-id": "11 g", | ||||||
|       "source": "a", |       "source": "trx a", | ||||||
|       "destination": "h", |       "destination": "trx h", | ||||||
|       "src-tp-id": "trx a", |       "src-tp-id": "trx a", | ||||||
|       "dst-tp-id": "trx h", |       "dst-tp-id": "trx h", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -579,17 +512,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": 300000000000.0 |           "path_bandwidth": 300000000000.0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "12<", |       "request-id": "12<", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -606,17 +537,15 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": null |           "path_bandwidth": null | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     { |     { | ||||||
|       "request-id": "12>", |       "request-id": "12>", | ||||||
|       "source": "f", |       "source": "trx f", | ||||||
|       "destination": "b", |       "destination": "trx b", | ||||||
|       "src-tp-id": "trx f", |       "src-tp-id": "trx f", | ||||||
|       "dst-tp-id": "trx b", |       "dst-tp-id": "trx b", | ||||||
|  |       "bidirectional": false, | ||||||
|       "path-constraints": { |       "path-constraints": { | ||||||
|         "te-bandwidth": { |         "te-bandwidth": { | ||||||
|           "technology": "flexi-grid", |           "technology": "flexi-grid", | ||||||
| @@ -633,9 +562,6 @@ | |||||||
|           "output-power": null, |           "output-power": null, | ||||||
|           "path_bandwidth": null |           "path_bandwidth": null | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       "optimizations": { |  | ||||||
|         "explicit-route-include-objects": [] |  | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
| @@ -644,8 +570,7 @@ | |||||||
|       "synchronization-id": "1", |       "synchronization-id": "1", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "1", |           "1", | ||||||
|           "2a" |           "2a" | ||||||
| @@ -656,8 +581,7 @@ | |||||||
|       "synchronization-id": "3", |       "synchronization-id": "3", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "3", |           "3", | ||||||
|           "1" |           "1" | ||||||
| @@ -668,8 +592,7 @@ | |||||||
|       "synchronization-id": "ff", |       "synchronization-id": "ff", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "ff", |           "ff", | ||||||
|           "13" |           "13" | ||||||
| @@ -680,8 +603,7 @@ | |||||||
|       "synchronization-id": "13", |       "synchronization-id": "13", | ||||||
|       "svec": { |       "svec": { | ||||||
|         "relaxable": "False", |         "relaxable": "False", | ||||||
|         "link-diverse": "True", |         "disjointness": "node link", | ||||||
|         "node-diverse": "True", |  | ||||||
|         "request-id-number": [ |         "request-id-number": [ | ||||||
|           "13", |           "13", | ||||||
|           "14" |           "14" | ||||||
|   | |||||||
| @@ -18,10 +18,8 @@ from pathlib import Path | |||||||
| import pytest | import pytest | ||||||
| from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch | ||||||
| from gnpy.core.network import load_network, build_network | from gnpy.core.network import load_network, build_network | ||||||
| from examples.path_requests_run import (requests_from_json , correct_route_list , | from examples.path_requests_run import requests_from_json, correct_route_list, load_requests | ||||||
|                                         load_requests , disjunctions_from_json) | from gnpy.core.request import compute_path_dsjctn, propagate, propagate_and_optimize_mode | ||||||
| from gnpy.core.request import (compute_path_dsjctn, isdisjoint , find_reversed_path, |  | ||||||
|                                propagate,propagate_and_optimize_mode)  |  | ||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
| from gnpy.core.elements import Roadm | from gnpy.core.elements import Roadm | ||||||
|  |  | ||||||
| @@ -35,7 +33,7 @@ eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' | |||||||
| @pytest.mark.parametrize("serv",[service_file_name]) | @pytest.mark.parametrize("serv",[service_file_name]) | ||||||
| @pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 2', 'mode 1', 'mode 2', 'nok']]) | @pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 2', 'mode 1', 'mode 2', 'nok']]) | ||||||
| def test_automaticmodefeature(net,eqpt,serv,expected_mode): | def test_automaticmodefeature(net,eqpt,serv,expected_mode): | ||||||
|     data = load_requests(serv,eqpt) |     data = load_requests(serv, eqpt, bidir=False) | ||||||
|     equipment = load_equipment(eqpt) |     equipment = load_equipment(eqpt) | ||||||
|     network = load_network(net,equipment) |     network = load_network(net,equipment) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -19,6 +19,7 @@ from examples.path_requests_run import (requests_from_json , correct_route_list | |||||||
| from gnpy.core.request import compute_path_dsjctn, isdisjoint , find_reversed_path | from gnpy.core.request import compute_path_dsjctn, isdisjoint , find_reversed_path | ||||||
| from gnpy.core.utils import db2lin, lin2db | from gnpy.core.utils import db2lin, lin2db | ||||||
| from gnpy.core.elements import Roadm | from gnpy.core.elements import Roadm | ||||||
|  | from gnpy.core.spectrum_assignment import build_oms_list | ||||||
|  |  | ||||||
| network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.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' | service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json' | ||||||
| @@ -29,10 +30,9 @@ eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' | |||||||
| @pytest.mark.parametrize("eqpt", [eqpt_library_name]) | @pytest.mark.parametrize("eqpt", [eqpt_library_name]) | ||||||
| @pytest.mark.parametrize("serv",[service_file_name]) | @pytest.mark.parametrize("serv",[service_file_name]) | ||||||
| def test_disjunction(net,eqpt,serv): | def test_disjunction(net,eqpt,serv): | ||||||
|     data = load_requests(serv,eqpt) |     data = load_requests(serv, eqpt, bidir=False) | ||||||
|     equipment = load_equipment(eqpt) |     equipment = load_equipment(eqpt) | ||||||
|     network = load_network(net,equipment) |     network = load_network(net,equipment) | ||||||
|  |  | ||||||
|     # Build the network once using the default power defined in SI in eqpt config |     # Build the network once using the default power defined in SI in eqpt config | ||||||
|     # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by |     # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by | ||||||
|     # spacing, f_min and f_max  |     # spacing, f_min and f_max  | ||||||
| @@ -41,6 +41,7 @@ def test_disjunction(net,eqpt,serv): | |||||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|     build_network(network, equipment, p_db, p_total_db) |     build_network(network, equipment, p_db, p_total_db) | ||||||
|  |     build_oms_list(network, equipment) | ||||||
|  |  | ||||||
|     rqs = requests_from_json(data, equipment) |     rqs = requests_from_json(data, equipment) | ||||||
|     rqs = correct_route_list(network, rqs) |     rqs = correct_route_list(network, rqs) | ||||||
| @@ -56,7 +57,7 @@ def test_disjunction(net,eqpt,serv): | |||||||
|         rqs_id_list = [r.request_id for r in rqs] |         rqs_id_list = [r.request_id for r in rqs] | ||||||
|         p1 = pths[rqs_id_list.index(e[0])][1:-1] |         p1 = pths[rqs_id_list.index(e[0])][1:-1] | ||||||
|         p2 = pths[rqs_id_list.index(e[1])][1:-1] |         p2 = pths[rqs_id_list.index(e[1])][1:-1] | ||||||
|         if isdisjoint(p1,p2) + isdisjoint(p1,find_reversed_path(p2, network)) > 0: |         if isdisjoint(p1, p2) + isdisjoint(p1, find_reversed_path(p2)) > 0: | ||||||
|             test = False |             test = False | ||||||
|             print(f'Computed path (roadms):{[e.uid for e in p1  if isinstance(e, Roadm)]}\n') |             print(f'Computed path (roadms):{[e.uid for e in p1  if isinstance(e, Roadm)]}\n') | ||||||
|             print(f'Computed path (roadms):{[e.uid for e in p2  if isinstance(e, Roadm)]}\n') |             print(f'Computed path (roadms):{[e.uid for e in p2  if isinstance(e, Roadm)]}\n') | ||||||
| @@ -68,7 +69,7 @@ def test_disjunction(net,eqpt,serv): | |||||||
| @pytest.mark.parametrize("eqpt", [eqpt_library_name]) | @pytest.mark.parametrize("eqpt", [eqpt_library_name]) | ||||||
| @pytest.mark.parametrize("serv",[service_file_name]) | @pytest.mark.parametrize("serv",[service_file_name]) | ||||||
| def test_does_not_loop_back(net,eqpt,serv): | def test_does_not_loop_back(net,eqpt,serv): | ||||||
|     data = load_requests(serv,eqpt) |     data = load_requests(serv, eqpt, bidir=False) | ||||||
|     equipment = load_equipment(eqpt) |     equipment = load_equipment(eqpt) | ||||||
|     network = load_network(net,equipment) |     network = load_network(net,equipment) | ||||||
|  |  | ||||||
| @@ -80,6 +81,7 @@ def test_does_not_loop_back(net,eqpt,serv): | |||||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|     build_network(network, equipment, p_db, p_total_db) |     build_network(network, equipment, p_db, p_total_db) | ||||||
|  |     build_oms_list(network, equipment) | ||||||
|  |  | ||||||
|     rqs = requests_from_json(data, equipment) |     rqs = requests_from_json(data, equipment) | ||||||
|     rqs = correct_route_list(network, rqs) |     rqs = correct_route_list(network, rqs) | ||||||
|   | |||||||
| @@ -3,38 +3,51 @@ | |||||||
| # @Author: Esther Le Rouzic | # @Author: Esther Le Rouzic | ||||||
| # @Date:   2018-06-15 | # @Date:   2018-06-15 | ||||||
|  |  | ||||||
| from gnpy.core.elements import Edfa | """ Adding tests to check the parser non regression | ||||||
| import numpy as np |     convention of naming of test files: | ||||||
|  |     - ..._expected.json for the reference output | ||||||
|  |     tests: | ||||||
|  |     - generation of topology json | ||||||
|  |     - reading of Eqpt sheet w and W/ power mode | ||||||
|  |     - consistency of autodesign | ||||||
|  |     - generation of service list based on service sheet | ||||||
|  |     - writing of results in csv | ||||||
|  |     - writing of results in json (same keys) | ||||||
|  | """ | ||||||
|  |  | ||||||
| from json import load | from json import load | ||||||
|  | from pathlib import Path | ||||||
|  | from os import unlink | ||||||
|  | from pandas import read_csv | ||||||
| import pytest | import pytest | ||||||
| from gnpy.core import network_from_json |  | ||||||
| from gnpy.core.elements import Transceiver, Fiber, Edfa |  | ||||||
| from gnpy.core.utils import lin2db, db2lin |  | ||||||
| from gnpy.core.info import SpectralInformation, Channel, Power |  | ||||||
| from gnpy.core.network import save_network, build_network |  | ||||||
| from tests.compare import compare_networks, compare_services | from tests.compare import compare_networks, compare_services | ||||||
|  | from copy import deepcopy | ||||||
|  | from gnpy.core.utils import lin2db | ||||||
|  | from gnpy.core.network import save_network, build_network | ||||||
| from gnpy.core.convert import convert_file | from gnpy.core.convert import convert_file | ||||||
| from gnpy.core.service_sheet import convert_service_sheet | from gnpy.core.service_sheet import convert_service_sheet | ||||||
| from gnpy.core.equipment import load_equipment, automatic_nch | from gnpy.core.equipment import load_equipment, automatic_nch | ||||||
| from gnpy.core.network import load_network | from gnpy.core.network import load_network | ||||||
| from pathlib import Path | from gnpy.core.request import (jsontocsv, requests_aggregation, | ||||||
| import filecmp |                                compute_path_dsjctn, Result_element) | ||||||
| from os import unlink | from gnpy.core.spectrum_assignment import build_oms_list, pth_assign_spectrum | ||||||
|  | from gnpy.core.exceptions import ServiceError | ||||||
|  | from examples.path_requests_run import (requests_from_json, disjunctions_from_json, | ||||||
|  |                                         correct_route_list, correct_disjn, | ||||||
|  |                                         compute_path_with_disjunction) | ||||||
|  |  | ||||||
| TEST_DIR = Path(__file__).parent | TEST_DIR = Path(__file__).parent | ||||||
| DATA_DIR = TEST_DIR / 'data' | DATA_DIR = TEST_DIR / 'data' | ||||||
| eqpt_filename = DATA_DIR / 'eqpt_config.json' | eqpt_filename = DATA_DIR / 'eqpt_config.json' | ||||||
|  |  | ||||||
| # adding tests to check the parser non regression |  | ||||||
| # convention of naming of test files: |  | ||||||
| # |  | ||||||
| #    - ..._expected.json for the reference output |  | ||||||
|  |  | ||||||
| @pytest.mark.parametrize('xls_input,expected_json_output', { | @pytest.mark.parametrize('xls_input,expected_json_output', { | ||||||
|     DATA_DIR / 'CORONET_Global_Topology.xls':   DATA_DIR / 'CORONET_Global_Topology_expected.json', |     DATA_DIR / 'CORONET_Global_Topology.xls':   DATA_DIR / 'CORONET_Global_Topology_expected.json', | ||||||
|     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_expected.json', |     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_expected.json', | ||||||
|     }.items()) |     }.items()) | ||||||
| def test_excel_json_generation(xls_input, expected_json_output): | def test_excel_json_generation(xls_input, expected_json_output): | ||||||
|  |     """ tests generation of topology json | ||||||
|  |     """ | ||||||
|     convert_file(xls_input) |     convert_file(xls_input) | ||||||
|  |  | ||||||
|     actual_json_output = xls_input.with_suffix('.json') |     actual_json_output = xls_input.with_suffix('.json') | ||||||
| @@ -55,20 +68,25 @@ def test_excel_json_generation(xls_input, expected_json_output): | |||||||
|  |  | ||||||
| # assume xls entries | # assume xls entries | ||||||
| # test that the build network gives correct results in gain mode | # test that the build network gives correct results in gain mode | ||||||
| #  |  | ||||||
| @pytest.mark.parametrize('xls_input,expected_json_output', { | @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 / 'CORONET_Global_Topology.xls':\ | ||||||
|     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_auto_design_expected.json', |                           DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json', | ||||||
|  |                           DATA_DIR / 'testTopology.xls':\ | ||||||
|  |                           DATA_DIR / 'testTopology_auto_design_expected.json', | ||||||
|                          }.items()) |                          }.items()) | ||||||
| def test_auto_design_generation_fromxlsgainmode(xls_input, expected_json_output): | 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) |     equipment = load_equipment(eqpt_filename) | ||||||
|     network = load_network(xls_input, equipment) |     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) |     # 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 |     equipment['Span']['default'].power_mode = False | ||||||
|     # Build the network once using the default power defined in SI in eqpt config |     # Build the network once using the default power defined in SI in eqpt config | ||||||
|  |  | ||||||
|     p_db = equipment['SI']['default'].power_dbm |     p_db = equipment['SI']['default'].power_dbm | ||||||
|      |  | ||||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|     build_network(network, equipment, p_db, p_total_db) |     build_network(network, equipment, p_db, p_total_db) | ||||||
| @@ -92,19 +110,23 @@ def test_auto_design_generation_fromxlsgainmode(xls_input, expected_json_output) | |||||||
|     assert not results.connections.different |     assert not results.connections.different | ||||||
|  |  | ||||||
| #test that autodesign creates same file as an input file already autodesigned | #test that autodesign creates same file as an input file already autodesigned | ||||||
| @pytest.mark.parametrize('json_input,expected_json_output', { | @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 / 'CORONET_Global_Topology_auto_design_expected.json':\ | ||||||
|     DATA_DIR / 'testTopology_auto_design_expected.json':     DATA_DIR / 'testTopology_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()) |                          }.items()) | ||||||
| def test_auto_design_generation_fromjson(json_input, expected_json_output): | 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) |     equipment = load_equipment(eqpt_filename) | ||||||
|     network = load_network(json_input, equipment) |     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) |     # 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 |     equipment['Span']['default'].power_mode = False | ||||||
|     # Build the network once using the default power defined in SI in eqpt config |     # Build the network once using the default power defined in SI in eqpt config | ||||||
|  |  | ||||||
|     p_db = equipment['SI']['default'].power_dbm |     p_db = equipment['SI']['default'].power_dbm | ||||||
|      |  | ||||||
|     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|     build_network(network, equipment, p_db, p_total_db) |     build_network(network, equipment, p_db, p_total_db) | ||||||
| @@ -128,11 +150,13 @@ def test_auto_design_generation_fromjson(json_input, expected_json_output): | |||||||
|     assert not results.connections.different |     assert not results.connections.different | ||||||
|  |  | ||||||
| # test services creation | # test services creation | ||||||
|  |  | ||||||
| @pytest.mark.parametrize('xls_input,expected_json_output', { | @pytest.mark.parametrize('xls_input,expected_json_output', { | ||||||
|     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_services_expected.json', |     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_services_expected.json', | ||||||
|  |     DATA_DIR / 'testService.xls':     DATA_DIR / 'testService_services_expected.json' | ||||||
|     }.items()) |     }.items()) | ||||||
| def test_excel_service_json_generation(xls_input, expected_json_output): | def test_excel_service_json_generation(xls_input, expected_json_output): | ||||||
|  |     """ test services creation | ||||||
|  |     """ | ||||||
|     convert_service_sheet(xls_input, eqpt_filename) |     convert_service_sheet(xls_input, eqpt_filename) | ||||||
|  |  | ||||||
|     actual_json_output = f'{str(xls_input)[:-4]}_services.json' |     actual_json_output = f'{str(xls_input)[:-4]}_services.json' | ||||||
| @@ -150,3 +174,173 @@ def test_excel_service_json_generation(xls_input, expected_json_output): | |||||||
|     assert not results.synchronizations.missing |     assert not results.synchronizations.missing | ||||||
|     assert not results.synchronizations.extra |     assert not results.synchronizations.extra | ||||||
|     assert not results.synchronizations.different |     assert not results.synchronizations.different | ||||||
|  |  | ||||||
|  |     # TODO verify that requested bandwidth is not zero ! | ||||||
|  |  | ||||||
|  | # test xls answers creation | ||||||
|  | @pytest.mark.parametrize('json_input, csv_output', { | ||||||
|  |     DATA_DIR / 'testTopology_response.json':     DATA_DIR / 'testTopology_response', | ||||||
|  | }.items()) | ||||||
|  | def test_csv_response_generation(json_input, csv_output): | ||||||
|  |     """ tests if generated csv is consistant with expected generation | ||||||
|  |         same columns (order not important) | ||||||
|  |     """ | ||||||
|  |     with open(json_input) as jsonfile: | ||||||
|  |         json_data = load(jsonfile) | ||||||
|  |     equipment = load_equipment(eqpt_filename) | ||||||
|  |     csv_filename = str(csv_output)+'.csv' | ||||||
|  |     with open(csv_filename, 'w', encoding='utf-8') as fcsv: | ||||||
|  |         jsontocsv(json_data, equipment, fcsv) | ||||||
|  |  | ||||||
|  |     expected_csv_filename = str(csv_output)+'_expected.csv' | ||||||
|  |  | ||||||
|  |     # expected header | ||||||
|  |     # csv_header = \ | ||||||
|  |     # [ | ||||||
|  |     #  'response-id', | ||||||
|  |     #  'source', | ||||||
|  |     #  'destination', | ||||||
|  |     #  'path_bandwidth', | ||||||
|  |     #  'Pass?', | ||||||
|  |     #  'nb of tsp pairs', | ||||||
|  |     #  'total cost', | ||||||
|  |     #  'transponder-type', | ||||||
|  |     #  'transponder-mode', | ||||||
|  |     #  'OSNR-0.1nm', | ||||||
|  |     #  'SNR-0.1nm', | ||||||
|  |     #  'SNR-bandwidth', | ||||||
|  |     #  'baud rate (Gbaud)', | ||||||
|  |     #  'input power (dBm)', | ||||||
|  |     #  'path', | ||||||
|  |     #  'spectrum (N,M)', | ||||||
|  |     #  'reversed path OSNR-0.1nm', | ||||||
|  |     #  'reversed path SNR-0.1nm', | ||||||
|  |     #  'reversed path SNR-bandwidth' | ||||||
|  |     # ] | ||||||
|  |  | ||||||
|  |     resp = read_csv(csv_filename) | ||||||
|  |     print(resp) | ||||||
|  |     unlink(csv_filename) | ||||||
|  |     expected_resp = read_csv(expected_csv_filename) | ||||||
|  |     print(expected_resp) | ||||||
|  |     resp_header = list(resp.head(0)) | ||||||
|  |     expected_resp_header = list(expected_resp.head(0)) | ||||||
|  |     # check that headers are the same | ||||||
|  |     resp_header.sort() | ||||||
|  |     expected_resp_header.sort() | ||||||
|  |     print('headers are differents') | ||||||
|  |     print(resp_header) | ||||||
|  |     print(expected_resp_header) | ||||||
|  |     assert resp_header == expected_resp_header | ||||||
|  |  | ||||||
|  |     # for each header checks that the output are as expected | ||||||
|  |     resp.sort_values(by=['response-id']) | ||||||
|  |     expected_resp.sort_values(by=['response-id']) | ||||||
|  |  | ||||||
|  |     for column in expected_resp: | ||||||
|  |         assert list(resp[column].fillna('')) == list(expected_resp[column].fillna('')) | ||||||
|  |         print('results are different') | ||||||
|  |         print(list(resp[column])) | ||||||
|  |         print(list(expected_resp[column])) | ||||||
|  |         print(type(list(resp[column])[-1])) | ||||||
|  |  | ||||||
|  | def compare_response(exp_resp, act_resp): | ||||||
|  |     """ False if the keys are different in the nested dicts as well | ||||||
|  |     """ | ||||||
|  |     print(exp_resp) | ||||||
|  |     print(act_resp) | ||||||
|  |     test = True | ||||||
|  |     for key in act_resp.keys(): | ||||||
|  |         if not key in exp_resp.keys(): | ||||||
|  |             print(f'{key} is not expected') | ||||||
|  |             return False | ||||||
|  |         if isinstance(act_resp[key], dict): | ||||||
|  |             test = compare_response(exp_resp[key], act_resp[key]) | ||||||
|  |     if test: | ||||||
|  |         for key in exp_resp.keys(): | ||||||
|  |             if not key in act_resp.keys(): | ||||||
|  |                 print(f'{key} is expected') | ||||||
|  |                 return False | ||||||
|  |             if isinstance(exp_resp[key], dict): | ||||||
|  |                 test = compare_response(exp_resp[key], act_resp[key]) | ||||||
|  |  | ||||||
|  |     # at this point exp_resp and act_resp have the same keys. Check if their values are the same | ||||||
|  |     for key in act_resp.keys(): | ||||||
|  |         if not isinstance(act_resp[key], dict): | ||||||
|  |             if exp_resp[key] != act_resp[key]: | ||||||
|  |                 print(f'expected value :{exp_resp[key]}\n actual value: {act_resp[key]}') | ||||||
|  |                 return False | ||||||
|  |     return test | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # test json answers creation | ||||||
|  | @pytest.mark.parametrize('xls_input, expected_response_file', { | ||||||
|  |     DATA_DIR / 'testTopology.xls':     DATA_DIR / 'testTopology_response.json', | ||||||
|  | }.items()) | ||||||
|  | def test_json_response_generation(xls_input, expected_response_file): | ||||||
|  |     """ tests if json response is correctly generated for all combinations of requests | ||||||
|  |     """ | ||||||
|  |     data = convert_service_sheet(xls_input, eqpt_filename) | ||||||
|  |     # change one of the request with bidir option to cover bidir case as well | ||||||
|  |     data['path-request'][2]['bidirectional'] = True | ||||||
|  |  | ||||||
|  |     equipment = load_equipment(eqpt_filename) | ||||||
|  |     network = load_network(xls_input, equipment) | ||||||
|  |     p_db = equipment['SI']['default'].power_dbm | ||||||
|  |  | ||||||
|  |     p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\ | ||||||
|  |         equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) | ||||||
|  |     build_network(network, equipment, p_db, p_total_db) | ||||||
|  |     oms_list = build_oms_list(network, equipment) | ||||||
|  |     rqs = requests_from_json(data, equipment) | ||||||
|  |     rqs = correct_route_list(network, rqs) | ||||||
|  |     dsjn = disjunctions_from_json(data) | ||||||
|  |     dsjn = correct_disjn(dsjn) | ||||||
|  |     rqs, dsjn = requests_aggregation(rqs, dsjn) | ||||||
|  |     pths = compute_path_dsjctn(network, equipment, rqs, dsjn) | ||||||
|  |     propagatedpths, reversed_pths, reversed_propagatedpths = \ | ||||||
|  |         compute_path_with_disjunction(network, equipment, rqs, pths) | ||||||
|  |     pth_assign_spectrum(pths, rqs, oms_list, reversed_pths) | ||||||
|  |  | ||||||
|  |     result = [] | ||||||
|  |     for i, pth in enumerate(propagatedpths): | ||||||
|  |         # test ServiceError handling : when M is zero at this point, the | ||||||
|  |         # json result should not be created if there is no blocking reason | ||||||
|  |         if i == 1: | ||||||
|  |             my_rq = deepcopy(rqs[i]) | ||||||
|  |             my_rq.M = 0 | ||||||
|  |             with pytest.raises(ServiceError): | ||||||
|  |                 Result_element(my_rq, pth, reversed_propagatedpths[i]).json | ||||||
|  |  | ||||||
|  |             my_rq.blocking_reason = 'NO_SPECTRUM' | ||||||
|  |             Result_element(my_rq, pth, reversed_propagatedpths[i]).json | ||||||
|  |  | ||||||
|  |         result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i])) | ||||||
|  |  | ||||||
|  |     temp = { | ||||||
|  |         'response': [n.json for n in result] | ||||||
|  |     } | ||||||
|  |     # load expected result and compare keys and values | ||||||
|  |  | ||||||
|  |     with open(expected_response_file) as jsonfile: | ||||||
|  |         expected = load(jsonfile) | ||||||
|  |         # since we changes bidir attribute of request#2, need to add the corresponding | ||||||
|  |         # metric in response | ||||||
|  |  | ||||||
|  |     for i, response in enumerate(temp['response']): | ||||||
|  |         if i == 2: | ||||||
|  |             # compare response must be False because z-a metric is missing | ||||||
|  |             # (request with bidir option to cover bidir case) | ||||||
|  |             assert not compare_response(expected['response'][i], response) | ||||||
|  |             print(f'response {response["response-id"]} should not match') | ||||||
|  |             expected['response'][2]['path-properties']['z-a-path-metric'] = [ | ||||||
|  |                 {'metric-type': 'SNR-bandwidth', 'accumulative-value': 22.809999999999999}, | ||||||
|  |                 {'metric-type': 'SNR-0.1nm', 'accumulative-value': 26.890000000000001}, | ||||||
|  |                 {'metric-type': 'OSNR-bandwidth', 'accumulative-value': 26.239999999999998}, | ||||||
|  |                 {'metric-type': 'OSNR-0.1nm', 'accumulative-value': 30.32}, | ||||||
|  |                 {'metric-type': 'reference_power', 'accumulative-value': 0.0012589254117941673}, | ||||||
|  |                 {'metric-type': 'path_bandwidth', 'accumulative-value': 60000000000.0}] | ||||||
|  |             # test should be OK now | ||||||
|  |         else: | ||||||
|  |             assert compare_response(expected['response'][i], response) | ||||||
|  |             print(f'response {response["response-id"]} is not correct') | ||||||
|   | |||||||
							
								
								
									
										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