mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-10-30 09:42:22 +00:00
Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40bbb9b553 | ||
|
|
c5a29d7b81 | ||
|
|
2fa0dac2f1 | ||
|
|
02916691c7 | ||
|
|
5d3526a74c | ||
|
|
459a82150b | ||
|
|
f67e9c4914 | ||
|
|
0c2bf58080 | ||
|
|
396020eeb5 | ||
|
|
84fd574df9 | ||
|
|
e97d20c3fe | ||
|
|
f52ff92918 | ||
|
|
4707abd2bd | ||
|
|
5b824a7a2c | ||
|
|
3784627bc9 | ||
|
|
1b265a3d27 | ||
|
|
927d73c720 | ||
|
|
1cf90a019b | ||
|
|
e2fac13219 | ||
|
|
be5519455f | ||
|
|
f98eb2c10c | ||
|
|
60b9256f22 | ||
|
|
94b9c16d67 | ||
|
|
eaccd63739 | ||
|
|
8fcb61f12c | ||
|
|
8e62955bb0 | ||
|
|
561fd76a20 | ||
|
|
ccb6653f50 | ||
|
|
3a31d458ee | ||
|
|
22d55ae881 | ||
|
|
3324645f78 | ||
|
|
3014a881f5 | ||
|
|
35877022ec | ||
|
|
9b985d1fc5 | ||
|
|
cd95c83bbf | ||
|
|
f9dbf7d132 | ||
|
|
d2a8d8e887 | ||
|
|
35c4073292 | ||
|
|
120c326e77 | ||
|
|
01115f9852 | ||
|
|
b91ea1828f | ||
|
|
d2a294ac5a | ||
|
|
f41acf31f6 | ||
|
|
8cef09158f | ||
|
|
918c19b1bc | ||
|
|
6820a3fc36 | ||
|
|
7e8ed590eb | ||
|
|
4218b7ef44 | ||
|
|
87af343b38 | ||
|
|
26cd33b4dc | ||
|
|
861724ef4f | ||
|
|
86492cff60 | ||
|
|
27fd5cdad6 | ||
|
|
2fc444be4b | ||
|
|
d3490ae30c | ||
|
|
59c3895a51 | ||
|
|
11bc41b941 | ||
|
|
9a7f94a391 | ||
|
|
6487b98136 | ||
|
|
15df99510f | ||
|
|
3d5b1fcf64 | ||
|
|
928bc42cb9 | ||
|
|
643680ec47 | ||
|
|
a4a144a319 | ||
|
|
093085fba8 | ||
|
|
c56ea898a6 | ||
|
|
a5398a5c57 | ||
|
|
6dd40935b7 | ||
|
|
e6ee512001 | ||
|
|
e13d27c1f5 | ||
|
|
bb552fbdd6 | ||
|
|
ed8a3dd933 | ||
|
|
3204077a6c | ||
|
|
85d1bf4e1e | ||
|
|
42edb2e6b9 | ||
|
|
21174a4190 | ||
|
|
f6c2da24cd | ||
|
|
9f16aaac61 | ||
|
|
29fc9d7dac | ||
|
|
be3af5c2e5 | ||
|
|
e0e9ebde28 | ||
|
|
5dc16a39c5 | ||
|
|
416da5c60b | ||
|
|
f8047f9afe | ||
|
|
9e91933106 | ||
|
|
7407e6809b | ||
|
|
56f66779f9 | ||
|
|
2704c56e50 | ||
|
|
ba4cc1ceef | ||
|
|
8396cea652 | ||
|
|
2b1029f3b6 | ||
|
|
8e2709490f | ||
|
|
ebf8249154 | ||
|
|
aaddffcb2e | ||
|
|
29d1f8c666 | ||
|
|
0126645c4d | ||
|
|
1d657d6819 | ||
|
|
0b965d931c | ||
|
|
d3eaa4d7ba | ||
|
|
1dbbc6273b | ||
|
|
efa8b83249 | ||
|
|
30599bf63a | ||
|
|
7c14fe02ab | ||
|
|
ee92011b21 | ||
|
|
9861a22ef9 | ||
|
|
cd0415e523 | ||
|
|
33dcdde422 |
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash
|
||||
cp -nr /oopt-gnpy/gnpy/example-data /shared
|
||||
cp -nr /opt/application/oopt-gnpy/gnpy/example-data /shared
|
||||
exec "$@"
|
||||
|
||||
1
.dockerignore
Normal file
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
venv/
|
||||
7
.github/pull_request_template.md
vendored
Normal file
7
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
# Thanks for contributing to GNPy
|
||||
|
||||
If it isn't much trouble, please send your contribution as patches to our Gerrit.
|
||||
Here's [how to submit patches](https://review.gerrithub.io/Documentation/intro-gerrit-walkthrough-github.html), and here's a [list of stuff we are currently working on](https://review.gerrithub.io/p/Telecominfraproject/oopt-gnpy/+/dashboard/main:main).
|
||||
Just sign in via your existing GitHub account.
|
||||
|
||||
However, if you feel more comfortable with filing GitHub PRs, we can work with that too.
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -65,3 +65,5 @@ target/
|
||||
|
||||
# MacOS DS_store
|
||||
.DS_Store
|
||||
|
||||
venv/
|
||||
|
||||
@@ -2,4 +2,3 @@
|
||||
host=review.gerrithub.io
|
||||
project=Telecominfraproject/oopt-gnpy
|
||||
defaultrebase=0
|
||||
defaultbranch=develop
|
||||
|
||||
10
.travis.yml
10
.travis.yml
@@ -1,13 +1,17 @@
|
||||
dist: xenial
|
||||
sudo: false
|
||||
dist: focal
|
||||
os: linux
|
||||
language: python
|
||||
services: docker
|
||||
python:
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
before_install:
|
||||
- sudo apt-get -y install graphviz
|
||||
install: skip
|
||||
script:
|
||||
- python setup.py develop
|
||||
- pip install --editable .
|
||||
- pip install pytest-cov rstcheck
|
||||
- pytest --cov-report=xml --cov=gnpy -v
|
||||
- rstcheck --ignore-roles cite *.rst
|
||||
|
||||
19
.zuul.yaml
19
.zuul.yaml
@@ -2,23 +2,24 @@
|
||||
- project:
|
||||
check:
|
||||
jobs:
|
||||
- tox-py36-cover
|
||||
- tox-py38-cover
|
||||
- coverage-diff:
|
||||
voting: false
|
||||
dependencies:
|
||||
- tox-py36-cover-previous
|
||||
- tox-py36-cover
|
||||
- tox-py38-cover-previous
|
||||
- tox-py38-cover
|
||||
vars:
|
||||
coverage_job_name_previous: tox-py36-cover-previous
|
||||
coverage_job_name_current: tox-py36-cover
|
||||
coverage_job_name_previous: tox-py38-cover-previous
|
||||
coverage_job_name_current: tox-py38-cover
|
||||
- tox-linters-diff:
|
||||
voting: false
|
||||
- tox-docs-el8
|
||||
- tox-py36-cover-previous
|
||||
- tox-py36-el8
|
||||
- tox-docs-f32
|
||||
- tox-py38-cover-previous
|
||||
gate:
|
||||
jobs:
|
||||
- tox-py36-el8
|
||||
- tox-docs-el8
|
||||
- tox-py38-f32
|
||||
- tox-docs-f32
|
||||
tag:
|
||||
jobs:
|
||||
- oopt-release-python:
|
||||
|
||||
22
Dockerfile
22
Dockerfile
@@ -1,8 +1,18 @@
|
||||
FROM python:3.7-slim
|
||||
COPY . /oopt-gnpy
|
||||
WORKDIR /oopt-gnpy
|
||||
RUN apt update; apt install -y git
|
||||
RUN pip install .
|
||||
WORKDIR /shared/example-data
|
||||
ENTRYPOINT ["/oopt-gnpy/.docker-entry.sh"]
|
||||
WORKDIR /opt/application/oopt-gnpy
|
||||
RUN mkdir -p /shared/example-data \
|
||||
&& groupadd gnpy \
|
||||
&& useradd -u 1000 -g gnpy -m gnpy \
|
||||
&& apt-get update \
|
||||
&& apt-get install git -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
COPY . /opt/application/oopt-gnpy
|
||||
WORKDIR /opt/application/oopt-gnpy
|
||||
RUN mkdir topology \
|
||||
&& mkdir equipment \
|
||||
&& mkdir autodesign \
|
||||
&& pip install . \
|
||||
&& chown -Rc gnpy:gnpy /opt/application/oopt-gnpy /shared/example-data
|
||||
USER gnpy
|
||||
ENTRYPOINT ["/opt/application/oopt-gnpy/.docker-entry.sh"]
|
||||
CMD ["/bin/bash"]
|
||||
|
||||
77
README.rst
77
README.rst
@@ -7,7 +7,7 @@
|
||||
`gnpy`: mesh optical network route planning and optimization library
|
||||
====================================================================
|
||||
|
||||
|docs| |travis| |doi| |contributors| |codacy-quality| |codecov|
|
||||
|pypi| |docs| |travis| |doi| |contributors| |codacy-quality| |codecov|
|
||||
|
||||
**`gnpy` is an open-source, community-developed library for building route
|
||||
planning and optimization tools in real-world mesh optical networks.**
|
||||
@@ -31,13 +31,6 @@ There are `weekly calls <https://telecominfraproject.workplace.com/events/702894
|
||||
Newcomers, users and telecom operators are especially welcome there.
|
||||
We encourage all interested people outside the TIP to `join the project <https://telecominfraproject.com/apply-for-membership/>`__.
|
||||
|
||||
Branches and Tagged Releases
|
||||
----------------------------
|
||||
|
||||
- all releases are `available via GitHub <https://github.com/Telecominfraproject/oopt-gnpy/releases>`_
|
||||
- the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, `validated code <https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality>`_. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group.
|
||||
- the `develop <https://github.com/Telecominfraproject/oopt-gnpy/tree/develop>`_ branch contains the latest code under active development, which may not be fully validated and tested.
|
||||
|
||||
How to Install
|
||||
--------------
|
||||
|
||||
@@ -77,7 +70,7 @@ example, to use the CORONET Global network defined in
|
||||
$ gnpy-transmission-example $(gnpy-example-data)/CORONET_Global_Topology.json
|
||||
|
||||
It is also possible to use an Excel file input (for example
|
||||
`gnpy/example-data/CORONET_Global_Topology.xlsx <gnpy/example-data/CORONET_Global_Topology.xlsx>`_).
|
||||
`gnpy/example-data/CORONET_Global_Topology.xls <gnpy/example-data/CORONET_Global_Topology.xls>`_).
|
||||
The Excel file will be processed into a JSON file with the same prefix.
|
||||
Further details about the Excel data structure are available `in the documentation <docs/excel.rst>`__.
|
||||
|
||||
@@ -118,27 +111,16 @@ An experimental support for Raman amplification is available:
|
||||
Configuration of Raman pumps (their frequencies, power and pumping direction) is done via the `RamanFiber element in the network topology <gnpy/example-data/raman_edfa_example_network.json>`_.
|
||||
General numeric parameters for simulaiton control are provided in the `gnpy/example-data/sim_params.json <gnpy/example-data/sim_params.json>`_.
|
||||
|
||||
Use ``gnpy-path-request`` to run multiple optimizations as follows:
|
||||
Use ``gnpy-path-request`` to request several paths at once:
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ gnpy-path-request -h
|
||||
Usage: gnpy-path-requests [-h] [-v] [-o OUTPUT] [network_filename] [service_filename] [eqpt_filename]
|
||||
$ cd $(gnpy-example-data)
|
||||
$ gnpy-path-request -o output_file.json \
|
||||
meshTopologyExampleV2.xls meshTopologyExampleV2_services.json
|
||||
|
||||
The ``network_filename`` and ``service_filename`` can be an XLS or JSON file. The ``eqpt_filename`` must be a JSON file.
|
||||
|
||||
To see an example of it, run:
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ cd $(gnpy-example-data)
|
||||
$ gnpy-path-request meshTopologyExampleV2.xls meshTopologyExampleV2_services.json eqpt_config.json -o output_file.json
|
||||
|
||||
This program requires a list of connections to be estimated and the equipment
|
||||
library. The program computes performances for the list of services (accepts
|
||||
`JSON <docs/json.rst>`__ or `Excel <docs/excel.rst>`__ format) using the same spectrum propagation modules as
|
||||
``gnpy-transmission-example``.
|
||||
The output format is based on `draft-ietf-teas-yang-path-computation-01 <https://tools.ietf.org/html/draft-ietf-teas-yang-path-computation-01>`_ with custom extensions (e.g., for transponder modes).
|
||||
This program operates on a network topology (`JSON <docs/json.rst>`__ or `Excel <docs/excel.rst>`__ format), processing the list of service requests (JSON or XLS again).
|
||||
The service requests and reply formats are based on the `draft-ietf-teas-yang-path-computation-01 <https://tools.ietf.org/html/draft-ietf-teas-yang-path-computation-01>`__ with custom extensions (e.g., for transponder modes).
|
||||
An example of the JSON input is provided in file `service-template.json`, while results are shown in `path_result_template.json`.
|
||||
|
||||
Important note: ``gnpy-path-request`` is not a network dimensionning tool: each service does not reserve spectrum, or occupy ressources such as transponders. It only computes path feasibility assuming the spectrum (between defined frequencies) is loaded with "nb of channels" spaced by "spacing" values as specified in the system parameters input in the service file, each cannel having the same characteristics in terms of baudrate, format,... as the service transponder. The transceiver element acts as a "logical starting/stopping point" for the spectral information propagation. At that point it is not meant to represent the capacity of add drop ports.
|
||||
@@ -146,6 +128,36 @@ As a result transponder type is not part of the network info. it is related to t
|
||||
|
||||
The current version includes a spectrum assigment features that enables to compute a candidate spectrum assignment for each service based on a first fit policy. Spectrum is assigned based on service specified spacing value, path_bandwidth value and selected mode for the transceiver. This spectrum assignment includes a basic capacity planning capability so that the spectrum resource is limited by the frequency min and max values defined for the links. If the requested services reach the link spectrum capacity, additional services feasibility are computed but marked as blocked due to spectrum reason.
|
||||
|
||||
REST API (experimental)
|
||||
-----------------------
|
||||
``gnpy`` provides an experimental api for requesting several paths at once. It is based on Flask server.
|
||||
You can run it through command line or Docker.
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ gnpy-rest
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ docker run -p 8080:8080 -it emmanuelledelfour/gnpy-experimental:candi-1.1 gnpy-rest
|
||||
|
||||
When starting the api server will aks for an encryption/decryption key. This key i used to encrypt equipment file when using /api/v1/equipments endpoint.
|
||||
This key is a Fernet key and can be generated this way:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from cryptography.fernet import Fernet
|
||||
Fernet.generate_key()
|
||||
|
||||
|
||||
After typing the key, you can detach the container by typing ^P^Q.
|
||||
After starting the api server, you can launch a request
|
||||
|
||||
.. code-block:: shell-session
|
||||
|
||||
$ curl -v -X POST -H "Content-Type: application/json" -d @<PATH_TO_JSON_REQUEST_FILE> https://localhost:8080/api/v1/path-computation -k
|
||||
|
||||
TODO: api documentation, unit tests, real WSGI server with trusted certificates
|
||||
|
||||
Contributing
|
||||
------------
|
||||
@@ -161,7 +173,8 @@ To get involved, please contact Jan Kundrát
|
||||
|
||||
See the `Onboarding Guide
|
||||
<https://github.com/Telecominfraproject/gnpy/wiki/Onboarding-Guide>`_ for
|
||||
specific details on code contributions.
|
||||
specific details on code contributions, or just `upload patches to our Gerrit
|
||||
<https://review.gerrithub.io/Documentation/intro-gerrit-walkthrough-github.html>`_.
|
||||
|
||||
See `AUTHORS.rst <AUTHORS.rst>`_ for past and present contributors.
|
||||
|
||||
@@ -212,8 +225,8 @@ implementations.
|
||||
:alt: Build Status via Travis CI
|
||||
:scale: 100%
|
||||
|
||||
.. |doi| image:: https://zenodo.org/badge/96894149.svg
|
||||
:target: https://zenodo.org/badge/latestdoi/96894149
|
||||
.. |doi| image:: https://zenodo.org/badge/DOI/10.5281/zenodo.3458319.svg
|
||||
:target: https://doi.org/10.5281/zenodo.3458319
|
||||
:alt: DOI
|
||||
:scale: 100%
|
||||
|
||||
@@ -232,6 +245,12 @@ implementations.
|
||||
:alt: Code Coverage via codecov
|
||||
:scale: 100%
|
||||
|
||||
.. |pypi| image:: https://img.shields.io/pypi/v/gnpy
|
||||
:target: https://pypi.org/project/gnpy/
|
||||
:alt: Install via PyPI
|
||||
:scale: 100%
|
||||
|
||||
|
||||
TIP OOPT/PSE & PSE WG Charter
|
||||
-----------------------------
|
||||
|
||||
|
||||
1
bindep.txt
Normal file
1
bindep.txt
Normal file
@@ -0,0 +1 @@
|
||||
graphviz
|
||||
269
docs/concepts.rst
Normal file
269
docs/concepts.rst
Normal file
@@ -0,0 +1,269 @@
|
||||
.. _concepts:
|
||||
|
||||
Simulating networks with GNPy
|
||||
=============================
|
||||
|
||||
Running simulations with GNPy requires three pieces of information:
|
||||
|
||||
- the :ref:`network topology<concepts-topology>`, which describes how the network looks like, what are the fiber lengths, what amplifiers are used, etc.,
|
||||
- the :ref:`equipment library<concepts-equipment>`, which holds machine-readable datasheets of the equipment used in the network,
|
||||
- the :ref:`simulation options<concepts-simulation>` holding instructions about what to simulate, and under which conditions.
|
||||
|
||||
.. _concepts-topology:
|
||||
|
||||
Network Topology
|
||||
----------------
|
||||
|
||||
The *topology* acts as a "digital self" of the simulated network.
|
||||
When given a network topology, GNPy can either run a specific simulation as-is, or it can *optimize* the topology before performing the simulation.
|
||||
|
||||
A network topology for GNPy is often a generic, mesh network.
|
||||
This enables GNPy to take into consideration the current spectrum allocation as well as availability and resiliency considerations.
|
||||
When the time comes to run a particular *propagation* of a signal and its impairments are computed, though, a linear path through the network is used.
|
||||
For this purpose, the *path* through the network refers to an ordered, acyclic sequence of *nodes* that are processed.
|
||||
This path is directional, and all "GNPy elements" along the path match the unidirectional part of a real-world network equipment.
|
||||
|
||||
.. note::
|
||||
In practical terms, an amplifier in GNPy refers to an entity with a single input port and a single output port.
|
||||
A real-world inline EDFA enclosed in a single chassis will be therefore represented as two GNPy-level amplifiers.
|
||||
|
||||
The network topology contains not just the physical topology of the network, but also references to the :ref:`equipment library<concepts-equipment>` and a set of *operating parameters* for each entity.
|
||||
These parameters include the **fiber length** of each fiber, the connector **attenutation losses**, or an amplifier's specific **gain setting**.
|
||||
|
||||
.. _complete-vs-incomplete:
|
||||
|
||||
Fully Specified vs. Partially Designed Networks
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Let's consider a simple triangle topology with three :abbr:`PoPs (Points of Presence)` covering three cities:
|
||||
|
||||
.. graphviz::
|
||||
:layout: neato
|
||||
:align: center
|
||||
|
||||
graph "High-level topology with three PoPs" {
|
||||
A -- B
|
||||
B -- C
|
||||
C -- A
|
||||
}
|
||||
|
||||
In the real world, each city would probably host a ROADM and some transponders:
|
||||
|
||||
.. graphviz::
|
||||
:layout: neato
|
||||
:align: center
|
||||
|
||||
graph "Simplified topology with transponders" {
|
||||
"ROADM A" [pos="2,2!"]
|
||||
"ROADM B" [pos="4,2!"]
|
||||
"ROADM C" [pos="3,1!"]
|
||||
"Transponder A" [shape=box, pos="0,2!"]
|
||||
"Transponder B" [shape=box, pos="6,2!"]
|
||||
"Transponder C" [shape=box, pos="3,0!"]
|
||||
|
||||
"ROADM A" -- "ROADM B"
|
||||
"ROADM B" -- "ROADM C"
|
||||
"ROADM C" -- "ROADM A"
|
||||
|
||||
"Transponder A" -- "ROADM A"
|
||||
"Transponder B" -- "ROADM B"
|
||||
"Transponder C" -- "ROADM C"
|
||||
}
|
||||
|
||||
GNPy simulation works by propagating the optical signal over a sequence of elements, which means that one has to add some preamplifiers and boosters.
|
||||
The amplifiers are, by definition, unidirectional, so the graph becomes quite complex:
|
||||
|
||||
.. _topo-roadm-preamp-booster:
|
||||
|
||||
.. graphviz::
|
||||
:layout: neato
|
||||
:align: center
|
||||
|
||||
digraph "Preamps and boosters are explicitly modeled in GNPy" {
|
||||
"ROADM A" [pos="2,4!"]
|
||||
"ROADM B" [pos="6,4!"]
|
||||
"ROADM C" [pos="4,0!"]
|
||||
"Transponder A" [shape=box, pos="1,5!"]
|
||||
"Transponder B" [shape=box, pos="7,5!"]
|
||||
"Transponder C" [shape=box, pos="4,-1!"]
|
||||
|
||||
"Transponder A" -> "ROADM A"
|
||||
"Transponder B" -> "ROADM B"
|
||||
"Transponder C" -> "ROADM C"
|
||||
"ROADM A" -> "Transponder A"
|
||||
"ROADM B" -> "Transponder B"
|
||||
"ROADM C" -> "Transponder C"
|
||||
|
||||
"Booster A C" [shape=triangle, orientation=-150, fixedsize=true, width=0.5, height=0.5, pos="2.2,3.2!", color=red, label=""]
|
||||
"Preamp A C" [shape=triangle, orientation=0, fixedsize=true, width=0.5, height=0.5, pos="1.5,3.0!", color=red, label=""]
|
||||
"ROADM A" -> "Booster A C"
|
||||
"Preamp A C" -> "ROADM A"
|
||||
|
||||
"Booster A B" [shape=triangle, orientation=-90, fixedsize=true, width=0.5, height=0.5, pos="3,4.3!", color=red, fontcolor=red, labelloc=b, label="\N\n\n"]
|
||||
"Preamp A B" [shape=triangle, orientation=90, fixedsize=true, width=0.5, height=0.5, pos="3,3.6!", color=red, fontcolor=red, labelloc=t, label="\n \N"]
|
||||
"ROADM A" -> "Booster A B"
|
||||
"Preamp A B" -> "ROADM A"
|
||||
|
||||
"Booster C B" [shape=triangle, orientation=-30, fixedsize=true, width=0.5, height=0.5, pos="4.7,0.9!", color=red, label=""]
|
||||
"Preamp C B" [shape=triangle, orientation=120, fixedsize=true, width=0.5, height=0.5, pos="5.4,0.7!", color=red, label=""]
|
||||
"ROADM C" -> "Booster C B"
|
||||
"Preamp C B" -> "ROADM C"
|
||||
|
||||
"Booster C A" [shape=triangle, orientation=30, fixedsize=true, width=0.5, height=0.5, pos="2.6,0.7!", color=red, label=""]
|
||||
"Preamp C A" [shape=triangle, orientation=-30, fixedsize=true, width=0.5, height=0.5, pos="3.3,0.9!", color=red, label=""]
|
||||
"ROADM C" -> "Booster C A"
|
||||
"Preamp C A" -> "ROADM C"
|
||||
|
||||
"Booster B A" [shape=triangle, orientation=90, fixedsize=true, width=0.5, height=0.5, pos="5,3.6!", labelloc=t, color=red, fontcolor=red, label="\n\N "]
|
||||
"Preamp B A" [shape=triangle, orientation=-90, fixedsize=true, width=0.5, height=0.5, pos="5,4.3!", labelloc=b, color=red, fontcolor=red, label="\N\n\n"]
|
||||
"ROADM B" -> "Booster B A"
|
||||
"Preamp B A" -> "ROADM B"
|
||||
|
||||
"Booster B C" [shape=triangle, orientation=-180, fixedsize=true, width=0.5, height=0.5, pos="6.5,3.0!", color=red, label=""]
|
||||
"Preamp B C" [shape=triangle, orientation=-20, fixedsize=true, width=0.5, height=0.5, pos="5.8,3.2!", color=red, label=""]
|
||||
"ROADM B" -> "Booster B C"
|
||||
"Preamp B C" -> "ROADM B"
|
||||
|
||||
"Booster A C" -> "Preamp C A"
|
||||
"Booster A B" -> "Preamp B A"
|
||||
"Booster C A" -> "Preamp A C"
|
||||
"Booster C B" -> "Preamp B C"
|
||||
"Booster B C" -> "Preamp C B"
|
||||
"Booster B A" -> "Preamp A B"
|
||||
}
|
||||
|
||||
In many regions, the ROADMs are not placed physically close to each other, so the long-haul fiber links (:abbr:`OMS (Optical Multiplex Section)`) are split into individual spans (:abbr:`OTS (Optical Transport Section)`) by in-line amplifiers, resulting in an even more complicated topology graphs:
|
||||
|
||||
.. graphviz::
|
||||
:layout: neato
|
||||
:align: center
|
||||
|
||||
digraph "A subset of a real topology with inline amplifiers" {
|
||||
"ROADM A" [pos="2,4!"]
|
||||
"ROADM B" [pos="6,4!"]
|
||||
"ROADM C" [pos="4,-3!"]
|
||||
"Transponder A" [shape=box, pos="1,5!"]
|
||||
"Transponder B" [shape=box, pos="7,5!"]
|
||||
"Transponder C" [shape=box, pos="4,-4!"]
|
||||
|
||||
"Transponder A" -> "ROADM A"
|
||||
"Transponder B" -> "ROADM B"
|
||||
"Transponder C" -> "ROADM C"
|
||||
"ROADM A" -> "Transponder A"
|
||||
"ROADM B" -> "Transponder B"
|
||||
"ROADM C" -> "Transponder C"
|
||||
|
||||
"Booster A C" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, height=0.5, pos="2.2,3.2!", label=""]
|
||||
"Preamp A C" [shape=triangle, orientation=0, fixedsize=true, width=0.5, height=0.5, pos="1.5,3.0!", label=""]
|
||||
"ROADM A" -> "Booster A C"
|
||||
"Preamp A C" -> "ROADM A"
|
||||
|
||||
"Booster A B" [shape=triangle, orientation=-90, fixedsize=true, width=0.5, height=0.5, pos="3,4.3!", label=""]
|
||||
"Preamp A B" [shape=triangle, orientation=90, fixedsize=true, width=0.5, height=0.5, pos="3,3.6!", label=""]
|
||||
"ROADM A" -> "Booster A B"
|
||||
"Preamp A B" -> "ROADM A"
|
||||
|
||||
"Booster C B" [shape=triangle, orientation=-30, fixedsize=true, width=0.5, height=0.5, pos="4.7,-2.1!", label=""]
|
||||
"Preamp C B" [shape=triangle, orientation=10, fixedsize=true, width=0.5, height=0.5, pos="5.4,-2.3!", label=""]
|
||||
"ROADM C" -> "Booster C B"
|
||||
"Preamp C B" -> "ROADM C"
|
||||
|
||||
"Booster C A" [shape=triangle, orientation=20, fixedsize=true, width=0.5, height=0.5, pos="2.6,-2.3!", label=""]
|
||||
"Preamp C A" [shape=triangle, orientation=-30, fixedsize=true, width=0.5, height=0.5, pos="3.3,-2.1!", label=""]
|
||||
"ROADM C" -> "Booster C A"
|
||||
"Preamp C A" -> "ROADM C"
|
||||
|
||||
"Booster B A" [shape=triangle, orientation=90, fixedsize=true, width=0.5, height=0.5, pos="5,3.6!", label=""]
|
||||
"Preamp B A" [shape=triangle, orientation=-90, fixedsize=true, width=0.5, height=0.5, pos="5,4.3!", label=""]
|
||||
"ROADM B" -> "Booster B A"
|
||||
"Preamp B A" -> "ROADM B"
|
||||
|
||||
"Booster B C" [shape=triangle, orientation=-180, fixedsize=true, width=0.5, height=0.5, pos="6.5,3.0!", label=""]
|
||||
"Preamp B C" [shape=triangle, orientation=-20, fixedsize=true, width=0.5, height=0.5, pos="5.8,3.2!", label=""]
|
||||
"ROADM B" -> "Booster B C"
|
||||
"Preamp B C" -> "ROADM B"
|
||||
|
||||
"Inline A C 1" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, pos="2.4,2.2!", label=" \N", color=red, fontcolor=red]
|
||||
"Inline A C 2" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, pos="2.6,1.2!", label=" \N", color=red, fontcolor=red]
|
||||
"Inline A C 3" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, pos="2.8,0.2!", label=" \N", color=red, fontcolor=red]
|
||||
"Inline A C n" [shape=triangle, orientation=-166, fixedsize=true, width=0.5, pos="3.0,-1.1!", label=" \N", color=red, fontcolor=red]
|
||||
|
||||
"Booster A C" -> "Inline A C 1"
|
||||
"Inline A C 1" -> "Inline A C 2"
|
||||
"Inline A C 2" -> "Inline A C 3"
|
||||
"Inline A C 3" -> "Inline A C n" [style=dotted]
|
||||
"Inline A C n" -> "Preamp C A"
|
||||
"Booster A B" -> "Preamp B A" [style=dotted]
|
||||
"Booster C A" -> "Preamp A C" [style=dotted]
|
||||
"Booster C B" -> "Preamp B C" [style=dotted]
|
||||
"Booster B C" -> "Preamp C B" [style=dotted]
|
||||
"Booster B A" -> "Preamp A B" [style=dotted]
|
||||
}
|
||||
|
||||
In such networks, GNPy's autodesign features becomes very useful.
|
||||
It is possible to connect ROADMs via "tentative links" which will be replaced by a sequence of actual fibers and specific amplifiers.
|
||||
In other cases where the location of amplifier huts is already known, but the specific EDFA models have not yet been decided, one can put in amplifier placeholders and let GNPy assign the best amplifier.
|
||||
|
||||
.. _concepts-equipment:
|
||||
|
||||
The Equipment Library
|
||||
---------------------
|
||||
|
||||
In order to produce an accurate simulation, GNPy needs to know the physical properties of each entity which affects the optical signal.
|
||||
Entries in the equipment library correspond to actual real-world, tangible entities.
|
||||
Unlike a typical :abbr:`NMS (Network Management System)`, GNPy considers not just the active :abbr:`NEs (Network Elements)` such as amplifiers and :abbr:`ROADMs (Reconfigurable Optical Add/Drop Multiplexers)`, but also the passive ones, such as the optical fiber.
|
||||
|
||||
As the signal propagates through the network, the largest source of optical impairments is the noise introduced from amplifiers.
|
||||
An accurate description of the :abbr:`EDFA (Erbium-Doped Fiber Amplifier)` and especially its noise characteristics is required.
|
||||
GNPy describes this property in terms of the **Noise Figure (NF)** of an amplifier model as a function of its operating point.
|
||||
|
||||
The amplifiers compensate power losses induced on the signal in the optical fiber.
|
||||
The linear losses, however, are just one phenomenon of a multitude of effects that affect the signals in a long fiber run.
|
||||
While a more detailed description is available :ref:`in the literature<physical-model>`, for the purpose of the equipment library, the description of the *optical fiber* comprises its **linear attenutation coefficient**, a set of parameters for the **Raman effect**, optical **dispersion**, etc.
|
||||
|
||||
Signals are introduced into the network via *transponders*.
|
||||
The set of parameters that are required describe the physical properties of each supported *mode* of the transponder, including its **symbol rate**, spectral **width**, etc.
|
||||
|
||||
In the junctions of the network, *ROADMs* are used for spectrum routing.
|
||||
GNPy currently does not take into consideration the spectrum filtering penalties of the :abbr:`WSSes (Wavelength Selective Switches)`, but the equipment library nonetheless contains a list of required parameters, such as the attenuation options, so that the network can be properly simulated.
|
||||
|
||||
.. _concepts-nf-model:
|
||||
|
||||
Amplifier Noise Figure Models
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
One of the key parameters of an amplifier is the method to use for computing the Noise Figure (NF).
|
||||
GNPy supports several different noise models with varying level of accuracy.
|
||||
When in doubt, contact your vendor's technical support and ask them to :ref:`contribute their equipment descriptions<extending-edfa>` to GNPy.
|
||||
|
||||
The most accurate noise models describe the resulting NF of an EDFA as a third-degree polynomial.
|
||||
GNPy understands polynomials as a NF-yielding function of the :ref:`gain difference from the optimal gain<ext-nf-model-polynomial-NF>`, or as a function of the input power resulting in an :ref:`incremental OSNR as used in OpenROADM<ext-nf-model-polynomial-OSNR-OpenROADM>`.
|
||||
For scenarios where the vendor has not yet contributed an accurate EDFA NF description to GNPy, it is possible to approximate the characteristics via an operator-focused, min-max NF model.
|
||||
|
||||
.. _nf-model-min-max-NF:
|
||||
|
||||
Min-max NF
|
||||
**********
|
||||
|
||||
This is an operator-focused model where performance is defined by the *minimal* and *maximal NF*.
|
||||
These are especially suited to model a dual-coil EDFA with a VOA in between.
|
||||
In these amplifiers, the minimal NF is achieved when the EDFA operates at its maximal (and usually optimal, in terms of flatness) gain.
|
||||
The worst (maximal) NF applies when the EDFA operates at its minimal gain.
|
||||
|
||||
This model is suitable for use when the vendor has not provided a more accurate performance description of the EDFA.
|
||||
|
||||
Raman Approximation
|
||||
*******************
|
||||
|
||||
While GNPy is fully Raman-aware, under certain scenarios it is useful to be able to run a simulation without an accurate Raman description.
|
||||
For these purposes the :ref:`polynomial NF<ext-nf-model-polynomial-NF>` model with :math:`\text{a} = \text{b} = \text{c} = 0`, and :math:`\text{d} = NF` can be used.
|
||||
|
||||
.. _concepts-simulation:
|
||||
|
||||
Simulation
|
||||
----------
|
||||
|
||||
When the network model has been instantiated and the physical properties and operational settings of the actual physical devices are known, GNPy can start simulating how the signal propagate through the optical fiber.
|
||||
|
||||
This set of input parameters include options such as the *spectrum allocation*, i.e., the number of channels and their spacing.
|
||||
Various strategies for network optimization can be provided as well.
|
||||
18
docs/conf.py
18
docs/conf.py
@@ -31,10 +31,12 @@ sys.path.insert(0, os.path.abspath('../'))
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = ['sphinx.ext.autodoc',
|
||||
'sphinx.ext.mathjax',
|
||||
'sphinx.ext.githubpages',
|
||||
'sphinxcontrib.bibtex',
|
||||
'pbr.sphinxext',]
|
||||
'sphinx.ext.mathjax',
|
||||
'sphinx.ext.githubpages',
|
||||
'sphinxcontrib.bibtex',
|
||||
'pbr.sphinxext',
|
||||
'sphinx.ext.graphviz',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
@@ -50,8 +52,8 @@ master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = 'gnpy'
|
||||
copyright = '2018, Telecom InfraProject - OOPT PSE Group'
|
||||
author = 'Telecom InfraProject - OOPT PSE Group'
|
||||
copyright = '2018 - 2021, Telecom Infra Project - OOPT PSE Group'
|
||||
author = 'Telecom Infra Project - OOPT PSE Group'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
@@ -150,7 +152,7 @@ latex_elements = {
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
(master_doc, 'gnpy.tex', 'gnpy Documentation',
|
||||
'Telecom InfraProject - OOPT PSE Group', 'manual'),
|
||||
'Telecom Infra Project - OOPT PSE Group', 'manual'),
|
||||
]
|
||||
|
||||
|
||||
@@ -181,3 +183,5 @@ autodoc_default_options = {
|
||||
'private-members': True,
|
||||
'show-inheritance': True,
|
||||
}
|
||||
|
||||
graphviz_output_format = 'svg'
|
||||
|
||||
@@ -121,15 +121,17 @@ and a fiber span from node3 to node6::
|
||||
Eqpt sheet
|
||||
----------
|
||||
|
||||
Eqt sheet is optional. It lists the amplifiers types and characteristics on each degree of the *Node A* line.
|
||||
Eqpt sheet must contain twelve columns::
|
||||
The equipment sheet (named "Eqpt") is optional.
|
||||
If provided, it specifies types of boosters and preamplifiers for all ROADM degrees of all ROADM nodes, and for all ILA nodes.
|
||||
|
||||
This sheet contains twelve columns::
|
||||
|
||||
<-- east cable from a to z --> <-- west from z to a -->
|
||||
Node A ; Node Z ; amp type ; att_in ; amp gain ; tilt ; att_out ; delta_p ; amp type ; att_in ; amp gain ; tilt ; att_out ; delta_p
|
||||
|
||||
If the sheet is present, it MUST have as many lines as egress directions of ROADMs defined in Links Sheet.
|
||||
If the sheet is present, it MUST have as many lines as there are egress directions of ROADMs defined in Links Sheet, and all ILAs.
|
||||
|
||||
For example, consider the following list of links (A,B and C being a ROADM and amp# ILAs)
|
||||
For example, consider the following list of links (A, B and C being a ROADM and amp# ILAs):
|
||||
|
||||
::
|
||||
|
||||
@@ -141,8 +143,8 @@ For example, consider the following list of links (A,B and C being a ROADM and a
|
||||
|
||||
then Eqpt sheet should contain:
|
||||
- one line for each ILAs: amp1, amp2, amp3
|
||||
- one line for each degree 1 ROADMs B and C
|
||||
- two lines for ROADM A which is a degree 2 ROADM
|
||||
- one line for each one-degree ROADM (B and C in this example)
|
||||
- two lines for each two-degree ROADM (just the ROADM A)
|
||||
|
||||
::
|
||||
|
||||
@@ -196,7 +198,7 @@ This generates a text file meshTopologyExampleV2_eqt_sheet.txt whose content ca
|
||||
Service sheet
|
||||
-------------
|
||||
|
||||
Service sheet is optional. It lists the services for which path and feasibility must be computed with ``gnpy-path_request``.
|
||||
Service sheet is optional. It lists the services for which path and feasibility must be computed with ``gnpy-path-request``.
|
||||
|
||||
Service sheet must contain 11 columns::
|
||||
|
||||
|
||||
161
docs/extending.rst
Normal file
161
docs/extending.rst
Normal file
@@ -0,0 +1,161 @@
|
||||
.. _extending:
|
||||
|
||||
Extending GNPy with vendor-specific data
|
||||
========================================
|
||||
|
||||
GNPy ships with an :ref:`equipment library<concepts-equipment>` containing machine-readable datasheets of networking equipment.
|
||||
Vendors who are willing to contribute descriptions of their supported products are encouraged to `submit a patch <https://review.gerrithub.io/Documentation/intro-gerrit-walkthrough-github.html>`__.
|
||||
|
||||
This chapter discusses option for modeling performance of :ref:`EDFA amplifiers<extending-edfa>`, :ref:`Raman amplifiers<extending-raman>`, :ref:`transponders<extending-transponder>` and :ref:`ROADMs<extending-roadm>`.
|
||||
|
||||
.. _extending-edfa:
|
||||
|
||||
EDFAs
|
||||
-----
|
||||
|
||||
An accurate description of the :abbr:`EDFA (Erbium-Doped Fiber Amplifier)` and especially its noise characteristics is required.
|
||||
GNPy describes this property in terms of the **Noise Figure (NF)** of an amplifier model as a function of its operating point.
|
||||
GNPy supports several different :ref:`noise models<concepts-nf-model>`, and vendors are encouraged to pick one which describes performance of their equipment most accurately.
|
||||
|
||||
.. _ext-nf-model-polynomial-NF:
|
||||
|
||||
Polynomial NF
|
||||
*************
|
||||
|
||||
This model computes the NF as a function of the difference between the optimal gain and the current gain.
|
||||
The NF is expressed as a third-degree polynomial:
|
||||
|
||||
.. math::
|
||||
|
||||
f(x) &= \text{a}x^3 + \text{b}x^2 + \text{c}x + \text{d}
|
||||
|
||||
\text{NF} &= f(G_\text{max} - G)
|
||||
|
||||
This model can be also used for fixed-gain fixed-NF amplifiers.
|
||||
In that case, use:
|
||||
|
||||
.. math::
|
||||
|
||||
a = b = c &= 0
|
||||
|
||||
d &= \text{NF}
|
||||
|
||||
.. _ext-nf-model-polynomial-OSNR-OpenROADM:
|
||||
|
||||
Polynomial OSNR (OpenROADM-style)
|
||||
*********************************
|
||||
|
||||
This model is useful for amplifiers compliant to the OpenROADM specification for ILA.
|
||||
In OpenROADM, amplifier performance is evaluated via its incremental OSNR, which is a function of the input power.
|
||||
|
||||
.. math::
|
||||
|
||||
\text{OSNR}_\text{inc}(P_\text{in}) = \text{a}P_\text{in}^3 + \text{b}P_\text{in}^2 + \text{c}P_\text{in} + \text{d}
|
||||
|
||||
.. _ext-nf-model-min-max-NF:
|
||||
|
||||
Min-max NF
|
||||
**********
|
||||
|
||||
When the vendor prefers not to share the amplifier description in full detail, GNPy also supports describing the NF characteristics via the *minimal* and *maximal NF*.
|
||||
This approximates a more accurate polynomial description reasonably well for some models of a dual-coil EDFA with a VOA in between.
|
||||
In these amplifiers, the minimal NF is achieved when the EDFA operates at its maximal (and usually optimal, in terms of flatness) gain.
|
||||
The worst (maximal) NF applies when the EDFA operates at the minimal gain.
|
||||
|
||||
.. _ext-nf-model-dual-stage-amplifier:
|
||||
|
||||
Dual-stage
|
||||
**********
|
||||
|
||||
Dual-stage amplifier combines two distinct amplifiers.
|
||||
Vendors which provide an accurate description of their preamp and booster stages separately can use the dual-stage model for an aggregate description of the whole amplifier.
|
||||
|
||||
.. _ext-nf-model-advanced:
|
||||
|
||||
Advanced Specification
|
||||
**********************
|
||||
|
||||
The amplifier performance can be further described in terms of gain ripple, NF ripple, and the dynamic gain tilt.
|
||||
When provided, the amplifier characteristic is fine-tuned as a function of carrier frequency.
|
||||
|
||||
.. _extending-raman:
|
||||
|
||||
Raman Amplifiers
|
||||
----------------
|
||||
|
||||
An accurate simulation of Raman amplification requires knowledge of:
|
||||
|
||||
- the *power* and *wavelength* of all Raman pumping lasers,
|
||||
- the *direction*, whether it is co-propagating or counter-propagating,
|
||||
- the Raman efficiency of the fiber,
|
||||
- the fiber temperature.
|
||||
|
||||
Under certain scenarios it is useful to be able to run a simulation without an accurate Raman description.
|
||||
For these purposes, it is possible to approximate a Raman amplifier via a fixed-gain EDFA with the :ref:`polynomial NF<ext-nf-model-polynomial-NF>` model using :math:`\text{a} = \text{b} = \text{c} = 0`, and a desired effective :math:`\text{d} = NF`.
|
||||
This is also useful to quickly approximate a hybrid EDFA+Raman amplifier.
|
||||
|
||||
.. _extending-transponder:
|
||||
|
||||
Transponders
|
||||
------------
|
||||
|
||||
Since transponders are usually capable of operating in a variety of modes, these are described separately.
|
||||
A *mode* usually refers to a particular performance point that is defined by a combination of the symbol rate, modulation format, and :abbr:`FEC (Forward Error Correction)`.
|
||||
|
||||
The following data are required for each mode:
|
||||
|
||||
``bit-rate``
|
||||
Data bit rate, in :math:`\text{Gbits}\times s^{-1}`.
|
||||
``baud-rate``
|
||||
Symbol modulation rate, in :math:`\text{Gbaud}`.
|
||||
``required-osnr``
|
||||
Minimal allowed OSNR for the receiver.
|
||||
``tx-osnr``
|
||||
Initial OSNR at the transmitter's output.
|
||||
``grid-spacing``
|
||||
Minimal grid spacing, i.e., an effective channel spectral bandwidth.
|
||||
In :math:`\text{Hz}`.
|
||||
``tx-roll-off``
|
||||
Roll-off parameter (:math:`\beta`) of the TX pulse shaping filter.
|
||||
This assumes a raised-cosine filter.
|
||||
``rx-power-min`` and ``rx-power-max``
|
||||
The allowed range of power at the receiver.
|
||||
In :math:`\text{dBm}`.
|
||||
``cd-max``
|
||||
Maximal allowed Chromatic Dispersion (CD).
|
||||
In :math:`\text{ps}/\text{nm}`.
|
||||
``pmd-max``
|
||||
Maximal allowed Polarization Mode Dispersion (PMD).
|
||||
In :math:`\text{ps}`.
|
||||
``cd-penalty``
|
||||
*Work-in-progress.*
|
||||
Describes the increase of the requires GSNR as the :abbr:`CD (Chromatic Dispersion)` deteriorates.
|
||||
``dgd-penalty``
|
||||
*Work-in-progress.*
|
||||
Describes the increase of the requires GSNR as the :abbr:`DGD (Differential Group Delay)` deteriorates.
|
||||
``pmd-penalty``
|
||||
*Work-in-progress.*
|
||||
Describes the increase of the requires GSNR as the :abbr:`PMD (Polarization Mode Dispersion)` deteriorates.
|
||||
|
||||
GNPy does not directly track the FEC performance, so the type of chosen FEC is likely indicated in the *name* of the selected transponder mode alone.
|
||||
|
||||
.. _extending-roadm:
|
||||
|
||||
ROADMs
|
||||
------
|
||||
|
||||
In a :abbr:`ROADM (Reconfigurable Add/Drop Multiplexer)`, GNPy simulates the impairments of the preamplifiers and boosters of line degrees :ref:`separately<topo-roadm-preamp-booster>`.
|
||||
The set of parameters for each ROADM model therefore includes:
|
||||
|
||||
``add-drop-osnr``
|
||||
OSNR penalty introduced by the Add and Drop stages of this ROADM type.
|
||||
``target-channel-out-power``
|
||||
Per-channel target TX power towards the egress amplifier.
|
||||
Within GNPy, a ROADM is expected to attenuate any signal that enters the ROADM node to this level.
|
||||
This can be overridden on a per-link in the network topology.
|
||||
``pmd``
|
||||
Polarization mode dispersion (PMD) penalty of the express path.
|
||||
In :math:`\text{ps}`.
|
||||
|
||||
Provisions are in place to define the list of all allowed booster and preamplifier types.
|
||||
This is useful for specifying constraints on what amplifier modules fit into ROADM chassis, and when using fully disaggregated ROADM topologies as well.
|
||||
@@ -8,9 +8,11 @@ in real-world mesh optical networks. It is based on the Gaussian Noise Model.
|
||||
.. toctree::
|
||||
:maxdepth: 4
|
||||
|
||||
concepts
|
||||
install
|
||||
json
|
||||
excel
|
||||
extending
|
||||
model
|
||||
gnpy-api
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ of the `gnpy` repo and install it with:
|
||||
|
||||
$ git clone https://github.com/Telecominfraproject/oopt-gnpy # clone the repo
|
||||
$ cd oopt-gnpy
|
||||
$ python setup.py develop
|
||||
$ pip install --editable . # note the trailing dot
|
||||
|
||||
To test that `gnpy` was successfully installed, you can run this command. If it
|
||||
executes without a ``ModuleNotFoundError``, you have successfully installed
|
||||
|
||||
@@ -7,10 +7,9 @@ Some data (such as network topology or the service requests) can be also passed
|
||||
Equipment Library
|
||||
-----------------
|
||||
|
||||
Design and transmission parameters are defined in a dedicated json file. By
|
||||
default, this information is read from `gnpy/example-data/eqpt_config.json
|
||||
<gnpy/example-data/eqpt_config.json>`_. This file defines the equipment libraries that
|
||||
can be customized (EDFAs, fibers, and transceivers).
|
||||
Design and transmission parameters are defined in a dedicated json file.
|
||||
By default, this information is read from `gnpy/example-data/eqpt_config.json <https://github.com/Telecominfraproject/oopt-gnpy/blob/master/gnpy/example-data/eqpt_config.json>`_.
|
||||
This file defines the equipment libraries that can be customized (EDFAs, fibers, and transceivers).
|
||||
|
||||
It also defines the simulation parameters (spans, ROADMs, and the spectral
|
||||
information to transmit.)
|
||||
@@ -23,7 +22,7 @@ can be added and existing ones removed. Three different noise models are availab
|
||||
|
||||
1. ``'type_def': 'variable_gain'`` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: ``nf_min``, ``nf_max``, and ``gain_flatmax``. It is not a simple interpolation but a 2-stage NF calculation.
|
||||
2. ``'type_def': 'fixed_gain'`` is a fixed gain model. `NF == Cte == nf0` if `gain_min < gain < gain_flatmax`
|
||||
3. ``'type_def': None`` is an advanced model. A detailed JSON configuration file is required (by default `gnpy/example-data/std_medium_gain_advanced_config.json <gnpy/example-data/std_medium_gain_advanced_config.json>`_). It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled.
|
||||
3. ``'type_def': None`` is an advanced model. A detailed JSON configuration file is required (by default `gnpy/example-data/std_medium_gain_advanced_config.json <https://github.com/Telecominfraproject/oopt-gnpy/blob/master/gnpy/example-data/std_medium_gain_advanced_config.json>`_). It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled.
|
||||
|
||||
For all amplifier models:
|
||||
|
||||
@@ -73,7 +72,7 @@ Transceiver
|
||||
The transceiver equipment library is a list of supported transceivers. New
|
||||
transceivers can be added and existing ones removed at will by the user. It is
|
||||
used to determine the service list path feasibility when running the
|
||||
`path_request_run.py routine <gnpy/example-data/path_request_run.py>`_.
|
||||
``gnpy-path-request`` script.
|
||||
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
| field | type | description |
|
||||
@@ -113,8 +112,47 @@ The modes are defined as follows:
|
||||
| ``cost`` | (number) | Arbitrary unit |
|
||||
+----------------------+-----------+-----------------------------------------+
|
||||
|
||||
Simulation parameters
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
ROADM
|
||||
~~~~~
|
||||
|
||||
The user can only modify the value of existing parameters:
|
||||
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| field | type | description |
|
||||
+==========================+===========+=============================================+
|
||||
| ``target_pch_out_db`` | (number) | Auto-design sets the ROADM egress channel |
|
||||
| | | power. This reflects typical control loop |
|
||||
| | | algorithms that adjust ROADM losses to |
|
||||
| | | equalize channels (eg coming from different |
|
||||
| | | ingress direction or add ports) |
|
||||
| | | This is the default value |
|
||||
| | | Roadm/params/target_pch_out_db if no value |
|
||||
| | | is given in the ``Roadm`` element in the |
|
||||
| | | topology input description. |
|
||||
| | | This default value is ignored if a |
|
||||
| | | params/target_pch_out_db value is input in |
|
||||
| | | the topology for a given ROADM. |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``add_drop_osnr`` | (number) | OSNR contribution from the add/drop ports |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``pmd`` | (number) | Polarization mode dispersion (PMD). (s) |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``restrictions`` | (dict of | If non-empty, keys ``preamp_variety_list`` |
|
||||
| | strings) | and ``booster_variety_list`` represent |
|
||||
| | | list of ``type_variety`` amplifiers which |
|
||||
| | | are allowed for auto-design within ROADM's |
|
||||
| | | line degrees. |
|
||||
| | | |
|
||||
| | | If no booster should be placed on a degree, |
|
||||
| | | insert a ``Fused`` node on the degree |
|
||||
| | | output. |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
|
||||
Global parameters
|
||||
-----------------
|
||||
|
||||
The following options are still defined in ``eqpt_config.json`` for legacy reasons, but
|
||||
they do not correspond to tangible network devices.
|
||||
|
||||
Auto-design automatically creates EDFA amplifier network elements when they are
|
||||
missing, after a fiber, or between a ROADM and a fiber. This auto-design
|
||||
@@ -256,42 +294,6 @@ parameters:
|
||||
}
|
||||
}
|
||||
|
||||
ROADM
|
||||
~~~~~
|
||||
|
||||
The user can only modify the value of existing parameters:
|
||||
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| field | type | description |
|
||||
+==========================+===========+=============================================+
|
||||
| ``target_pch_out_db`` | (number) | Auto-design sets the ROADM egress channel |
|
||||
| | | power. This reflects typical control loop |
|
||||
| | | algorithms that adjust ROADM losses to |
|
||||
| | | equalize channels (eg coming from different |
|
||||
| | | ingress direction or add ports) |
|
||||
| | | This is the default value |
|
||||
| | | Roadm/params/target_pch_out_db if no value |
|
||||
| | | is given in the ``Roadm`` element in the |
|
||||
| | | topology input description. |
|
||||
| | | This default value is ignored if a |
|
||||
| | | params/target_pch_out_db value is input in |
|
||||
| | | the topology for a given ROADM. |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``add_drop_osnr`` | (number) | OSNR contribution from the add/drop ports |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``pmd`` | (number) | Polarization mode dispersion (PMD). (s) |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``restrictions`` | (dict of | If non-empty, keys ``preamp_variety_list`` |
|
||||
| | strings) | and ``booster_variety_list`` represent |
|
||||
| | | list of ``type_variety`` amplifiers which |
|
||||
| | | are allowed for auto-design within ROADM's |
|
||||
| | | line degrees. |
|
||||
| | | |
|
||||
| | | If no booster should be placed on a degree, |
|
||||
| | | insert a ``Fused`` node on the degree |
|
||||
| | | output. |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
|
||||
SpectralInformation
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
.. _physical-model:
|
||||
|
||||
Physical Model used in GNPy
|
||||
===========================
|
||||
|
||||
|
||||
9
gnpy/api/__init__.py
Normal file
9
gnpy/api/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
# coding: utf-8
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
import gnpy.api.route.path_request_route
|
||||
import gnpy.api.route.status_route
|
||||
import gnpy.api.route.topology_route
|
||||
import gnpy.api.route.equipments_route
|
||||
1
gnpy/api/exception/__init__.py
Normal file
1
gnpy/api/exception/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# coding: utf-8
|
||||
14
gnpy/api/exception/config_error.py
Normal file
14
gnpy/api/exception/config_error.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class ConfigError(Exception):
|
||||
""" Exception raise for configuration file error
|
||||
Attributes:
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
14
gnpy/api/exception/equipment_error.py
Normal file
14
gnpy/api/exception/equipment_error.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class EquipmentError(Exception):
|
||||
""" Exception raise for equipment error
|
||||
Attributes:
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
33
gnpy/api/exception/exception_handler.py
Normal file
33
gnpy/api/exception/exception_handler.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# coding: utf-8
|
||||
import json
|
||||
import re
|
||||
|
||||
import werkzeug
|
||||
|
||||
from gnpy.api.model.error import Error
|
||||
|
||||
_reaesc = re.compile(r'\x1b[^m]*m')
|
||||
|
||||
|
||||
def common_error_handler(exception):
|
||||
"""
|
||||
|
||||
:type exception: Exception
|
||||
|
||||
"""
|
||||
status_code = 500
|
||||
if not isinstance(exception, werkzeug.exceptions.HTTPException):
|
||||
exception = werkzeug.exceptions.InternalServerError()
|
||||
exception.description = "Something went wrong on our side."
|
||||
else:
|
||||
status_code = exception.code
|
||||
response = Error(message=exception.name, description=exception.description,
|
||||
code=status_code)
|
||||
|
||||
return werkzeug.Response(response=json.dumps(response.__dict__), status=status_code, mimetype='application/json')
|
||||
|
||||
|
||||
def bad_request_handler(exception):
|
||||
response = Error(message='bad request', description=_reaesc.sub('', str(exception)),
|
||||
code=400)
|
||||
return werkzeug.Response(response=json.dumps(response.__dict__), status=400, mimetype='application/json')
|
||||
14
gnpy/api/exception/path_computation_error.py
Normal file
14
gnpy/api/exception/path_computation_error.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class PathComputationError(Exception):
|
||||
""" Exception raise for path computation error error
|
||||
Attributes:
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
14
gnpy/api/exception/topology_error.py
Normal file
14
gnpy/api/exception/topology_error.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class TopologyError(Exception):
|
||||
""" Exception raise for topology error
|
||||
Attributes:
|
||||
message -- explanation of the error
|
||||
"""
|
||||
|
||||
def __init__(self, message):
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
return self.message
|
||||
1
gnpy/api/model/__init__.py
Normal file
1
gnpy/api/model/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# coding: utf-8
|
||||
17
gnpy/api/model/error.py
Normal file
17
gnpy/api/model/error.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class Error:
|
||||
|
||||
def __init__(self, code: int = None, message: str = None, description: str = None):
|
||||
"""Error
|
||||
:param code: The code of this Error.
|
||||
:type code: int
|
||||
:param message: The message of this Error.
|
||||
:type message: str
|
||||
:param description: The description of this Error.
|
||||
:type description: str
|
||||
"""
|
||||
self.code = code
|
||||
self.message = message
|
||||
self.description = description
|
||||
8
gnpy/api/model/result.py
Normal file
8
gnpy/api/model/result.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
class Result:
|
||||
|
||||
def __init__(self, message: str = None, description: str = None):
|
||||
self.message = message
|
||||
self.description = description
|
||||
83
gnpy/api/rest_example.py
Normal file
83
gnpy/api/rest_example.py
Normal file
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
gnpy.tools.rest_example
|
||||
=======================
|
||||
|
||||
GNPy as a rest API example
|
||||
'''
|
||||
|
||||
import logging
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
import werkzeug
|
||||
from flask_injector import FlaskInjector
|
||||
from injector import singleton
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
import gnpy.core.exceptions as exceptions
|
||||
from gnpy.api import app
|
||||
from gnpy.api.exception.exception_handler import bad_request_handler, common_error_handler
|
||||
from gnpy.api.exception.path_computation_error import PathComputationError
|
||||
from gnpy.api.exception.topology_error import TopologyError
|
||||
from gnpy.api.service import config_service
|
||||
from gnpy.api.service.encryption_service import EncryptionService
|
||||
from gnpy.api.service.equipment_service import EquipmentService
|
||||
from gnpy.api.service.path_request_service import PathRequestService
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _init_logger():
|
||||
handler = RotatingFileHandler('api.log', maxBytes=1024 * 1024, backupCount=5, encoding='utf-8')
|
||||
ch = logging.StreamHandler()
|
||||
logging.basicConfig(level=logging.INFO, handlers=[handler, ch],
|
||||
format="%(asctime)s %(levelname)s %(name)s(%(lineno)s) [%(threadName)s - %(thread)d] - %("
|
||||
"message)s")
|
||||
|
||||
|
||||
def _init_app(key):
|
||||
app.register_error_handler(KeyError, bad_request_handler)
|
||||
app.register_error_handler(TypeError, bad_request_handler)
|
||||
app.register_error_handler(ValueError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.ConfigurationError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.DisjunctionError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.EquipmentConfigError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.NetworkTopologyError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.ServiceError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.SpectrumError, bad_request_handler)
|
||||
app.register_error_handler(exceptions.ParametersError, bad_request_handler)
|
||||
app.register_error_handler(AssertionError, bad_request_handler)
|
||||
app.register_error_handler(InternalServerError, common_error_handler)
|
||||
app.register_error_handler(TopologyError, bad_request_handler)
|
||||
app.register_error_handler(PathComputationError, bad_request_handler)
|
||||
for error_code in werkzeug.exceptions.default_exceptions:
|
||||
app.register_error_handler(error_code, common_error_handler)
|
||||
config = config_service.init_config()
|
||||
config.add_section('SECRET')
|
||||
config.set('SECRET', 'equipment', key)
|
||||
app.config['properties'] = config
|
||||
|
||||
|
||||
def _configure(binder):
|
||||
binder.bind(EquipmentService,
|
||||
to=EquipmentService(EncryptionService(app.config['properties'].get('SECRET', 'equipment'))),
|
||||
scope=singleton)
|
||||
binder.bind(PathRequestService,
|
||||
to=PathRequestService(EncryptionService(app.config['properties'].get('SECRET', 'equipment'))),
|
||||
scope=singleton)
|
||||
app.config['properties'].pop('SECRET', None)
|
||||
|
||||
|
||||
def main():
|
||||
key = input('Enter encryption/decryption key: ')
|
||||
_init_logger()
|
||||
_init_app(key)
|
||||
FlaskInjector(app=app, modules=[_configure])
|
||||
|
||||
app.run(host='0.0.0.0', port=8080, ssl_context='adhoc')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
2
gnpy/api/route/__init__.py
Normal file
2
gnpy/api/route/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# coding: utf-8
|
||||
|
||||
38
gnpy/api/route/equipments_route.py
Normal file
38
gnpy/api/route/equipments_route.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# coding: utf-8
|
||||
import http
|
||||
import json
|
||||
|
||||
from flask import request
|
||||
|
||||
from gnpy.api import app
|
||||
from gnpy.api.exception.equipment_error import EquipmentError
|
||||
from gnpy.api.model.result import Result
|
||||
from gnpy.api.service.equipment_service import EquipmentService
|
||||
|
||||
EQUIPMENT_BASE_PATH = '/api/v1/equipments'
|
||||
EQUIPMENT_ID_PATH = EQUIPMENT_BASE_PATH + '/<equipment_id>'
|
||||
|
||||
|
||||
@app.route(EQUIPMENT_BASE_PATH, methods=['POST'])
|
||||
def create_equipment(equipment_service: EquipmentService):
|
||||
if not request.is_json:
|
||||
raise EquipmentError('Request body is not json')
|
||||
equipment_identifier = equipment_service.save_equipment(request.json)
|
||||
response = Result(message='Equipment creation ok', description=equipment_identifier)
|
||||
return json.dumps(response.__dict__), 201, {'location': EQUIPMENT_BASE_PATH + '/' + equipment_identifier}
|
||||
|
||||
|
||||
@app.route(EQUIPMENT_ID_PATH, methods=['PUT'])
|
||||
def update_equipment(equipment_id, equipment_service: EquipmentService):
|
||||
if not request.is_json:
|
||||
raise EquipmentError('Request body is not json')
|
||||
equipment_identifier = equipment_service.update_equipment(request.json, equipment_id)
|
||||
response = Result(message='Equipment update ok', description=equipment_identifier)
|
||||
return json.dumps(response.__dict__), http.HTTPStatus.OK, {
|
||||
'location': EQUIPMENT_BASE_PATH + '/' + equipment_identifier}
|
||||
|
||||
|
||||
@app.route(EQUIPMENT_ID_PATH, methods=['DELETE'])
|
||||
def delete_equipment(equipment_id, equipment_service: EquipmentService):
|
||||
equipment_service.delete_equipment(equipment_id)
|
||||
return '', http.HTTPStatus.NO_CONTENT
|
||||
63
gnpy/api/route/path_request_route.py
Normal file
63
gnpy/api/route/path_request_route.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# coding: utf-8
|
||||
import http
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from flask import request
|
||||
|
||||
from gnpy.api import app
|
||||
from gnpy.api.exception.equipment_error import EquipmentError
|
||||
from gnpy.api.exception.topology_error import TopologyError
|
||||
from gnpy.api.service import topology_service
|
||||
from gnpy.api.service.equipment_service import EquipmentService
|
||||
from gnpy.api.service.path_request_service import PathRequestService
|
||||
from gnpy.tools.json_io import _equipment_from_json, network_from_json
|
||||
from gnpy.topology.request import ResultElement
|
||||
|
||||
PATH_COMPUTATION_BASE_PATH = '/api/v1/path-computation'
|
||||
AUTODESIGN_PATH = PATH_COMPUTATION_BASE_PATH + '/<path_computation_id>/autodesign'
|
||||
|
||||
_examples_dir = Path(__file__).parent.parent.parent / 'example-data'
|
||||
|
||||
|
||||
@app.route(PATH_COMPUTATION_BASE_PATH, methods=['POST'])
|
||||
def compute_path(equipment_service: EquipmentService, path_request_service: PathRequestService):
|
||||
data = request.json
|
||||
service = data['gnpy-api:service']
|
||||
if 'gnpy-api:topology' in data:
|
||||
topology = data['gnpy-api:topology']
|
||||
elif 'gnpy-api:topology_id' in data:
|
||||
topology = topology_service.get_topology(data['gnpy-api:topology_id'])
|
||||
else:
|
||||
raise TopologyError('No topology found in request')
|
||||
if 'gnpy-api:equipment' in data:
|
||||
equipment = data['gnpy-api:equipment']
|
||||
elif 'gnpy-api:equipment_id' in data:
|
||||
equipment = equipment_service.get_equipment(data['gnpy-api:equipment_id'])
|
||||
else:
|
||||
raise EquipmentError('No equipment found in request')
|
||||
equipment = _equipment_from_json(equipment,
|
||||
os.path.join(_examples_dir, 'std_medium_gain_advanced_config.json'))
|
||||
network = network_from_json(topology, equipment)
|
||||
|
||||
propagatedpths, reversed_propagatedpths, rqs, path_computation_id = path_request_service.path_requests_run(service,
|
||||
network,
|
||||
equipment)
|
||||
# Generate the output
|
||||
result = []
|
||||
# assumes that list of rqs and list of propgatedpths have same order
|
||||
for i, pth in enumerate(propagatedpths):
|
||||
result.append(ResultElement(rqs[i], pth, reversed_propagatedpths[i]))
|
||||
return {"result": {"response": [n.json for n in result]}}, 201, {
|
||||
'location': AUTODESIGN_PATH.replace('<path_computation_id>', path_computation_id)}
|
||||
|
||||
|
||||
@app.route(AUTODESIGN_PATH, methods=['GET'])
|
||||
def get_autodesign(path_computation_id, path_request_service: PathRequestService):
|
||||
return path_request_service.get_autodesign(path_computation_id), http.HTTPStatus.OK
|
||||
|
||||
|
||||
@app.route(AUTODESIGN_PATH, methods=['DELETE'])
|
||||
def delete_autodesign(path_computation_id, path_request_service: PathRequestService):
|
||||
path_request_service.delete_autodesign(path_computation_id)
|
||||
return '', http.HTTPStatus.NO_CONTENT
|
||||
7
gnpy/api/route/status_route.py
Normal file
7
gnpy/api/route/status_route.py
Normal file
@@ -0,0 +1,7 @@
|
||||
# coding: utf-8
|
||||
from gnpy.api import app
|
||||
|
||||
|
||||
@app.route('/api/v1/status', methods=['GET'])
|
||||
def api_status():
|
||||
return {"version": "v1", "status": "ok"}, 200
|
||||
43
gnpy/api/route/topology_route.py
Normal file
43
gnpy/api/route/topology_route.py
Normal file
@@ -0,0 +1,43 @@
|
||||
# coding: utf-8
|
||||
import http
|
||||
import json
|
||||
|
||||
from flask import request
|
||||
|
||||
from gnpy.api import app
|
||||
from gnpy.api.exception.topology_error import TopologyError
|
||||
from gnpy.api.model.result import Result
|
||||
from gnpy.api.service import topology_service
|
||||
|
||||
TOPOLOGY_BASE_PATH = '/api/v1/topologies'
|
||||
TOPOLOGY_ID_PATH = TOPOLOGY_BASE_PATH + '/<topology_id>'
|
||||
|
||||
|
||||
@app.route(TOPOLOGY_BASE_PATH, methods=['POST'])
|
||||
def create_topology():
|
||||
if not request.is_json:
|
||||
raise TopologyError('Request body is not json')
|
||||
topology_identifier = topology_service.save_topology(request.json)
|
||||
response = Result(message='Topology creation ok', description=topology_identifier)
|
||||
return json.dumps(response.__dict__), 201, {'location': TOPOLOGY_BASE_PATH + '/' + topology_identifier}
|
||||
|
||||
|
||||
@app.route(TOPOLOGY_ID_PATH, methods=['PUT'])
|
||||
def update_topology(topology_id):
|
||||
if not request.is_json:
|
||||
raise TopologyError('Request body is not json')
|
||||
topology_identifier = topology_service.update_topology(request.json, topology_id)
|
||||
response = Result(message='Topology update ok', description=topology_identifier)
|
||||
return json.dumps(response.__dict__), http.HTTPStatus.OK, {
|
||||
'location': TOPOLOGY_BASE_PATH + '/' + topology_identifier}
|
||||
|
||||
|
||||
@app.route(TOPOLOGY_ID_PATH, methods=['GET'])
|
||||
def get_topology(topology_id):
|
||||
return topology_service.get_topology(topology_id), http.HTTPStatus.OK
|
||||
|
||||
|
||||
@app.route(TOPOLOGY_ID_PATH, methods=['DELETE'])
|
||||
def delete_topology(topology_id):
|
||||
topology_service.delete_topology(topology_id)
|
||||
return '', http.HTTPStatus.NO_CONTENT
|
||||
1
gnpy/api/service/__init__.py
Normal file
1
gnpy/api/service/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# coding: utf-8
|
||||
45
gnpy/api/service/config_service.py
Normal file
45
gnpy/api/service/config_service.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# coding: utf-8
|
||||
import configparser
|
||||
import os
|
||||
|
||||
from flask import current_app
|
||||
|
||||
from gnpy.api.exception.config_error import ConfigError
|
||||
|
||||
|
||||
def init_config(properties_file_path: str = os.path.join(os.path.dirname(__file__),
|
||||
'properties.ini')) -> configparser.ConfigParser:
|
||||
"""
|
||||
Read config from properties_file_path
|
||||
@param properties_file_path: the properties file to read
|
||||
@return: config parser
|
||||
"""
|
||||
if not os.path.exists(properties_file_path):
|
||||
raise ConfigError('Properties file does not exist ' + properties_file_path)
|
||||
config = configparser.ConfigParser()
|
||||
config.read(properties_file_path)
|
||||
return config
|
||||
|
||||
|
||||
def get_topology_dir() -> str:
|
||||
"""
|
||||
Get the base dir where topologies are saved
|
||||
@return: the directory of topologies
|
||||
"""
|
||||
return current_app.config['properties'].get('DIRECTORY', 'topology')
|
||||
|
||||
|
||||
def get_equipment_dir() -> str:
|
||||
"""
|
||||
Get the base dir where equipments are saved
|
||||
@return: the directory of equipments
|
||||
"""
|
||||
return current_app.config['properties'].get('DIRECTORY', 'equipment')
|
||||
|
||||
|
||||
def get_autodesign_dir() -> str:
|
||||
"""
|
||||
Get the base dir where autodesign are saved
|
||||
@return: the directory of equipments
|
||||
"""
|
||||
return current_app.config['properties'].get('DIRECTORY', 'autodesign')
|
||||
13
gnpy/api/service/encryption_service.py
Normal file
13
gnpy/api/service/encryption_service.py
Normal file
@@ -0,0 +1,13 @@
|
||||
# coding: utf-8
|
||||
from cryptography.fernet import Fernet
|
||||
|
||||
|
||||
class EncryptionService:
|
||||
def __init__(self, key):
|
||||
self._fernet = Fernet(key)
|
||||
|
||||
def encrypt(self, data):
|
||||
return self._fernet.encrypt(data)
|
||||
|
||||
def decrypt(self, data):
|
||||
return self._fernet.decrypt(data)
|
||||
66
gnpy/api/service/equipment_service.py
Normal file
66
gnpy/api/service/equipment_service.py
Normal file
@@ -0,0 +1,66 @@
|
||||
# coding: utf-
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from injector import Inject
|
||||
|
||||
from gnpy.api.exception.equipment_error import EquipmentError
|
||||
from gnpy.api.service import config_service
|
||||
from gnpy.api.service.encryption_service import EncryptionService
|
||||
|
||||
|
||||
class EquipmentService:
|
||||
|
||||
def __init__(self, encryption_service: EncryptionService):
|
||||
self.encryption = encryption_service
|
||||
|
||||
def save_equipment(self, equipment):
|
||||
"""
|
||||
Save equipment to file.
|
||||
@param equipment: json content
|
||||
@return: a UUID identifier to identify the equipment
|
||||
"""
|
||||
equipment_identifier = str(uuid.uuid4())
|
||||
# TODO: validate json content
|
||||
self._write_equipment(equipment, equipment_identifier)
|
||||
return equipment_identifier
|
||||
|
||||
def update_equipment(self, equipment, equipment_identifier):
|
||||
"""
|
||||
Update equipment with identifier equipment_identifier.
|
||||
@param equipment_identifier: the identifier of the equipment to be updated
|
||||
@param equipment: json content
|
||||
@return: a UUID identifier to identify the equipment
|
||||
"""
|
||||
# TODO: validate json content
|
||||
self._write_equipment(equipment, equipment_identifier)
|
||||
return equipment_identifier
|
||||
|
||||
def _write_equipment(self, equipment, equipment_identifier):
|
||||
equipment_dir = config_service.get_equipment_dir()
|
||||
with(open(os.path.join(equipment_dir, '.'.join([equipment_identifier, 'json'])), 'wb')) as file:
|
||||
file.write(self.encryption.encrypt(json.dumps(equipment).encode()))
|
||||
|
||||
def get_equipment(self, equipment_id: str) -> dict:
|
||||
"""
|
||||
Get the equipment with id equipment_id
|
||||
@param equipment_id:
|
||||
@return: the equipment in json format
|
||||
"""
|
||||
equipment_dir = config_service.get_equipment_dir()
|
||||
equipment_file = os.path.join(equipment_dir, '.'.join([equipment_id, 'json']))
|
||||
if not os.path.exists(equipment_file):
|
||||
raise EquipmentError('Equipment with id {} does not exist '.format(equipment_id))
|
||||
with(open(equipment_file, 'rb')) as file:
|
||||
return json.loads(self.encryption.decrypt(file.read()))
|
||||
|
||||
def delete_equipment(self, equipment_id: str):
|
||||
"""
|
||||
Delete equipment with id equipment_id
|
||||
@param equipment_id:
|
||||
"""
|
||||
equipment_dir = config_service.get_equipment_dir()
|
||||
equipment_file = os.path.join(equipment_dir, '.'.join([equipment_id, 'json']))
|
||||
if os.path.exists(equipment_file):
|
||||
os.remove(equipment_file)
|
||||
100
gnpy/api/service/path_request_service.py
Normal file
100
gnpy/api/service/path_request_service.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import gnpy.core.ansi_escapes as ansi_escapes
|
||||
from gnpy.api.exception.path_computation_error import PathComputationError
|
||||
from gnpy.api.service import config_service
|
||||
from gnpy.api.service.encryption_service import EncryptionService
|
||||
from gnpy.core.network import build_network
|
||||
from gnpy.core.utils import lin2db, automatic_nch
|
||||
from gnpy.tools.json_io import requests_from_json, disjunctions_from_json, network_to_json
|
||||
from gnpy.topology.request import (compute_path_dsjctn, requests_aggregation,
|
||||
correct_json_route_list,
|
||||
deduplicate_disjunctions, compute_path_with_disjunction)
|
||||
from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PathRequestService:
|
||||
|
||||
def __init__(self, encryption_service: EncryptionService):
|
||||
self.encryption = encryption_service
|
||||
|
||||
def path_requests_run(self, service, network, equipment):
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,
|
||||
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
path_computation_identifier = str(uuid.uuid4())
|
||||
autodesign_dir = config_service.get_autodesign_dir()
|
||||
with(open(os.path.join(autodesign_dir, '.'.join([path_computation_identifier, 'json'])), 'wb')) as file:
|
||||
file.write(self.encryption.encrypt(json.dumps(network_to_json(network)).encode()))
|
||||
oms_list = build_oms_list(network, equipment)
|
||||
rqs = requests_from_json(service, equipment)
|
||||
|
||||
# check that request ids are unique. Non unique ids, may
|
||||
# mess the computation: better to stop the computation
|
||||
all_ids = [r.request_id for r in rqs]
|
||||
if len(all_ids) != len(set(all_ids)):
|
||||
for item in list(set(all_ids)):
|
||||
all_ids.remove(item)
|
||||
msg = f'Requests id {all_ids} are not unique'
|
||||
_logger.critical(msg)
|
||||
raise ValueError('Requests id ' + all_ids + ' are not unique')
|
||||
rqs = correct_json_route_list(network, rqs)
|
||||
|
||||
# pths = compute_path(network, equipment, rqs)
|
||||
dsjn = disjunctions_from_json(service)
|
||||
|
||||
# need to warn or correct in case of wrong disjunction form
|
||||
# disjunction must not be repeated with same or different ids
|
||||
dsjn = deduplicate_disjunctions(dsjn)
|
||||
|
||||
rqs, dsjn = requests_aggregation(rqs, dsjn)
|
||||
# TODO export novel set of aggregated demands in a json file
|
||||
|
||||
_logger.info(f'{ansi_escapes.blue}The following services have been requested:{ansi_escapes.reset}' + str(rqs))
|
||||
|
||||
_logger.info(f'{ansi_escapes.blue}Computing all paths with constraints{ansi_escapes.reset}')
|
||||
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
|
||||
|
||||
_logger.info(f'{ansi_escapes.blue}Propagating on selected path{ansi_escapes.reset}')
|
||||
propagatedpths, reversed_pths, reversed_propagatedpths = compute_path_with_disjunction(network, equipment, rqs,
|
||||
pths)
|
||||
# Note that deepcopy used in compute_path_with_disjunction returns
|
||||
# a list of nodes which are not belonging to network (they are copies of the node objects).
|
||||
# so there can not be propagation on these nodes.
|
||||
|
||||
pth_assign_spectrum(pths, rqs, oms_list, reversed_pths)
|
||||
return propagatedpths, reversed_propagatedpths, rqs, path_computation_identifier
|
||||
|
||||
def get_autodesign(self, path_computation_id):
|
||||
"""
|
||||
Get the autodesign with id topology_id
|
||||
@param path_computation_id:
|
||||
@return: the autodesign in json format
|
||||
"""
|
||||
autodesign_dir = config_service.get_autodesign_dir()
|
||||
autodesign_file = os.path.join(autodesign_dir, '.'.join([path_computation_id, 'json']))
|
||||
if not os.path.exists(autodesign_file):
|
||||
raise PathComputationError('Autodesign with id {} does not exist '.format(path_computation_id))
|
||||
with(open(autodesign_file, 'rb')) as file:
|
||||
return json.loads(self.encryption.decrypt(file.read()))
|
||||
|
||||
def delete_autodesign(self, path_computation_id: str):
|
||||
"""
|
||||
Delete autodesign with id equipment_id
|
||||
@param path_computation_id:
|
||||
"""
|
||||
autodesign_dir = config_service.get_autodesign_dir()
|
||||
autodesign_file = os.path.join(autodesign_dir, '.'.join([path_computation_id, 'json']))
|
||||
if os.path.exists(autodesign_file):
|
||||
os.remove(autodesign_file)
|
||||
4
gnpy/api/service/properties.ini
Normal file
4
gnpy/api/service/properties.ini
Normal file
@@ -0,0 +1,4 @@
|
||||
[DIRECTORY]
|
||||
topology: /opt/application/oopt-gnpy/topology
|
||||
equipment: /opt/application/oopt-gnpy/equipment
|
||||
autodesign: /opt/application/oopt-gnpy/autodesign
|
||||
62
gnpy/api/service/topology_service.py
Normal file
62
gnpy/api/service/topology_service.py
Normal file
@@ -0,0 +1,62 @@
|
||||
# coding: utf-
|
||||
import json
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from gnpy.api.exception.topology_error import TopologyError
|
||||
from gnpy.api.service import config_service
|
||||
|
||||
|
||||
def save_topology(topology):
|
||||
"""
|
||||
Save topology to file.
|
||||
@param topology: json content
|
||||
@return: a UUID identifier to identify the topology
|
||||
"""
|
||||
topology_identifier = str(uuid.uuid4())
|
||||
# TODO: validate json content
|
||||
_write_topology(topology, topology_identifier)
|
||||
return topology_identifier
|
||||
|
||||
|
||||
def update_topology(topology, topology_identifier):
|
||||
"""
|
||||
Update topology with identifier topology_identifier.
|
||||
@param topology_identifier: the identifier of the topology to be updated
|
||||
@param topology: json content
|
||||
@return: a UUID identifier to identify the topology
|
||||
"""
|
||||
# TODO: validate json content
|
||||
_write_topology(topology, topology_identifier)
|
||||
return topology_identifier
|
||||
|
||||
|
||||
def _write_topology(topology, topology_identifier):
|
||||
topology_dir = config_service.get_topology_dir()
|
||||
with(open(os.path.join(topology_dir, '.'.join([topology_identifier, 'json'])), 'w')) as file:
|
||||
json.dump(topology, file)
|
||||
|
||||
|
||||
def get_topology(topology_id: str) -> dict:
|
||||
"""
|
||||
Get the topology with id topology_id
|
||||
@param topology_id:
|
||||
@return: the topology in json format
|
||||
"""
|
||||
topology_dir = config_service.get_topology_dir()
|
||||
topology_file = os.path.join(topology_dir, '.'.join([topology_id, 'json']))
|
||||
if not os.path.exists(topology_file):
|
||||
raise TopologyError('Topology with id {} does not exist '.format(topology_id))
|
||||
with(open(topology_file, 'r')) as file:
|
||||
return json.load(file)
|
||||
|
||||
|
||||
def delete_topology(topology_id: str):
|
||||
"""
|
||||
Delete topology with id topology_id
|
||||
@param topology_id:
|
||||
"""
|
||||
topology_dir = config_service.get_topology_dir()
|
||||
topology_file = os.path.join(topology_dir, '.'.join([topology_id, 'json']))
|
||||
if os.path.exists(topology_file):
|
||||
os.remove(topology_file)
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
"""
|
||||
gnpy.core.elements
|
||||
==================
|
||||
|
||||
@@ -18,10 +18,9 @@ Network elements MUST implement two attributes :py:attr:`uid` and :py:attr:`name
|
||||
unique identifier and a printable name, and provide the :py:meth:`__call__` method taking a
|
||||
:class:`SpectralInformation` as an input and returning another :class:`SpectralInformation`
|
||||
instance as a result.
|
||||
'''
|
||||
"""
|
||||
|
||||
from numpy import abs, arange, array, divide, errstate, ones
|
||||
from numpy import interp, mean, pi, polyfit, polyval, sum, sqrt
|
||||
from numpy import abs, arange, array, divide, errstate, ones, interp, mean, pi, polyfit, polyval, sum, sqrt
|
||||
from scipy.constants import h, c
|
||||
from collections import namedtuple
|
||||
|
||||
@@ -184,17 +183,20 @@ class Transceiver(_Node):
|
||||
return spectral_info
|
||||
|
||||
|
||||
RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr pmd restrictions')
|
||||
RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr pmd restrictions per_degree_pch_out_db')
|
||||
|
||||
|
||||
class Roadm(_Node):
|
||||
def __init__(self, *args, params, **kwargs):
|
||||
if 'per_degree_pch_out_db' not in params.keys():
|
||||
params['per_degree_pch_out_db'] = {}
|
||||
super().__init__(*args, params=RoadmParams(**params), **kwargs)
|
||||
self.loss = 0 # auto-design interest
|
||||
self.effective_loss = None
|
||||
self.effective_pch_out_db = self.params.target_pch_out_db
|
||||
self.passive = True
|
||||
self.restrictions = self.params.restrictions
|
||||
self.per_degree_pch_out_db = self.params.per_degree_pch_out_db
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
@@ -202,8 +204,9 @@ class Roadm(_Node):
|
||||
'type': type(self).__name__,
|
||||
'params': {
|
||||
'target_pch_out_db': self.effective_pch_out_db,
|
||||
'restrictions': self.restrictions
|
||||
},
|
||||
'restrictions': self.restrictions,
|
||||
'per_degree_pch_out_db': self.per_degree_pch_out_db
|
||||
},
|
||||
'metadata': {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
@@ -220,15 +223,21 @@ class Roadm(_Node):
|
||||
f' effective loss (dB): {self.effective_loss:.2f}',
|
||||
f' pch out (dBm): {self.effective_pch_out_db!r}'])
|
||||
|
||||
def propagate(self, pref, *carriers):
|
||||
def propagate(self, pref, *carriers, degree):
|
||||
# pin_target and loss are read from eqpt_config.json['Roadm']
|
||||
# all ingress channels in xpress are set to this power level
|
||||
# but add channels are not, so we define an effective loss
|
||||
# in the case of add channels
|
||||
self.effective_pch_out_db = min(pref.p_spani, self.params.target_pch_out_db)
|
||||
# find the target power on this degree:
|
||||
# if a target power has been defined for this degree use it else use the global one.
|
||||
# if the input power is lower than the target one, use the input power instead because
|
||||
# a ROADM doesn't amplify, it can only attenuate
|
||||
# TODO maybe add a minimum loss for the ROADM
|
||||
per_degree_pch = self.per_degree_pch_out_db[degree] if degree in self.per_degree_pch_out_db.keys() else self.params.target_pch_out_db
|
||||
self.effective_pch_out_db = min(pref.p_spani, per_degree_pch)
|
||||
self.effective_loss = pref.p_spani - self.effective_pch_out_db
|
||||
carriers_power = array([c.power.signal + c.power.nli + c.power.ase for c in carriers])
|
||||
carriers_att = list(map(lambda x: lin2db(x * 1e3) - self.params.target_pch_out_db, carriers_power))
|
||||
carriers_att = list(map(lambda x: lin2db(x * 1e3) - per_degree_pch, carriers_power))
|
||||
exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default=0)
|
||||
carriers_att = list(map(lambda x: db2lin(x + exceeding_att), carriers_att))
|
||||
for carrier_att, carrier in zip(carriers_att, carriers):
|
||||
@@ -242,8 +251,8 @@ class Roadm(_Node):
|
||||
def update_pref(self, pref):
|
||||
return pref._replace(p_span0=pref.p_span0, p_spani=self.effective_pch_out_db)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers))
|
||||
def __call__(self, spectral_info, degree):
|
||||
carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers, degree=degree))
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
return spectral_info._replace(carriers=carriers, pref=pref)
|
||||
|
||||
@@ -303,8 +312,6 @@ class Fiber(_Node):
|
||||
if not params:
|
||||
params = {}
|
||||
super().__init__(*args, params=FiberParams(**params), **kwargs)
|
||||
self.carriers_in = None
|
||||
self.carriers_out = None
|
||||
self.pch_out_db = None
|
||||
self.nli_solver = NliSolver(self)
|
||||
|
||||
@@ -360,22 +367,6 @@ class Fiber(_Node):
|
||||
def passive(self):
|
||||
return True
|
||||
|
||||
def carriers(self, loc, attr):
|
||||
"""retrieve carriers information
|
||||
|
||||
:param loc: (in, out) of the class element
|
||||
:param attr: (ase, nli, signal, total) power information
|
||||
"""
|
||||
if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')):
|
||||
yield None
|
||||
return
|
||||
loc_attr = 'carriers_' + loc
|
||||
for c in getattr(self, loc_attr):
|
||||
if attr == 'total':
|
||||
yield c.power.ase + c.power.nli + c.power.signal
|
||||
else:
|
||||
yield c.power._asdict().get(attr, None)
|
||||
|
||||
def alpha(self, frequencies):
|
||||
"""It returns the values of the series expansion of attenuation coefficient alpha(f) for all f in frequencies
|
||||
|
||||
@@ -418,11 +409,11 @@ class Fiber(_Node):
|
||||
return self.params.pmd_coef * sqrt(self.params.length)
|
||||
|
||||
def _gn_analytic(self, carrier, *carriers):
|
||||
"""Computes the nonlinear interference power on a single carrier.
|
||||
r"""Computes the nonlinear interference power on a single carrier.
|
||||
The method uses eq. 120 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__.
|
||||
|
||||
:param carrier: the signal under analysis
|
||||
:param carriers: the full WDM comb
|
||||
:param \*carriers: the full WDM comb
|
||||
:return: carrier_nli: the amount of nonlinear interference in W on the under analysis
|
||||
"""
|
||||
|
||||
@@ -478,10 +469,8 @@ class Fiber(_Node):
|
||||
return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
self.carriers_in = spectral_info.carriers
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
self.carriers_out = carriers
|
||||
return spectral_info._replace(carriers=carriers, pref=pref)
|
||||
|
||||
|
||||
@@ -495,16 +484,18 @@ class RamanFiber(Fiber):
|
||||
self.raman_pumps = None
|
||||
self.raman_solver = RamanSolver(self)
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
return dict(super().to_json, operational=self.operational)
|
||||
|
||||
def update_pref(self, pref, *carriers):
|
||||
pch_out_db = lin2db(mean([carrier.power.signal for carrier in carriers])) + 30
|
||||
self.pch_out_db = round(pch_out_db, 2)
|
||||
return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
self.carriers_in = spectral_info.carriers
|
||||
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||
pref = self.update_pref(spectral_info.pref, *carriers)
|
||||
self.carriers_out = carriers
|
||||
return spectral_info._replace(carriers=carriers, pref=pref)
|
||||
|
||||
def propagate(self, *carriers):
|
||||
@@ -586,8 +577,6 @@ class Edfa(_Node):
|
||||
self.effective_pch_out_db = None
|
||||
self.passive = False
|
||||
self.att_in = None
|
||||
self.carriers_in = None
|
||||
self.carriers_out = None
|
||||
self.effective_gain = self.operational.gain_target
|
||||
self.delta_p = self.operational.delta_p # delta P with Pref (power swwep) in power mode
|
||||
self.tilt_target = self.operational.tilt_target
|
||||
@@ -639,22 +628,6 @@ class Edfa(_Node):
|
||||
f' effective pch (dBm): {self.effective_pch_out_db!r}',
|
||||
f' output VOA (dB): {self.out_voa:.2f}'])
|
||||
|
||||
def carriers(self, loc, attr):
|
||||
"""retrieve carriers information
|
||||
|
||||
:param loc: (in, out) of the class element
|
||||
:param attr: (ase, nli, signal, total) power information
|
||||
"""
|
||||
if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')):
|
||||
yield None
|
||||
return
|
||||
loc_attr = 'carriers_' + loc
|
||||
for c in getattr(self, loc_attr):
|
||||
if attr == 'total':
|
||||
yield c.power.ase + c.power.nli + c.power.signal
|
||||
else:
|
||||
yield c.power._asdict().get(attr, None)
|
||||
|
||||
def interpol_params(self, frequencies, pin, baud_rates, pref):
|
||||
"""interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from JSON
|
||||
"""
|
||||
@@ -714,8 +687,6 @@ class Edfa(_Node):
|
||||
nf_avg = pin_ch - polyval(nf_model.nf_coef, pin_ch) + 58
|
||||
elif type_def == 'advanced_model':
|
||||
nf_avg = polyval(nf_fit_coeff, -dg)
|
||||
else:
|
||||
assert False, "Unrecognized amplifier type, this should have been checked by the JSON loader"
|
||||
return nf_avg + pad, pad
|
||||
|
||||
def _calc_nf(self, avg=False):
|
||||
@@ -934,8 +905,6 @@ class Edfa(_Node):
|
||||
p_spani=pref.p_spani + self.effective_gain - self.out_voa)
|
||||
|
||||
def __call__(self, spectral_info):
|
||||
self.carriers_in = spectral_info.carriers
|
||||
carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers))
|
||||
pref = self.update_pref(spectral_info.pref)
|
||||
self.carriers_out = carriers
|
||||
return spectral_info._replace(carriers=carriers, pref=pref)
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
"""
|
||||
gnpy.core.exceptions
|
||||
====================
|
||||
|
||||
Exceptions thrown by other gnpy modules
|
||||
'''
|
||||
"""
|
||||
|
||||
|
||||
class ConfigurationError(Exception):
|
||||
'''User-provided configuration contains an error'''
|
||||
"""User-provided configuration contains an error"""
|
||||
|
||||
|
||||
class EquipmentConfigError(ConfigurationError):
|
||||
'''Incomplete or wrong configuration within the equipment library'''
|
||||
"""Incomplete or wrong configuration within the equipment library"""
|
||||
|
||||
|
||||
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'''
|
||||
"""Service of user-provided request is wrong"""
|
||||
|
||||
|
||||
class DisjunctionError(ServiceError):
|
||||
'''Disjunction of user-provided request can not be satisfied'''
|
||||
"""Disjunction of user-provided request can not be satisfied"""
|
||||
|
||||
|
||||
class SpectrumError(Exception):
|
||||
'''Spectrum errors of the program'''
|
||||
"""Spectrum errors of the program"""
|
||||
|
||||
|
||||
class ParametersError(ConfigurationError):
|
||||
'''Incomplete or wrong configurations within parameters json'''
|
||||
"""Incomplete or wrong configurations within parameters json"""
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
"""
|
||||
gnpy.core.info
|
||||
==============
|
||||
|
||||
This module contains classes for modelling :class:`SpectralInformation`.
|
||||
'''
|
||||
|
||||
"""
|
||||
|
||||
from collections import namedtuple
|
||||
from gnpy.core.utils import automatic_nch, lin2db
|
||||
|
||||
@@ -8,7 +8,6 @@ gnpy.core.network
|
||||
Working with networks which consist of network elements
|
||||
'''
|
||||
|
||||
from scipy.interpolate import interp1d
|
||||
from operator import attrgetter
|
||||
from gnpy.core import ansi_escapes, elements
|
||||
from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError
|
||||
@@ -138,6 +137,9 @@ def select_edfa(raman_allowed, gain_target, power_target, equipment, uid, restri
|
||||
|
||||
|
||||
def target_power(network, node, equipment): # get_fiber_dp
|
||||
if isinstance(node, elements.Roadm):
|
||||
return 0
|
||||
|
||||
SPAN_LOSS_REF = 20
|
||||
POWER_SLOPE = 0.3
|
||||
dp_range = list(equipment['Span']['default'].delta_power_range_db)
|
||||
@@ -147,13 +149,10 @@ def target_power(network, node, equipment): # get_fiber_dp
|
||||
dp = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2])
|
||||
dp = max(dp_range[0], dp)
|
||||
dp = min(dp_range[1], dp)
|
||||
except KeyError:
|
||||
except IndexError:
|
||||
raise ConfigurationError(f'invalid delta_power_range_db definition in eqpt_config[Span]'
|
||||
f'delta_power_range_db: [lower_bound, upper_bound, step]')
|
||||
|
||||
if isinstance(node, elements.Roadm):
|
||||
dp = 0
|
||||
|
||||
return dp
|
||||
|
||||
|
||||
@@ -161,7 +160,7 @@ def prev_node_generator(network, node):
|
||||
"""fused spans interest:
|
||||
iterate over all predecessors while they are Fused or Fiber type"""
|
||||
try:
|
||||
prev_node = next(n for n in network.predecessors(node))
|
||||
prev_node = next(network.predecessors(node))
|
||||
except StopIteration:
|
||||
raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology')
|
||||
# yield and re-iterate
|
||||
@@ -176,7 +175,7 @@ def next_node_generator(network, node):
|
||||
"""fused spans interest:
|
||||
iterate over all successors while they are Fused or Fiber type"""
|
||||
try:
|
||||
next_node = next(n for n in network.successors(node))
|
||||
next_node = next(network.successors(node))
|
||||
except StopIteration:
|
||||
raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology')
|
||||
# yield and re-iterate
|
||||
@@ -192,13 +191,13 @@ def span_loss(network, node):
|
||||
return the total span loss of all the fibers spliced by a Fused node"""
|
||||
loss = node.loss if node.passive else 0
|
||||
try:
|
||||
prev_node = next(n for n in network.predecessors(node))
|
||||
prev_node = next(network.predecessors(node))
|
||||
if isinstance(prev_node, elements.Fused):
|
||||
loss += sum(n.loss for n in prev_node_generator(network, node))
|
||||
except StopIteration:
|
||||
pass
|
||||
try:
|
||||
next_node = next(n for n in network.successors(node))
|
||||
next_node = next(network.successors(node))
|
||||
if isinstance(next_node, elements.Fused):
|
||||
loss += sum(n.loss for n in next_node_generator(network, node))
|
||||
except StopIteration:
|
||||
@@ -229,10 +228,10 @@ def find_last_node(network, node):
|
||||
def set_amplifier_voa(amp, power_target, power_mode):
|
||||
VOA_MARGIN = 1 # do not maximize the VOA optimization
|
||||
if amp.out_voa is None:
|
||||
if power_mode:
|
||||
if power_mode and amp.params.out_voa_auto:
|
||||
voa = min(amp.params.p_max - power_target,
|
||||
amp.params.gain_flatmax - amp.effective_gain)
|
||||
voa = max(round2float(max(voa, 0), 0.5) - VOA_MARGIN, 0) if amp.params.out_voa_auto else 0
|
||||
voa = max(round2float(voa, 0.5) - VOA_MARGIN, 0)
|
||||
amp.delta_p = amp.delta_p + voa
|
||||
amp.effective_gain = amp.effective_gain + voa
|
||||
else:
|
||||
@@ -240,24 +239,39 @@ def set_amplifier_voa(amp, power_target, power_mode):
|
||||
amp.out_voa = voa
|
||||
|
||||
|
||||
def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
def set_egress_amplifier(network, this_node, equipment, pref_ch_db, pref_total_db):
|
||||
""" this node can be a transceiver or a ROADM (same function called in both cases)
|
||||
"""
|
||||
power_mode = equipment['Span']['default'].power_mode
|
||||
next_oms = (n for n in network.successors(roadm) if not isinstance(n, elements.Transceiver))
|
||||
next_oms = (n for n in network.successors(this_node) if not isinstance(n, elements.Transceiver))
|
||||
this_node_degree = {k: v for k, v in this_node.per_degree_pch_out_db.items()} if hasattr(this_node, 'per_degree_pch_out_db') else {}
|
||||
for oms in next_oms:
|
||||
# go through all the OMS departing from the Roadm
|
||||
node = roadm
|
||||
prev_node = roadm
|
||||
next_node = oms
|
||||
# go through all the OMS departing from the ROADM
|
||||
prev_node = this_node
|
||||
node = oms
|
||||
# if isinstance(next_node, elements.Fused): #support ROADM wo egress amp for metro applications
|
||||
# node = find_last_node(next_node)
|
||||
# next_node = next(n for n in network.successors(node))
|
||||
# next_node = find_last_node(next_node)
|
||||
prev_dp = getattr(node.params, 'target_pch_out_db', 0)
|
||||
if node.uid not in this_node_degree:
|
||||
# if no target power is defined on this degree or no per degree target power is given use the global one
|
||||
# if target_pch_out_db is not an attribute, then the element must be a transceiver
|
||||
this_node_degree[node.uid] = getattr(this_node.params, 'target_pch_out_db', 0)
|
||||
# use the target power on this degree
|
||||
prev_dp = this_node_degree[node.uid] - pref_ch_db
|
||||
dp = prev_dp
|
||||
prev_voa = 0
|
||||
voa = 0
|
||||
while True:
|
||||
visited_nodes = []
|
||||
while not (isinstance(node, elements.Roadm) or isinstance(node, elements.Transceiver)):
|
||||
# go through all nodes in the OMS (loop until next Roadm instance)
|
||||
try:
|
||||
next_node = next(network.successors(node))
|
||||
except StopIteration:
|
||||
raise NetworkTopologyError(f'{type(node).__name__} {node.uid} is not properly connected, please check network topology')
|
||||
visited_nodes.append(node)
|
||||
if next_node in visited_nodes:
|
||||
raise NetworkTopologyError(f'Loop detected for {type(node).__name__} {node.uid}, please check network topology')
|
||||
if isinstance(node, elements.Edfa):
|
||||
node_loss = span_loss(network, prev_node)
|
||||
voa = node.out_voa if node.out_voa else 0
|
||||
@@ -265,20 +279,20 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
dp = target_power(network, next_node, equipment)
|
||||
else:
|
||||
dp = node.delta_p
|
||||
gain_from_dp = node_loss + dp - prev_dp + prev_voa
|
||||
if node.effective_gain is None or power_mode:
|
||||
gain_target = gain_from_dp
|
||||
gain_target = node_loss + dp - prev_dp + prev_voa
|
||||
else: # gain mode with effective_gain
|
||||
gain_target = node.effective_gain
|
||||
dp = prev_dp - node_loss + gain_target
|
||||
dp = prev_dp - node_loss - prev_voa + gain_target
|
||||
|
||||
power_target = pref_total_db + dp
|
||||
|
||||
raman_allowed = False
|
||||
if isinstance(prev_node, elements.Fiber):
|
||||
max_fiber_lineic_loss_for_raman = \
|
||||
equipment['Span']['default'].max_fiber_lineic_loss_for_raman
|
||||
raman_allowed = prev_node.params.loss_coef < max_fiber_lineic_loss_for_raman
|
||||
else:
|
||||
raman_allowed = False
|
||||
|
||||
# implementation of restrictions on roadm boosters
|
||||
if isinstance(prev_node, elements.Roadm):
|
||||
@@ -303,19 +317,30 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
gain_target += power_reduction
|
||||
elif node.params.raman and not raman_allowed:
|
||||
print(f'{ansi_escapes.red}WARNING{ansi_escapes.reset}: raman is used in node {node.uid}\n but fiber lineic loss is above threshold\n')
|
||||
else:
|
||||
# if variety is imposed by user, and if the gain_target (computed or imposed) is also above
|
||||
# variety max gain + extended range, then warn that gain > max_gain + extended range
|
||||
if gain_target - equipment['Edfa'][node.params.type_variety].gain_flatmax - \
|
||||
equipment['Span']['default'].target_extended_gain > 1e-2:
|
||||
# 1e-2 to allow a small margin according to round2float min step
|
||||
print(f'{ansi_escapes.red}WARNING{ansi_escapes.reset}: '
|
||||
f'WARNING: effective gain in Node {node.uid} is above user '
|
||||
f'specified amplifier {node.params.type_variety}\n'
|
||||
f'max flat gain: {equipment["Edfa"][node.params.type_variety].gain_flatmax}dB ; '
|
||||
f'required gain: {gain_target}dB. Please check amplifier type.')
|
||||
|
||||
node.delta_p = dp if power_mode else None
|
||||
node.effective_gain = gain_target
|
||||
set_amplifier_voa(node, power_target, power_mode)
|
||||
if isinstance(next_node, elements.Roadm) or isinstance(next_node, elements.Transceiver):
|
||||
break
|
||||
|
||||
prev_dp = dp
|
||||
prev_voa = voa
|
||||
prev_node = node
|
||||
node = next_node
|
||||
# print(f'{node.uid}')
|
||||
next_node = next(n for n in network.successors(node))
|
||||
|
||||
if isinstance(this_node, elements.Roadm):
|
||||
this_node.per_degree_pch_out_db = {k: v for k, v in this_node_degree.items()}
|
||||
|
||||
def add_egress_amplifier(network, node):
|
||||
next_nodes = [n for n in network.successors(node)
|
||||
@@ -328,8 +353,8 @@ def add_egress_amplifier(network, node):
|
||||
params={},
|
||||
metadata={
|
||||
'location': {
|
||||
'latitude': (node.lat * 2 + next_node.lat * 2) / 4,
|
||||
'longitude': (node.lng * 2 + next_node.lng * 2) / 4,
|
||||
'latitude': (node.lat + next_node.lat) / 2,
|
||||
'longitude': (node.lng + next_node.lng) / 2,
|
||||
'city': node.loc.city,
|
||||
'region': node.loc.region,
|
||||
}
|
||||
@@ -351,24 +376,20 @@ def calculate_new_length(fiber_length, bounds, target_length):
|
||||
if fiber_length < bounds.stop:
|
||||
return fiber_length, 1
|
||||
|
||||
n_spans = int(fiber_length // target_length)
|
||||
n_spans2 = int(fiber_length // target_length)
|
||||
n_spans1 = n_spans2 + 1
|
||||
|
||||
length1 = fiber_length / (n_spans + 1)
|
||||
delta1 = target_length - length1
|
||||
result1 = (length1, n_spans + 1)
|
||||
|
||||
length2 = fiber_length / n_spans
|
||||
delta2 = length2 - target_length
|
||||
result2 = (length2, n_spans)
|
||||
length1 = fiber_length / n_spans1
|
||||
length2 = fiber_length / n_spans2
|
||||
|
||||
if (bounds.start <= length1 <= bounds.stop) and not(bounds.start <= length2 <= bounds.stop):
|
||||
result = result1
|
||||
return (length1, n_spans1)
|
||||
elif (bounds.start <= length2 <= bounds.stop) and not(bounds.start <= length1 <= bounds.stop):
|
||||
result = result2
|
||||
return (length2, n_spans2)
|
||||
elif target_length - length1 < length2 - target_length:
|
||||
return (length1, n_spans1)
|
||||
else:
|
||||
result = result1 if delta1 < delta2 else result2
|
||||
|
||||
return result
|
||||
return (length2, n_spans2)
|
||||
|
||||
|
||||
def split_fiber(network, fiber, bounds, target_length, equipment):
|
||||
@@ -386,9 +407,8 @@ def split_fiber(network, fiber, bounds, target_length, equipment):
|
||||
|
||||
fiber.params.length = new_length
|
||||
|
||||
f = interp1d([prev_node.lng, next_node.lng], [prev_node.lat, next_node.lat])
|
||||
xpos = [prev_node.lng + (next_node.lng - prev_node.lng) * (n + 1) / (n_spans + 1) for n in range(n_spans)]
|
||||
ypos = f(xpos)
|
||||
ypos = [prev_node.lat + (next_node.lat - prev_node.lat) * (n + 1) / (n_spans + 1) for n in range(n_spans)]
|
||||
for span, lng, lat in zip(range(n_spans), xpos, ypos):
|
||||
new_span = elements.Fiber(uid=f'{fiber.uid}_({span+1}/{n_spans})',
|
||||
type_variety=fiber.type_variety,
|
||||
@@ -416,11 +436,14 @@ def split_fiber(network, fiber, bounds, target_length, equipment):
|
||||
|
||||
def add_connector_loss(network, fibers, default_con_in, default_con_out, EOL):
|
||||
for fiber in fibers:
|
||||
try:
|
||||
next_node = next(network.successors(fiber))
|
||||
except StopIteration:
|
||||
raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology')
|
||||
if fiber.params.con_in is None:
|
||||
fiber.params.con_in = default_con_in
|
||||
if fiber.params.con_out is None:
|
||||
fiber.params.con_out = default_con_out
|
||||
next_node = next(n for n in network.successors(fiber))
|
||||
if not isinstance(next_node, elements.Fused):
|
||||
fiber.params.con_out += EOL
|
||||
|
||||
@@ -431,22 +454,21 @@ def add_fiber_padding(network, fibers, padding):
|
||||
for fiber in network.predecessors(n)
|
||||
if isinstance(fiber, elements.Fiber))"""
|
||||
for fiber in fibers:
|
||||
this_span_loss = span_loss(network, fiber)
|
||||
try:
|
||||
next_node = next(network.successors(fiber))
|
||||
except StopIteration:
|
||||
raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology')
|
||||
if this_span_loss < padding and not (isinstance(next_node, elements.Fused)):
|
||||
if isinstance(next_node, elements.Fused):
|
||||
continue
|
||||
this_span_loss = span_loss(network, fiber)
|
||||
if this_span_loss < padding:
|
||||
# add a padding att_in at the input of the 1st fiber:
|
||||
# address the case when several fibers are spliced together
|
||||
first_fiber = find_first_node(network, fiber)
|
||||
# in order to support no booster , fused might be placed
|
||||
# just after a roadm: need to check that first_fiber is really a fiber
|
||||
if isinstance(first_fiber, elements.Fiber):
|
||||
if first_fiber.params.att_in is None:
|
||||
first_fiber.params.att_in = padding - this_span_loss
|
||||
else:
|
||||
first_fiber.params.att_in = first_fiber.params.att_in + padding - this_span_loss
|
||||
first_fiber.params.att_in = first_fiber.params.att_in + padding - this_span_loss
|
||||
|
||||
|
||||
def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
@@ -455,14 +477,11 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
min_length = max(int(default_span_data.padding / 0.2 * 1e3), 50_000)
|
||||
bounds = range(min_length, max_length)
|
||||
target_length = max(min_length, 90_000)
|
||||
default_con_in = default_span_data.con_in
|
||||
default_con_out = default_span_data.con_out
|
||||
padding = default_span_data.padding
|
||||
|
||||
# set roadm loss for gain_mode before to build network
|
||||
fibers = [f for f in network.nodes() if isinstance(f, elements.Fiber)]
|
||||
add_connector_loss(network, fibers, default_con_in, default_con_out, default_span_data.EOL)
|
||||
add_fiber_padding(network, fibers, padding)
|
||||
add_connector_loss(network, fibers, default_span_data.con_in, default_span_data.con_out, default_span_data.EOL)
|
||||
add_fiber_padding(network, fibers, default_span_data.padding)
|
||||
# don't group split fiber and add amp in the same loop
|
||||
# =>for code clarity (at the expense of speed):
|
||||
for fiber in fibers:
|
||||
@@ -473,12 +492,12 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
for node in amplified_nodes:
|
||||
add_egress_amplifier(network, node)
|
||||
|
||||
roadms = [r for r in network.nodes() if isinstance(r, elements.Roadm)]
|
||||
roadms = [r for r in amplified_nodes if isinstance(r, elements.Roadm)]
|
||||
for roadm in roadms:
|
||||
set_egress_amplifier(network, roadm, equipment, pref_total_db)
|
||||
set_egress_amplifier(network, roadm, equipment, pref_ch_db, pref_total_db)
|
||||
|
||||
# support older json input topology wo Roadms:
|
||||
if len(roadms) == 0:
|
||||
trx = [t for t in network.nodes() if isinstance(t, elements.Transceiver)]
|
||||
for t in trx:
|
||||
set_egress_amplifier(network, t, equipment, pref_total_db)
|
||||
trx = [t for t in network.nodes() if isinstance(t, elements.Transceiver)]
|
||||
for t in trx:
|
||||
next_node = next(network.successors(t), None)
|
||||
if next_node and not isinstance(next_node, elements.Roadm):
|
||||
set_egress_amplifier(network, t, equipment, 0, pref_total_db)
|
||||
|
||||
@@ -6,13 +6,11 @@ gnpy.core.parameters
|
||||
====================
|
||||
|
||||
This module contains all parameters to configure standard network elements.
|
||||
|
||||
"""
|
||||
|
||||
from scipy.constants import c, pi
|
||||
from numpy import squeeze, log10, exp
|
||||
|
||||
|
||||
from gnpy.core.utils import db2lin, convert_length
|
||||
from gnpy.core.exceptions import ParametersError
|
||||
|
||||
|
||||
@@ -1,4 +1,17 @@
|
||||
import numpy as np
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
gnpy.core.science_utils
|
||||
=======================
|
||||
|
||||
Solver definitions to calculate the Raman effect and the nonlinear interference noise
|
||||
|
||||
The solvers take as input instances of the spectral information, the fiber and the simulation parameters
|
||||
"""
|
||||
|
||||
from numpy import interp, pi, zeros, shape, where, cos, reshape, array, append, ones, argsort, nan, exp, arange, sqrt, \
|
||||
empty, vstack, trapz, arcsinh, clip, abs, sum
|
||||
from operator import attrgetter
|
||||
from logging import getLogger
|
||||
import scipy.constants as ph
|
||||
@@ -11,7 +24,6 @@ from math import isclose
|
||||
from gnpy.core.utils import db2lin, lin2db
|
||||
from gnpy.core.exceptions import EquipmentConfigError
|
||||
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
@@ -65,7 +77,7 @@ def propagate_raman_fiber(fiber, *carriers):
|
||||
|
||||
new_carriers = []
|
||||
for carrier, attenuation, rmn_ase in zip(carriers, fiber_attenuation, raman_ase):
|
||||
carrier_nli = np.interp(carrier.frequency, nli_frequencies, computed_nli)
|
||||
carrier_nli = interp(carrier.frequency, nli_frequencies, computed_nli)
|
||||
pwr = carrier.power
|
||||
pwr = pwr._replace(signal=pwr.signal / attenuation / attenuation_out,
|
||||
nli=(pwr.nli + carrier_nli) / attenuation / attenuation_out,
|
||||
@@ -83,10 +95,10 @@ def frequency_resolution(carrier, carriers, sim_params, fiber):
|
||||
return res_dict[method], method, res_dict
|
||||
|
||||
def _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol):
|
||||
return k_tol * abs(alpha0) / abs(beta2) / (1 + delta_count) / (4 * np.pi ** 2 * grid_size)
|
||||
return k_tol * abs(alpha0) / abs(beta2) / (1 + delta_count) / (4 * pi ** 2 * grid_size)
|
||||
|
||||
def _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol):
|
||||
return phi_tol / abs(beta2) / (1 + delta_count) / delta_z / (4 * np.pi ** 2 * grid_size)
|
||||
return phi_tol / abs(beta2) / (1 + delta_count) / delta_z / (4 * pi ** 2 * grid_size)
|
||||
|
||||
grid_size = sim_params.nli_params.wdm_grid_size
|
||||
delta_z = sim_params.raman_params.space_resolution
|
||||
@@ -117,20 +129,20 @@ def raised_cosine_comb(f, *carriers):
|
||||
:param carriers: namedtuple describing the WDM comb
|
||||
:return: PSD of the WDM comb evaluated over f
|
||||
"""
|
||||
psd = np.zeros(np.shape(f))
|
||||
psd = zeros(shape(f))
|
||||
for carrier in carriers:
|
||||
f_nch = carrier.frequency
|
||||
g_ch = carrier.power.signal / carrier.baud_rate
|
||||
ts = 1 / carrier.baud_rate
|
||||
passband = (1 - carrier.roll_off) / (2 / carrier.baud_rate)
|
||||
stopband = (1 + carrier.roll_off) / (2 / carrier.baud_rate)
|
||||
ff = np.abs(f - f_nch)
|
||||
tf = ff - passband
|
||||
pass_band = (1 - carrier.roll_off) / (2 / carrier.baud_rate)
|
||||
stop_band = (1 + carrier.roll_off) / (2 / carrier.baud_rate)
|
||||
ff = abs(f - f_nch)
|
||||
tf = ff - pass_band
|
||||
if carrier.roll_off == 0:
|
||||
psd = np.where(tf <= 0, g_ch, 0.) + psd
|
||||
psd = where(tf <= 0, g_ch, 0.) + psd
|
||||
else:
|
||||
psd = g_ch * (np.where(tf <= 0, 1., 0.) + 1 / 2 * (1 + np.cos(np.pi * ts / carrier.roll_off * tf)) *
|
||||
np.where(tf > 0, 1., 0.) * np.where(np.abs(ff) <= stopband, 1., 0.)) + psd
|
||||
psd = g_ch * (where(tf <= 0, 1., 0.) + 1 / 2 * (1 + cos(pi * ts / carrier.roll_off * tf)) *
|
||||
where(tf > 0, 1., 0.) * where(abs(ff) <= stop_band, 1., 0.)) + psd
|
||||
return psd
|
||||
|
||||
|
||||
@@ -227,13 +239,13 @@ class RamanSolver:
|
||||
|
||||
alphap_fiber = self.fiber.alpha(freq_array)
|
||||
|
||||
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
|
||||
freq_diff = abs(freq_array - reshape(freq_array, (len(freq_array), 1)))
|
||||
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
|
||||
cr = interp_cr(freq_diff)
|
||||
|
||||
# z propagation axis
|
||||
z_array = self.stimulated_raman_scattering.z
|
||||
ase_bc = np.zeros(freq_array.shape)
|
||||
ase_bc = zeros(freq_array.shape)
|
||||
|
||||
# calculate ase power
|
||||
int_spontaneous_raman = self._int_spontaneous_raman(z_array, self._stimulated_raman_scattering.power,
|
||||
@@ -254,28 +266,28 @@ class RamanSolver:
|
||||
"""
|
||||
|
||||
# Signal power spectrum
|
||||
pow_array = np.array([])
|
||||
f_array = np.array([])
|
||||
noise_bandwidth_array = np.array([])
|
||||
pow_array = array([])
|
||||
f_array = array([])
|
||||
noise_bandwidth_array = array([])
|
||||
for carrier in sorted(carriers, key=attrgetter('frequency')):
|
||||
f_array = np.append(f_array, carrier.frequency)
|
||||
pow_array = np.append(pow_array, carrier.power.signal)
|
||||
f_array = append(f_array, carrier.frequency)
|
||||
pow_array = append(pow_array, carrier.power.signal)
|
||||
ref_bw = carrier.baud_rate
|
||||
noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw)
|
||||
noise_bandwidth_array = append(noise_bandwidth_array, ref_bw)
|
||||
|
||||
propagation_direction = np.ones(len(f_array))
|
||||
propagation_direction = ones(len(f_array))
|
||||
|
||||
# Raman pump power spectrum
|
||||
if raman_pumps:
|
||||
for pump in raman_pumps:
|
||||
pow_array = np.append(pow_array, pump.power)
|
||||
f_array = np.append(f_array, pump.frequency)
|
||||
pow_array = append(pow_array, pump.power)
|
||||
f_array = append(f_array, pump.frequency)
|
||||
direction = +1 if pump.propagation_direction.lower() == 'coprop' else -1
|
||||
propagation_direction = np.append(propagation_direction, direction)
|
||||
noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw)
|
||||
propagation_direction = append(propagation_direction, direction)
|
||||
noise_bandwidth_array = append(noise_bandwidth_array, ref_bw)
|
||||
|
||||
# Final sorting
|
||||
ind = np.argsort(f_array)
|
||||
ind = argsort(f_array)
|
||||
f_array = f_array[ind]
|
||||
pow_array = pow_array[ind]
|
||||
propagation_direction = propagation_direction[ind]
|
||||
@@ -293,27 +305,26 @@ class RamanSolver:
|
||||
h = ph.value('Planck constant')
|
||||
kb = ph.value('Boltzmann constant')
|
||||
|
||||
power_ase = np.nan * np.ones(raman_matrix.shape)
|
||||
power_ase = nan * ones(raman_matrix.shape)
|
||||
int_pump = cumtrapz(raman_matrix, z_array, dx=dx, axis=1, initial=0)
|
||||
|
||||
for f_ind, f_ase in enumerate(freq_array):
|
||||
cr_raman = cr_raman_matrix[f_ind, :]
|
||||
vibrational_loss = f_ase / freq_array[:f_ind]
|
||||
eta = 1 / (np.exp((h * freq_diff[f_ind, f_ind + 1:]) / (kb * temperature)) - 1)
|
||||
eta = 1 / (exp((h * freq_diff[f_ind, f_ind + 1:]) / (kb * temperature)) - 1)
|
||||
|
||||
int_fiber_loss = -alphap_fiber[f_ind] * z_array
|
||||
int_raman_loss = np.sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(),
|
||||
int_raman_loss = sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(),
|
||||
axis=0)
|
||||
int_raman_gain = np.sum((cr_raman[f_ind + 1:] * int_pump[f_ind + 1:, :].transpose()).transpose(), axis=0)
|
||||
int_raman_gain = sum((cr_raman[f_ind + 1:] * int_pump[f_ind + 1:, :].transpose()).transpose(), axis=0)
|
||||
|
||||
int_gain_loss = int_fiber_loss + int_raman_gain + int_raman_loss
|
||||
|
||||
new_ase = np.sum((cr_raman[f_ind + 1:] * (1 + eta) * raman_matrix[f_ind + 1:, :].transpose()).transpose()
|
||||
new_ase = sum((cr_raman[f_ind + 1:] * (1 + eta) * raman_matrix[f_ind + 1:, :].transpose()).transpose()
|
||||
* h * f_ase * bn_array[f_ind], axis=0)
|
||||
|
||||
bc_evolution = ase_bc[f_ind] * np.exp(int_gain_loss)
|
||||
ase_evolution = np.exp(int_gain_loss) * cumtrapz(new_ase *
|
||||
np.exp(-int_gain_loss), z_array, dx=dx, initial=0)
|
||||
bc_evolution = ase_bc[f_ind] * exp(int_gain_loss)
|
||||
ase_evolution = exp(int_gain_loss) * cumtrapz(new_ase * exp(-int_gain_loss), z_array, dx=dx, initial=0)
|
||||
|
||||
power_ase[f_ind, :] = bc_evolution + ase_evolution
|
||||
|
||||
@@ -332,7 +343,7 @@ class RamanSolver:
|
||||
sim_params = simulation.sim_params
|
||||
|
||||
if not sim_params.raman_params.flag_raman:
|
||||
raman_efficiency['cr'] = np.zeros(len(raman_efficiency['cr']))
|
||||
raman_efficiency['cr'] = zeros(len(raman_efficiency['cr']))
|
||||
# raman solver parameters
|
||||
z_resolution = sim_params.raman_params.space_resolution
|
||||
tolerance = sim_params.raman_params.tolerance
|
||||
@@ -343,12 +354,12 @@ class RamanSolver:
|
||||
|
||||
alphap_fiber = self.fiber.alpha(freq_array)
|
||||
|
||||
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
|
||||
freq_diff = abs(freq_array - reshape(freq_array, (len(freq_array), 1)))
|
||||
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
|
||||
cr = interp_cr(freq_diff)
|
||||
|
||||
# z propagation axis
|
||||
z = np.arange(0, fiber_length + 1, z_resolution)
|
||||
z = arange(0, fiber_length + 1, z_resolution)
|
||||
|
||||
def ode_function(z, p):
|
||||
return self._ode_stimulated_raman(z, p, alphap_fiber, freq_array, cr, prop_direct)
|
||||
@@ -362,14 +373,14 @@ class RamanSolver:
|
||||
bvp_solution = solve_bvp(ode_function, boundary_residual, z, initial_guess_conditions, tol=tolerance)
|
||||
|
||||
rho = (bvp_solution.y.transpose() / power_spectrum).transpose()
|
||||
rho = np.sqrt(rho) # From power attenuation to field attenuation
|
||||
rho = sqrt(rho) # From power attenuation to field attenuation
|
||||
stimulated_raman_scattering = StimulatedRamanScattering(freq_array, bvp_solution.x, rho, bvp_solution.y)
|
||||
|
||||
self._stimulated_raman_scattering = stimulated_raman_scattering
|
||||
|
||||
def _residuals_stimulated_raman(self, ya, yb, power_spectrum, prop_direct):
|
||||
|
||||
computed_boundary_value = np.zeros(ya.size)
|
||||
computed_boundary_value = zeros(ya.size)
|
||||
|
||||
for index, direction in enumerate(prop_direct):
|
||||
if direction == +1:
|
||||
@@ -393,12 +404,12 @@ class RamanSolver:
|
||||
the second ndarray index identifies the step in z. ndarray
|
||||
"""
|
||||
|
||||
power_guess = np.empty((power_spectrum.size, z.size))
|
||||
power_guess = empty((power_spectrum.size, z.size))
|
||||
for f_index, power_slice in enumerate(power_spectrum):
|
||||
if prop_direct[f_index] == +1:
|
||||
power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z) * power_slice
|
||||
power_guess[f_index, :] = exp(-alphap_fiber[f_index] * z) * power_slice
|
||||
else:
|
||||
power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z[::-1]) * power_slice
|
||||
power_guess[f_index, :] = exp(-alphap_fiber[f_index] * z[::-1]) * power_slice
|
||||
|
||||
return power_guess
|
||||
|
||||
@@ -419,26 +430,26 @@ class RamanSolver:
|
||||
:return: dP/dz: the power variation in dz [W/m]. numpy array. Size n
|
||||
"""
|
||||
|
||||
dpdz = np.nan * np.ones(power_spectrum.shape)
|
||||
dpdz = nan * ones(power_spectrum.shape)
|
||||
for f_ind, power in enumerate(power_spectrum):
|
||||
cr_raman = cr_raman_matrix[f_ind, :]
|
||||
vibrational_loss = freq_array[f_ind] / freq_array[:f_ind]
|
||||
|
||||
for z_ind, power_sample in enumerate(power):
|
||||
raman_gain = np.sum(cr_raman[f_ind + 1:] * power_spectrum[f_ind + 1:, z_ind])
|
||||
raman_loss = np.sum(vibrational_loss * cr_raman[:f_ind] * power_spectrum[:f_ind, z_ind])
|
||||
raman_gain = sum(cr_raman[f_ind + 1:] * power_spectrum[f_ind + 1:, z_ind])
|
||||
raman_loss = sum(vibrational_loss * cr_raman[:f_ind] * power_spectrum[:f_ind, z_ind])
|
||||
|
||||
dpdz_element = prop_direct[f_ind] * (-alphap_fiber[f_ind] + raman_gain - raman_loss) * power_sample
|
||||
dpdz[f_ind][z_ind] = dpdz_element
|
||||
|
||||
return np.vstack(dpdz)
|
||||
return vstack(dpdz)
|
||||
|
||||
|
||||
class NliSolver:
|
||||
""" This class implements the NLI models.
|
||||
Model and method can be specified in `sim_params.nli_params.method`.
|
||||
List of implemented methods:
|
||||
'gn_model_analytic': brute force triple integral solution
|
||||
'gn_model_analytic': eq. 120 from arXiv:1209.0394
|
||||
'ggn_spectrally_separated_xpm_spm': XPM plus SPM
|
||||
"""
|
||||
|
||||
@@ -488,38 +499,38 @@ class NliSolver:
|
||||
|
||||
return carrier_nli
|
||||
|
||||
def _compute_eta_matrix(self, carrier_cut, *carriers):
|
||||
cut_index = carrier_cut.channel_number - 1
|
||||
def _compute_eta_matrix(self, cut_carrier, *carriers):
|
||||
cut_index = cut_carrier.channel_number - 1
|
||||
simulation = Simulation.get_simulation()
|
||||
sim_params = simulation.sim_params
|
||||
# Matrix initialization
|
||||
matrix_size = max(carriers, key=lambda x: getattr(x, 'channel_number')).channel_number
|
||||
eta_matrix = np.zeros(shape=(matrix_size, matrix_size))
|
||||
eta_matrix = zeros(shape=(matrix_size, matrix_size))
|
||||
|
||||
# SPM
|
||||
logger.debug(f'Start computing SPM on channel #{carrier_cut.channel_number}')
|
||||
logger.debug(f'Start computing SPM on channel #{cut_carrier.channel_number}')
|
||||
# SPM GGN
|
||||
if 'ggn' in sim_params.nli_params.nli_method_name.lower():
|
||||
partial_nli = self._generalized_spectrally_separated_spm(carrier_cut)
|
||||
partial_nli = self._generalized_spectrally_separated_spm(cut_carrier)
|
||||
# SPM GN
|
||||
elif 'gn' in sim_params.nli_params.nli_method_name.lower():
|
||||
partial_nli = self._gn_analytic(carrier_cut, *[carrier_cut])
|
||||
eta_matrix[cut_index, cut_index] = partial_nli / (carrier_cut.power.signal**3)
|
||||
partial_nli = self._gn_analytic(cut_carrier, *[cut_carrier])
|
||||
eta_matrix[cut_index, cut_index] = partial_nli / (cut_carrier.power.signal**3)
|
||||
|
||||
# XPM
|
||||
for pump_carrier in carriers:
|
||||
pump_index = pump_carrier.channel_number - 1
|
||||
if not (cut_index == pump_index):
|
||||
logger.debug(f'Start computing XPM on channel #{carrier_cut.channel_number} '
|
||||
logger.debug(f'Start computing XPM on channel #{cut_carrier.channel_number} '
|
||||
f'from channel #{pump_carrier.channel_number}')
|
||||
# XPM GGN
|
||||
if 'ggn' in sim_params.nli_params.nli_method_name.lower():
|
||||
partial_nli = self._generalized_spectrally_separated_xpm(carrier_cut, pump_carrier)
|
||||
partial_nli = self._generalized_spectrally_separated_xpm(cut_carrier, pump_carrier)
|
||||
# XPM GGN
|
||||
elif 'gn' in sim_params.nli_params.nli_method_name.lower():
|
||||
partial_nli = self._gn_analytic(carrier_cut, *[pump_carrier])
|
||||
eta_matrix[pump_index, pump_index] = partial_nli /\
|
||||
(carrier_cut.power.signal * pump_carrier.power.signal**2)
|
||||
partial_nli = self._gn_analytic(cut_carrier, *[pump_carrier])
|
||||
eta_matrix[pump_index, pump_index] = \
|
||||
partial_nli / (cut_carrier.power.signal * pump_carrier.power.signal**2)
|
||||
return eta_matrix
|
||||
|
||||
# Methods for computing GN-model
|
||||
@@ -537,12 +548,12 @@ class NliSolver:
|
||||
|
||||
g_nli = 0
|
||||
for interfering_carrier in carriers:
|
||||
g_interfearing = interfering_carrier.power.signal / interfering_carrier.baud_rate
|
||||
g_interfering = interfering_carrier.power.signal / interfering_carrier.baud_rate
|
||||
g_signal = carrier.power.signal / carrier.baud_rate
|
||||
g_nli += g_interfearing**2 * g_signal \
|
||||
g_nli += g_interfering**2 * g_signal \
|
||||
* _psi(carrier, interfering_carrier, beta2=beta2, asymptotic_length=asymptotic_length)
|
||||
g_nli *= (16.0 / 27.0) * (gamma * effective_length) ** 2 /\
|
||||
(2 * np.pi * abs(beta2) * asymptotic_length)
|
||||
(2 * pi * abs(beta2) * asymptotic_length)
|
||||
carrier_nli = carrier.baud_rate * g_nli
|
||||
return carrier_nli
|
||||
|
||||
@@ -559,26 +570,26 @@ class NliSolver:
|
||||
self._generalized_psi(carrier, carrier, f_eval, f_cut_resolution, f_cut_resolution)
|
||||
return spm_nli
|
||||
|
||||
def _generalized_spectrally_separated_xpm(self, carrier_cut, pump_carrier):
|
||||
def _generalized_spectrally_separated_xpm(self, cut_carrier, pump_carrier):
|
||||
gamma = self.fiber.params.gamma
|
||||
simulation = Simulation.get_simulation()
|
||||
sim_params = simulation.sim_params
|
||||
delta_index = pump_carrier.channel_number - carrier_cut.channel_number
|
||||
delta_index = pump_carrier.channel_number - cut_carrier.channel_number
|
||||
f_cut_resolution = sim_params.nli_params.f_cut_resolution[f'delta_{delta_index}']
|
||||
f_pump_resolution = sim_params.nli_params.f_pump_resolution
|
||||
f_eval = carrier_cut.frequency
|
||||
f_eval = cut_carrier.frequency
|
||||
g_pump = (pump_carrier.power.signal / pump_carrier.baud_rate)
|
||||
g_cut = (carrier_cut.power.signal / carrier_cut.baud_rate)
|
||||
g_cut = (cut_carrier.power.signal / cut_carrier.baud_rate)
|
||||
frequency_offset_threshold = self._frequency_offset_threshold(pump_carrier.baud_rate)
|
||||
if abs(carrier_cut.frequency - pump_carrier.frequency) <= frequency_offset_threshold:
|
||||
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * gamma ** 2 * g_pump**2 * g_cut * \
|
||||
2 * self._generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution)
|
||||
if abs(cut_carrier.frequency - pump_carrier.frequency) <= frequency_offset_threshold:
|
||||
xpm_nli = cut_carrier.baud_rate * (16.0 / 27.0) * gamma ** 2 * g_pump**2 * g_cut * \
|
||||
2 * self._generalized_psi(cut_carrier, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution)
|
||||
else:
|
||||
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * gamma ** 2 * g_pump**2 * g_cut * \
|
||||
2 * self._fast_generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution)
|
||||
xpm_nli = cut_carrier.baud_rate * (16.0 / 27.0) * gamma ** 2 * g_pump**2 * g_cut * \
|
||||
2 * self._fast_generalized_psi(cut_carrier, pump_carrier, f_eval, f_cut_resolution)
|
||||
return xpm_nli
|
||||
|
||||
def _fast_generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution):
|
||||
def _fast_generalized_psi(self, cut_carrier, pump_carrier, f_eval, f_cut_resolution):
|
||||
""" It computes the generalized psi function similarly to the one used in the GN model
|
||||
:return: generalized_psi
|
||||
"""
|
||||
@@ -589,29 +600,29 @@ class NliSolver:
|
||||
f_ref_beta = self.fiber.params.ref_frequency
|
||||
z = self.stimulated_raman_scattering.z
|
||||
frequency_rho = self.stimulated_raman_scattering.frequency
|
||||
rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
|
||||
rho_norm = self.stimulated_raman_scattering.rho * exp(abs(alpha0) * z / 2)
|
||||
if len(frequency_rho) == 1:
|
||||
def rho_function(f): return rho_norm[0, :]
|
||||
else:
|
||||
rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate')
|
||||
rho_norm_pump = rho_function(pump_carrier.frequency)
|
||||
|
||||
f1_array = np.array([pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
|
||||
pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2)])
|
||||
f2_array = np.arange(carrier_cut.frequency,
|
||||
carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
|
||||
f_cut_resolution) # Only positive f2 is used since integrand_f2 is symmetric
|
||||
f1_array = array([pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
|
||||
pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2)])
|
||||
f2_array = arange(cut_carrier.frequency,
|
||||
cut_carrier.frequency + (cut_carrier.baud_rate * (1 + cut_carrier.roll_off) / 2),
|
||||
f_cut_resolution) # Only positive f2 is used since integrand_f2 is symmetric
|
||||
|
||||
integrand_f1 = np.zeros(len(f1_array))
|
||||
integrand_f1 = zeros(len(f1_array))
|
||||
for f1_index, f1 in enumerate(f1_array):
|
||||
delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
|
||||
(beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
|
||||
delta_beta = 4 * pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
|
||||
(beta2 + pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
|
||||
integrand_f2 = self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0)
|
||||
integrand_f1[f1_index] = 2 * np.trapz(integrand_f2, f2_array) # 2x since integrand_f2 is symmetric in f2
|
||||
integrand_f1[f1_index] = 2 * trapz(integrand_f2, f2_array) # 2x since integrand_f2 is symmetric in f2
|
||||
generalized_psi = 0.5 * sum(integrand_f1) * pump_carrier.baud_rate
|
||||
return generalized_psi
|
||||
|
||||
def _generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution):
|
||||
def _generalized_psi(self, cut_carrier, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution):
|
||||
""" It computes the generalized psi function similarly to the one used in the GN model
|
||||
:return: generalized_psi
|
||||
"""
|
||||
@@ -622,44 +633,44 @@ class NliSolver:
|
||||
f_ref_beta = self.fiber.params.ref_frequency
|
||||
z = self.stimulated_raman_scattering.z
|
||||
frequency_rho = self.stimulated_raman_scattering.frequency
|
||||
rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
|
||||
rho_norm = self.stimulated_raman_scattering.rho * exp(abs(alpha0) * z / 2)
|
||||
if len(frequency_rho) == 1:
|
||||
def rho_function(f): return rho_norm[0, :]
|
||||
else:
|
||||
rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate')
|
||||
rho_norm_pump = rho_function(pump_carrier.frequency)
|
||||
|
||||
f1_array = np.arange(pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
|
||||
pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
|
||||
f_pump_resolution)
|
||||
f2_array = np.arange(carrier_cut.frequency - (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
|
||||
carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
|
||||
f_cut_resolution)
|
||||
f1_array = arange(pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
|
||||
pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
|
||||
f_pump_resolution)
|
||||
f2_array = arange(cut_carrier.frequency - (cut_carrier.baud_rate * (1 + cut_carrier.roll_off) / 2),
|
||||
cut_carrier.frequency + (cut_carrier.baud_rate * (1 + cut_carrier.roll_off) / 2),
|
||||
f_cut_resolution)
|
||||
psd1 = raised_cosine_comb(f1_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal)
|
||||
|
||||
integrand_f1 = np.zeros(len(f1_array))
|
||||
integrand_f1 = zeros(len(f1_array))
|
||||
for f1_index, (f1, psd1_sample) in enumerate(zip(f1_array, psd1)):
|
||||
f3_array = f1 + f2_array - f_eval
|
||||
psd2 = raised_cosine_comb(f2_array, carrier_cut) * (carrier_cut.baud_rate / carrier_cut.power.signal)
|
||||
psd2 = raised_cosine_comb(f2_array, cut_carrier) * (cut_carrier.baud_rate / cut_carrier.power.signal)
|
||||
psd3 = raised_cosine_comb(f3_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal)
|
||||
ggg = psd1_sample * psd2 * psd3
|
||||
|
||||
delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
|
||||
(beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
|
||||
delta_beta = 4 * pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
|
||||
(beta2 + pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
|
||||
|
||||
integrand_f2 = ggg * self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0)
|
||||
integrand_f1[f1_index] = np.trapz(integrand_f2, f2_array)
|
||||
generalized_psi = np.trapz(integrand_f1, f1_array)
|
||||
integrand_f1[f1_index] = trapz(integrand_f2, f2_array)
|
||||
generalized_psi = trapz(integrand_f1, f1_array)
|
||||
return generalized_psi
|
||||
|
||||
@staticmethod
|
||||
def _generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0):
|
||||
w = 1j * delta_beta - alpha0
|
||||
generalized_rho_nli = (rho_norm_pump[-1]**2 * np.exp(w * z[-1]) - rho_norm_pump[0]**2 * np.exp(w * z[0])) / w
|
||||
generalized_rho_nli = (rho_norm_pump[-1]**2 * exp(w * z[-1]) - rho_norm_pump[0]**2 * exp(w * z[0])) / w
|
||||
for z_ind in range(0, len(z) - 1):
|
||||
derivative_rho = (rho_norm_pump[z_ind + 1]**2 - rho_norm_pump[z_ind]**2) / (z[z_ind + 1] - z[z_ind])
|
||||
generalized_rho_nli -= derivative_rho * (np.exp(w * z[z_ind + 1]) - np.exp(w * z[z_ind])) / (w**2)
|
||||
generalized_rho_nli = np.abs(generalized_rho_nli)**2
|
||||
generalized_rho_nli -= derivative_rho * (exp(w * z[z_ind + 1]) - exp(w * z[z_ind])) / (w**2)
|
||||
generalized_rho_nli = abs(generalized_rho_nli)**2
|
||||
return generalized_rho_nli
|
||||
|
||||
def _frequency_offset_threshold(self, symbol_rate):
|
||||
@@ -674,15 +685,14 @@ class NliSolver:
|
||||
|
||||
def _psi(carrier, interfering_carrier, beta2, asymptotic_length):
|
||||
"""Calculates eq. 123 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__"""
|
||||
|
||||
if carrier.channel_number == interfering_carrier.channel_number: # SCI, SPM
|
||||
psi = np.arcsinh(0.5 * np.pi**2 * asymptotic_length * abs(beta2) * carrier.baud_rate**2)
|
||||
psi = arcsinh(0.5 * pi**2 * asymptotic_length * abs(beta2) * carrier.baud_rate**2)
|
||||
else: # XCI, XPM
|
||||
delta_f = carrier.frequency - interfering_carrier.frequency
|
||||
psi = np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) *
|
||||
carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
|
||||
psi -= np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) *
|
||||
carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
|
||||
psi = arcsinh(pi**2 * asymptotic_length * abs(beta2) *
|
||||
carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
|
||||
psi -= arcsinh(pi**2 * asymptotic_length * abs(beta2) *
|
||||
carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
|
||||
return psi
|
||||
|
||||
|
||||
@@ -712,7 +722,7 @@ def estimate_nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
|
||||
# nf2 should be nf1 + 0.3 < nf2 < nf1 + 2
|
||||
# If not, recompute and check delta_p
|
||||
if not nf1 + 0.3 < nf2 < nf1 + 2:
|
||||
nf2 = np.clip(nf2, nf1 + 0.3, nf1 + 2)
|
||||
nf2 = clip(nf2, nf1 + 0.3, nf1 + 2)
|
||||
g1a_max = lin2db(db2lin(nf2) / (db2lin(nf_min) - db2lin(nf1)))
|
||||
delta_p = gain_max - g1a_max
|
||||
g1a_min = gain_min - (gain_max - gain_min) - delta_p
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
'''
|
||||
"""
|
||||
gnpy.core.utils
|
||||
===============
|
||||
|
||||
This module contains utility functions that are used with gnpy.
|
||||
'''
|
||||
|
||||
"""
|
||||
|
||||
from csv import writer
|
||||
import numpy as np
|
||||
from numpy import pi, cos, sqrt, log10
|
||||
from numpy import pi, cos, sqrt, log10, linspace, zeros, shape, where, logical_and
|
||||
from scipy import constants
|
||||
|
||||
from gnpy.core.exceptions import ConfigurationError
|
||||
|
||||
|
||||
@@ -70,7 +69,7 @@ def arrange_frequencies(length, start, stop):
|
||||
:return: an array of frequencies determined by the spacing parameter
|
||||
:rtype: numpy.ndarray
|
||||
"""
|
||||
return np.linspace(start, stop, length)
|
||||
return linspace(start, stop, length)
|
||||
|
||||
|
||||
def lin2db(value):
|
||||
@@ -190,12 +189,12 @@ def rrc(ffs, baud_rate, alpha):
|
||||
Ts = 1 / baud_rate
|
||||
l_lim = (1 - alpha) / (2 * Ts)
|
||||
r_lim = (1 + alpha) / (2 * Ts)
|
||||
hf = np.zeros(np.shape(ffs))
|
||||
slope_inds = np.where(
|
||||
np.logical_and(np.abs(ffs) > l_lim, np.abs(ffs) < r_lim))
|
||||
hf = zeros(shape(ffs))
|
||||
slope_inds = where(
|
||||
logical_and(abs(ffs) > l_lim, abs(ffs) < r_lim))
|
||||
hf[slope_inds] = 0.5 * (1 + cos((pi * Ts / alpha) *
|
||||
(np.abs(ffs[slope_inds]) - l_lim)))
|
||||
p_inds = np.where(np.logical_and(np.abs(ffs) > 0, np.abs(ffs) < l_lim))
|
||||
(abs(ffs[slope_inds]) - l_lim)))
|
||||
p_inds = where(logical_and(abs(ffs) > 0, abs(ffs) < l_lim))
|
||||
hf[p_inds] = 1
|
||||
return sqrt(hf)
|
||||
|
||||
|
||||
@@ -635,7 +635,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -643,6 +643,21 @@
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lorient_KMA to Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lannion_CAS to Stbrieuc",
|
||||
"metadata": {
|
||||
@@ -654,7 +669,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -692,7 +707,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -711,7 +726,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -730,7 +745,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -749,7 +764,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -768,7 +783,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -787,7 +802,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_high_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -882,7 +897,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_high_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -901,7 +916,7 @@
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
@@ -946,21 +961,6 @@
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lorient_KMA to Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
@@ -1245,4 +1245,4 @@
|
||||
"to_node": "trx Brest_KLA"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -14,8 +14,8 @@
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -39,8 +39,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -104,8 +104,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -129,8 +129,8 @@
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
@@ -154,8 +154,8 @@
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
@@ -179,8 +179,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -204,8 +204,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -229,8 +229,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
|
||||
@@ -27,7 +27,7 @@ from gnpy.core.utils import db2lin, lin2db, automatic_nch
|
||||
from gnpy.topology.request import (ResultElement, jsontocsv, compute_path_dsjctn, requests_aggregation,
|
||||
BLOCKING_NOPATH, correct_json_route_list,
|
||||
deduplicate_disjunctions, compute_path_with_disjunction,
|
||||
PathRequest, compute_constrained_path, propagate2)
|
||||
PathRequest, compute_constrained_path, propagate)
|
||||
from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum
|
||||
from gnpy.tools.json_io import load_equipment, load_network, load_json, load_requests, save_network, \
|
||||
requests_from_json, disjunctions_from_json, save_json
|
||||
@@ -188,6 +188,8 @@ def transmission_main_example(args=None):
|
||||
params['loose_list'] = ['strict']
|
||||
params['format'] = ''
|
||||
params['path_bandwidth'] = 0
|
||||
params['effective_freq_slot'] = None
|
||||
params['blocking_reason'] = None
|
||||
trx_params = trx_mode_params(equipment)
|
||||
if args.power:
|
||||
trx_params['power'] = db2lin(float(args.power)) * 1e-3
|
||||
@@ -200,7 +202,14 @@ def transmission_main_example(args=None):
|
||||
|
||||
pref_ch_db = lin2db(req.power * 1e3) # reference channel power / span (SL=20dB)
|
||||
pref_total_db = pref_ch_db + lin2db(req.nb_channel) # reference total power / span (SL=20dB)
|
||||
build_network(network, equipment, pref_ch_db, pref_total_db)
|
||||
try:
|
||||
build_network(network, equipment, pref_ch_db, pref_total_db)
|
||||
except exceptions.NetworkTopologyError as e:
|
||||
print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}')
|
||||
sys.exit(1)
|
||||
except exceptions.ConfigurationError as e:
|
||||
print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}')
|
||||
sys.exit(1)
|
||||
path = compute_constrained_path(network, req)
|
||||
|
||||
spans = [s.params.length for s in path if isinstance(s, RamanFiber) or isinstance(s, Fiber)]
|
||||
@@ -225,7 +234,7 @@ def transmission_main_example(args=None):
|
||||
print(f'\nPropagating with input power = {ansi_escapes.cyan}{lin2db(req.power*1e3):.2f} dBm{ansi_escapes.reset}:')
|
||||
else:
|
||||
print(f'\nPropagating in {ansi_escapes.cyan}gain mode{ansi_escapes.reset}: power cannot be set manually')
|
||||
infos = propagate2(path, req, equipment)
|
||||
infos = propagate(path, req, equipment)
|
||||
if len(power_range) == 1:
|
||||
for elem in path:
|
||||
print(elem)
|
||||
@@ -237,11 +246,6 @@ def transmission_main_example(args=None):
|
||||
else:
|
||||
print(path[-1])
|
||||
|
||||
# print(f'\n !!!!!!!!!!!!!!!!! TEST POINT !!!!!!!!!!!!!!!!!!!!!')
|
||||
# print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}')
|
||||
# => use "in" or "out" parameter
|
||||
# => use "nli" or "ase" or "signal" or "total" parameter
|
||||
|
||||
if args.save_network is not None:
|
||||
save_network(network, args.save_network)
|
||||
print(f'{ansi_escapes.blue}Network (after autodesign) saved to {args.save_network}{ansi_escapes.reset}')
|
||||
@@ -257,7 +261,7 @@ def transmission_main_example(args=None):
|
||||
'SNR NLI (signal bw, dB)',
|
||||
'SNR total (signal bw, dB)'))
|
||||
for final_carrier, ch_osnr, ch_snr_nl, ch_snr in zip(
|
||||
infos[path[-1]][1].carriers, path[-1].osnr_ase, path[-1].osnr_nli, path[-1].snr):
|
||||
infos.carriers, path[-1].osnr_ase, path[-1].osnr_nli, path[-1].snr):
|
||||
ch_freq = final_carrier.frequency * 1e-12
|
||||
ch_power = lin2db(final_carrier.power.signal * 1e3)
|
||||
print(
|
||||
@@ -280,7 +284,7 @@ def transmission_main_example(args=None):
|
||||
print(f'\n(Invalid destination node {args.destination!r} replaced with {destination.uid})')
|
||||
|
||||
if args.plot:
|
||||
plot_results(network, path, source, destination, infos)
|
||||
plot_results(network, path, source, destination)
|
||||
|
||||
|
||||
def _path_result_json(pathresult):
|
||||
@@ -317,7 +321,14 @@ def path_requests_run(args=None):
|
||||
|
||||
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)
|
||||
try:
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
except exceptions.NetworkTopologyError as e:
|
||||
print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}')
|
||||
sys.exit(1)
|
||||
except exceptions.ConfigurationError as e:
|
||||
print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}')
|
||||
sys.exit(1)
|
||||
if args.save_network is not None:
|
||||
save_network(network, args.save_network)
|
||||
print(f'{ansi_escapes.blue}Network (after autodesign) saved to {args.save_network}{ansi_escapes.reset}')
|
||||
@@ -401,7 +412,8 @@ def path_requests_run(args=None):
|
||||
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)}',
|
||||
psnr, f'{rqs[i].OSNR + equipment["SI"]["default"].sys_margins}',
|
||||
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)
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ In the "Links" sheet, only the first three columns ("Node A", "Node Z" and
|
||||
the "east" information so that it is possible to input undirected data.
|
||||
"""
|
||||
|
||||
from sys import exit
|
||||
from xlrd import open_workbook
|
||||
from argparse import ArgumentParser
|
||||
from collections import namedtuple, Counter, defaultdict
|
||||
@@ -124,6 +123,23 @@ class Eqpt(object):
|
||||
}
|
||||
|
||||
|
||||
class Roadm(object):
|
||||
def __init__(self, **kwargs):
|
||||
super(Roadm, self).__init__()
|
||||
self.update_attr(kwargs)
|
||||
|
||||
def update_attr(self, kwargs):
|
||||
clean_kwargs = {k: v for k, v in kwargs.items() if v != ''}
|
||||
for k, v in self.default_values.items():
|
||||
v = clean_kwargs.get(k, v)
|
||||
setattr(self, k, v)
|
||||
|
||||
default_values = {'from_node': '',
|
||||
'to_node': '',
|
||||
'target_pch_out_db': None
|
||||
}
|
||||
|
||||
|
||||
def read_header(my_sheet, line, slice_):
|
||||
""" return the list of headers !:= ''
|
||||
header_i = [(header, header_column_index), ...]
|
||||
@@ -169,7 +185,7 @@ def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in):
|
||||
if slice_out == (-1, -1):
|
||||
if h0 in ('east', 'Node A', 'Node Z', 'City'):
|
||||
print(f'{ansi_escapes.red}CRITICAL{ansi_escapes.reset}: missing _{h0}_ header: EXECUTION ENDS')
|
||||
exit()
|
||||
raise NetworkTopologyError(f'Missing _{h0}_ header')
|
||||
else:
|
||||
print(f'missing header {h0}')
|
||||
elif not isinstance(input_headers_dict[h0], dict):
|
||||
@@ -178,7 +194,7 @@ def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in):
|
||||
headers = parse_headers(my_sheet, input_headers_dict[h0], headers, start_line + 1, slice_out)
|
||||
if headers == {}:
|
||||
print(f'{ansi_escapes.red}CRITICAL ERROR{ansi_escapes.reset}: could not find any header to read _ ABORT')
|
||||
exit()
|
||||
raise NetworkTopologyError('Could not find any header to read')
|
||||
return headers
|
||||
|
||||
|
||||
@@ -193,6 +209,10 @@ def parse_sheet(my_sheet, input_headers_dict, header_line, start_line, column):
|
||||
yield parse_row(row[0: column], headers)
|
||||
|
||||
|
||||
def _format_items(items):
|
||||
return '\n'.join(f' - {item}' for item in items)
|
||||
|
||||
|
||||
def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
|
||||
duplicate_links = []
|
||||
@@ -206,19 +226,23 @@ def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
for l in duplicate_links:
|
||||
links.remove(l)
|
||||
|
||||
try:
|
||||
test_nodes = [n for n in nodes_by_city if n not in links_by_city]
|
||||
test_links = [n for n in links_by_city if n not in nodes_by_city]
|
||||
test_eqpts = [n for n in eqpts_by_city if n not in nodes_by_city]
|
||||
assert (test_nodes == [] or test_nodes == [''])\
|
||||
and (test_links == [] or test_links == [''])\
|
||||
and (test_eqpts == [] or test_eqpts == [''])
|
||||
except AssertionError:
|
||||
msg = f'CRITICAL error in excel input: Names in Nodes and Links sheets do no match, check:\
|
||||
\n{test_nodes} in Nodes sheet\
|
||||
\n{test_links} in Links sheet\
|
||||
\n{test_eqpts} in Eqpt sheet'
|
||||
raise NetworkTopologyError(msg)
|
||||
unreferenced_nodes = [n for n in nodes_by_city if n not in links_by_city]
|
||||
if unreferenced_nodes:
|
||||
raise NetworkTopologyError(f'{ansi_escapes.red}XLS error:{ansi_escapes.reset} The following nodes are not '
|
||||
f'referenced from the {ansi_escapes.cyan}Links{ansi_escapes.reset} sheet. '
|
||||
f'If unused, remove them from the {ansi_escapes.cyan}Nodes{ansi_escapes.reset} '
|
||||
f'sheet:\n'
|
||||
+ _format_items(unreferenced_nodes))
|
||||
# no need to check "Links" for invalid nodes because that's already in parse_excel()
|
||||
wrong_eqpt_from = [n for n in eqpts_by_city if n not in nodes_by_city]
|
||||
wrong_eqpt_to = [n.to_city for destinations in eqpts_by_city.values()
|
||||
for n in destinations if n.to_city not in nodes_by_city]
|
||||
wrong_eqpt = wrong_eqpt_from + wrong_eqpt_to
|
||||
if wrong_eqpt:
|
||||
raise NetworkTopologyError(f'{ansi_escapes.red}XLS error:{ansi_escapes.reset} '
|
||||
f'The {ansi_escapes.cyan}Eqpt{ansi_escapes.reset} sheet refers to nodes that '
|
||||
f'are not defined in the {ansi_escapes.cyan}Nodes{ansi_escapes.reset} sheet:\n'
|
||||
+ _format_items(wrong_eqpt))
|
||||
|
||||
for city, link in links_by_city.items():
|
||||
if nodes_by_city[city].node_type.lower() == 'ila' and len(link) != 2:
|
||||
@@ -226,7 +250,7 @@ def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
# => correct to make it a ROADM and remove entry in links_by_city
|
||||
# TODO: put in log rather than print
|
||||
print(f'invalid node type ({nodes_by_city[city].node_type})\
|
||||
specified in {city}, replaced by ROADM')
|
||||
specified in {city}, replaced by ROADM')
|
||||
nodes_by_city[city].node_type = 'ROADM'
|
||||
for n in nodes:
|
||||
if n.city == city:
|
||||
@@ -234,8 +258,98 @@ def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
return nodes, links
|
||||
|
||||
|
||||
def create_roadm_element(node, roadms_by_city):
|
||||
""" create the json element for a roadm node, including the different cases:
|
||||
- if there are restrictions
|
||||
- if there are per degree target power defined on a direction
|
||||
direction is defined by the booster name, so that booster must also be created in eqpt sheet
|
||||
if the direction is defined in roadm
|
||||
"""
|
||||
roadm = {'uid': f'roadm {node.city}'}
|
||||
if node.preamp_restriction != '' or node.booster_restriction != '':
|
||||
roadm['params'] = {
|
||||
'restrictions': {
|
||||
'preamp_variety_list': silent_remove(node.preamp_restriction.split(' | '), ''),
|
||||
'booster_variety_list': silent_remove(node.booster_restriction.split(' | '), '')}
|
||||
}
|
||||
if node.city in roadms_by_city.keys():
|
||||
if 'params' not in roadm.keys():
|
||||
roadm['params'] = {}
|
||||
roadm['params']['per_degree_pch_out_db'] = {}
|
||||
for elem in roadms_by_city[node.city]:
|
||||
to_node = f'east edfa in {node.city} to {elem.to_node}'
|
||||
if elem.target_pch_out_db is not None:
|
||||
roadm['params']['per_degree_pch_out_db'][to_node] = elem.target_pch_out_db
|
||||
roadm['metadata'] = {'location': {'city': node.city,
|
||||
'region': node.region,
|
||||
'latitude': node.latitude,
|
||||
'longitude': node.longitude}}
|
||||
roadm['type'] = 'Roadm'
|
||||
return roadm
|
||||
|
||||
|
||||
def create_east_eqpt_element(node):
|
||||
""" create amplifiers json elements for the east direction.
|
||||
this includes the case where the case of a fused element defined instead of an
|
||||
ILA in eqpt sheet
|
||||
"""
|
||||
eqpt = {'uid': f'east edfa in {node.from_city} to {node.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[node.from_city].city,
|
||||
'region': nodes_by_city[node.from_city].region,
|
||||
'latitude': nodes_by_city[node.from_city].latitude,
|
||||
'longitude': nodes_by_city[node.from_city].longitude}}}
|
||||
if node.east_amp_type.lower() != '' and node.east_amp_type.lower() != 'fused':
|
||||
eqpt['type'] = 'Edfa'
|
||||
eqpt['type_variety'] = f'{node.east_amp_type}'
|
||||
eqpt['operational'] = {'gain_target': node.east_amp_gain,
|
||||
'delta_p': node.east_amp_dp,
|
||||
'tilt_target': node.east_tilt,
|
||||
'out_voa': node.east_att_out}
|
||||
elif node.east_amp_type.lower() == '':
|
||||
eqpt['type'] = 'Edfa'
|
||||
eqpt['operational'] = {'gain_target': node.east_amp_gain,
|
||||
'delta_p': node.east_amp_dp,
|
||||
'tilt_target': node.east_tilt,
|
||||
'out_voa': node.east_att_out}
|
||||
elif node.east_amp_type.lower() == 'fused':
|
||||
# fused edfa variety is a hack to indicate that there should not be
|
||||
# booster amplifier out the roadm.
|
||||
# If user specifies ILA in Nodes sheet and fused in Eqpt sheet, then assumes that
|
||||
# this is a fused nodes.
|
||||
eqpt['type'] = 'Fused'
|
||||
eqpt['params'] = {'loss': 0}
|
||||
return eqpt
|
||||
|
||||
|
||||
def create_west_eqpt_element(node):
|
||||
""" create amplifiers json elements for the west direction.
|
||||
this includes the case where the case of a fused element defined instead of an
|
||||
ILA in eqpt sheet
|
||||
"""
|
||||
eqpt = {'uid': f'west edfa in {node.from_city} to {node.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[node.from_city].city,
|
||||
'region': nodes_by_city[node.from_city].region,
|
||||
'latitude': nodes_by_city[node.from_city].latitude,
|
||||
'longitude': nodes_by_city[node.from_city].longitude}},
|
||||
'type': 'Edfa'}
|
||||
if node.west_amp_type.lower() != '' and node.west_amp_type.lower() != 'fused':
|
||||
eqpt['type_variety'] = f'{node.west_amp_type}'
|
||||
eqpt['operational'] = {'gain_target': node.west_amp_gain,
|
||||
'delta_p': node.west_amp_dp,
|
||||
'tilt_target': node.west_tilt,
|
||||
'out_voa': node.west_att_out}
|
||||
elif node.west_amp_type.lower() == '':
|
||||
eqpt['operational'] = {'gain_target': node.west_amp_gain,
|
||||
'delta_p': node.west_amp_dp,
|
||||
'tilt_target': node.west_tilt,
|
||||
'out_voa': node.west_att_out}
|
||||
elif node.west_amp_type.lower() == 'fused':
|
||||
eqpt['type'] = 'Fused'
|
||||
eqpt['params'] = {'loss': 0}
|
||||
return eqpt
|
||||
|
||||
def xls_to_json_data(input_filename, filter_region=[]):
|
||||
nodes, links, eqpts = parse_excel(input_filename)
|
||||
nodes, links, eqpts, roadms = parse_excel(input_filename)
|
||||
if filter_region:
|
||||
nodes = [n for n in nodes if n.region.lower() in filter_region]
|
||||
cities = {n.city for n in nodes}
|
||||
@@ -258,6 +372,10 @@ def xls_to_json_data(input_filename, filter_region=[]):
|
||||
for eqpt in eqpts:
|
||||
eqpts_by_city[eqpt.from_city].append(eqpt)
|
||||
|
||||
roadms_by_city = defaultdict(list)
|
||||
for roadm in roadms:
|
||||
roadms_by_city[roadm.from_node].append(roadm)
|
||||
|
||||
nodes, links = sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city)
|
||||
|
||||
return {
|
||||
@@ -269,28 +387,8 @@ def xls_to_json_data(input_filename, filter_region=[]):
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Transceiver'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] +
|
||||
[{'uid': f'roadm {x.city}',
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Roadm'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'
|
||||
and x.booster_restriction == '' and x.preamp_restriction == ''] +
|
||||
[{'uid': f'roadm {x.city}',
|
||||
'params': {
|
||||
'restrictions': {
|
||||
'preamp_variety_list': silent_remove(x.preamp_restriction.split(' | '), ''),
|
||||
'booster_variety_list': silent_remove(x.booster_restriction.split(' | '), '')
|
||||
}
|
||||
},
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Roadm'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' and
|
||||
(x.booster_restriction != '' or x.preamp_restriction != '')] +
|
||||
[create_roadm_element(x, roadms_by_city)
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] +
|
||||
[{'uid': f'west fused spans in {x.city}',
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
@@ -325,60 +423,12 @@ def xls_to_json_data(input_filename, filter_region=[]):
|
||||
'params': {'length': round(x.west_distance, 3),
|
||||
'length_units': x.distance_units,
|
||||
'loss_coef': x.west_lineic,
|
||||
'con_in': x.west_con_in,
|
||||
'con_out': x.west_con_out}
|
||||
} # missing ILA construction
|
||||
for x in links] +
|
||||
[{'uid': f'east edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Edfa',
|
||||
'type_variety': e.east_amp_type,
|
||||
'operational': {'gain_target': e.east_amp_gain,
|
||||
'delta_p': e.east_amp_dp,
|
||||
'tilt_target': e.east_tilt,
|
||||
'out_voa': e.east_att_out}
|
||||
}
|
||||
for e in eqpts if (e.east_amp_type.lower() != '' and \
|
||||
e.east_amp_type.lower() != 'fused')] +
|
||||
[{'uid': f'west edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Edfa',
|
||||
'type_variety': e.west_amp_type,
|
||||
'operational': {'gain_target': e.west_amp_gain,
|
||||
'delta_p': e.west_amp_dp,
|
||||
'tilt_target': e.west_tilt,
|
||||
'out_voa': e.west_att_out}
|
||||
}
|
||||
for e in eqpts if (e.west_amp_type.lower() != '' and \
|
||||
e.west_amp_type.lower() != 'fused')] +
|
||||
# fused edfa variety is a hack to indicate that there should not be
|
||||
# booster amplifier out the roadm.
|
||||
# If user specifies ILA in Nodes sheet and fused in Eqpt sheet, then assumes that
|
||||
# this is a fused nodes.
|
||||
[{'uid': f'east edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Fused',
|
||||
'params': {'loss': 0}
|
||||
}
|
||||
for e in eqpts if e.east_amp_type.lower() == 'fused'] +
|
||||
[{'uid': f'west edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Fused',
|
||||
'params': {'loss': 0}
|
||||
}
|
||||
for e in eqpts if e.west_amp_type.lower() == 'fused'],
|
||||
'con_in':x.west_con_in,
|
||||
'con_out':x.west_con_out}
|
||||
} # missing ILA construction
|
||||
for x in links] +
|
||||
[create_east_eqpt_element(e) for e in eqpts] +
|
||||
[create_west_eqpt_element(e) for e in eqpts],
|
||||
'connections':
|
||||
list(chain.from_iterable([eqpt_connection_by_city(n.city)
|
||||
for n in nodes]))
|
||||
@@ -399,6 +449,7 @@ def convert_file(input_filename, filter_region=[], output_json_file_name=None):
|
||||
output_json_file_name = input_filename.with_suffix('.json')
|
||||
with open(output_json_file_name, 'w', encoding='utf-8') as edfa_json_file:
|
||||
edfa_json_file.write(dumps(data, indent=2, ensure_ascii=False))
|
||||
edfa_json_file.write('\n') # add end of file newline because json dumps does not.
|
||||
return output_json_file_name
|
||||
|
||||
|
||||
@@ -407,7 +458,7 @@ def corresp_names(input_filename, network):
|
||||
and names used in the json, and created by the autodesign.
|
||||
All names are listed
|
||||
"""
|
||||
nodes, links, eqpts = parse_excel(input_filename)
|
||||
nodes, links, eqpts, roadms = parse_excel(input_filename)
|
||||
fused = [n.uid for n in network.nodes() if isinstance(n, Fused)]
|
||||
ila = [n.uid for n in network.nodes() if isinstance(n, Edfa)]
|
||||
|
||||
@@ -435,17 +486,15 @@ def corresp_names(input_filename, network):
|
||||
# build corresp ila based on eqpt sheet
|
||||
# start with east direction
|
||||
corresp_ila = {e.from_city: [f'east edfa in {e.from_city} to {e.to_city}']
|
||||
for e in eqpts if e.east_amp_type.lower() != '' and
|
||||
f'east edfa in {e.from_city} to {e.to_city}' in ila}
|
||||
for e in eqpts if f'east edfa in {e.from_city} to {e.to_city}' in ila}
|
||||
# west direction, append name or create a new item in dict
|
||||
for my_e in eqpts:
|
||||
if my_e.west_amp_type.lower() != '':
|
||||
name = f'west edfa in {my_e.from_city} to {my_e.to_city}'
|
||||
if name in ila:
|
||||
if my_e.from_city in corresp_ila.keys():
|
||||
corresp_ila[my_e.from_city].append(name)
|
||||
else:
|
||||
corresp_ila[my_e.from_city] = [name]
|
||||
name = f'west edfa in {my_e.from_city} to {my_e.to_city}'
|
||||
if name in ila:
|
||||
if my_e.from_city in corresp_ila.keys():
|
||||
corresp_ila[my_e.from_city].append(name)
|
||||
else:
|
||||
corresp_ila[my_e.from_city] = [name]
|
||||
# complete with potential autodesign names: amplifiers
|
||||
for my_l in links:
|
||||
name = f'Edfa0_fiber ({my_l.to_city} \u2192 {my_l.from_city})-{my_l.west_cable}'
|
||||
@@ -466,7 +515,6 @@ def corresp_names(input_filename, network):
|
||||
corresp_ila[my_l.to_city].append(name)
|
||||
else:
|
||||
corresp_ila[my_l.to_city] = [name]
|
||||
|
||||
# merge fused with ila:
|
||||
for key, val in corresp_fused.items():
|
||||
if key in corresp_ila.keys():
|
||||
@@ -531,6 +579,10 @@ def parse_excel(input_filename):
|
||||
'att_out': 'west_att_out'
|
||||
}
|
||||
}
|
||||
roadm_headers = {'Node A': 'from_node',
|
||||
'Node Z': 'to_node',
|
||||
'per degree target power (dBm)': 'target_pch_out_db'
|
||||
}
|
||||
|
||||
with open_workbook(input_filename) as wb:
|
||||
nodes_sheet = wb.sheet_by_name('Nodes')
|
||||
@@ -540,6 +592,11 @@ def parse_excel(input_filename):
|
||||
except Exception:
|
||||
# eqpt_sheet is optional
|
||||
eqpt_sheet = None
|
||||
try:
|
||||
roadm_sheet = wb.sheet_by_name('Roadms')
|
||||
except Exception:
|
||||
# roadm_sheet is optional
|
||||
roadm_sheet = None
|
||||
|
||||
nodes = []
|
||||
for node in parse_sheet(nodes_sheet, node_headers, NODES_LINE, NODES_LINE + 1, NODES_COLUMN):
|
||||
@@ -558,6 +615,11 @@ def parse_excel(input_filename):
|
||||
for eqpt in parse_sheet(eqpt_sheet, eqpt_headers, EQPTS_LINE, EQPTS_LINE + 2, EQPTS_COLUMN):
|
||||
eqpts.append(Eqpt(**eqpt))
|
||||
|
||||
roadms = []
|
||||
if roadm_sheet is not None:
|
||||
for roadm in parse_sheet(roadm_sheet, roadm_headers, ROADMS_LINE, ROADMS_LINE+2, ROADMS_COLUMN):
|
||||
roadms.append(Roadm(**roadm))
|
||||
|
||||
# sanity check
|
||||
all_cities = Counter(n.city for n in nodes)
|
||||
if len(all_cities) != len(nodes):
|
||||
@@ -566,10 +628,14 @@ def parse_excel(input_filename):
|
||||
for lnk in links:
|
||||
if lnk.from_city not in all_cities or lnk.to_city not in all_cities:
|
||||
bad_links.append([lnk.from_city, lnk.to_city])
|
||||
if bad_links:
|
||||
raise NetworkTopologyError(f'Bad link(s): {bad_links}.')
|
||||
|
||||
return nodes, links, eqpts
|
||||
if bad_links:
|
||||
raise NetworkTopologyError(f'{ansi_escapes.red}XLS error:{ansi_escapes.reset} '
|
||||
f'The {ansi_escapes.cyan}Links{ansi_escapes.reset} sheet references nodes that '
|
||||
f'are not defined in the {ansi_escapes.cyan}Nodes{ansi_escapes.reset} sheet:\n'
|
||||
+ _format_items(f'{item[0]} -> {item[1]}' for item in bad_links))
|
||||
|
||||
return nodes, links, eqpts, roadms
|
||||
|
||||
|
||||
def eqpt_connection_by_city(city_name):
|
||||
@@ -615,14 +681,13 @@ def eqpt_in_city_to_city(in_city, to_city, direction='east'):
|
||||
if in_city in eqpts_by_city:
|
||||
for e in eqpts_by_city[in_city]:
|
||||
if nodes_by_city[in_city].node_type.lower() == 'roadm':
|
||||
if e.to_city == to_city and getattr(e, amp_direction) != '':
|
||||
if e.to_city == to_city:
|
||||
return_eqpt = f'{direction} edfa in {e.from_city} to {e.to_city}'
|
||||
elif nodes_by_city[in_city].node_type.lower() == 'ila':
|
||||
if e.to_city != to_city:
|
||||
direction = rev_direction
|
||||
amp_direction = amp_rev_direction
|
||||
if getattr(e, amp_direction) != '':
|
||||
return_eqpt = f'{direction} edfa in {e.from_city} to {e.to_city}'
|
||||
return_eqpt = f'{direction} edfa in {e.from_city} to {e.to_city}'
|
||||
if nodes_by_city[in_city].node_type.lower() == 'fused':
|
||||
return_eqpt = f'{direction} fused spans in {in_city}'
|
||||
return return_eqpt
|
||||
@@ -729,6 +794,8 @@ LINKS_COLUMN = 16
|
||||
LINKS_LINE = 3
|
||||
EQPTS_LINE = 3
|
||||
EQPTS_COLUMN = 14
|
||||
ROADMS_LINE = 3
|
||||
ROADMS_COLUMN = 3
|
||||
|
||||
|
||||
def _do_convert():
|
||||
|
||||
@@ -210,6 +210,8 @@ class Amp(_JsonThing):
|
||||
except KeyError:
|
||||
raise EquipmentConfigError(f'missing preamp/booster variety input for amplifier: {type_variety} in equipment config')
|
||||
dual_stage_def = Model_dual_stage(preamp_variety, booster_variety)
|
||||
else:
|
||||
raise EquipmentConfigError(f'Edfa type_def {type_def} does not exist')
|
||||
|
||||
json_data = load_json(config)
|
||||
|
||||
@@ -230,14 +232,6 @@ def load_equipment(filename):
|
||||
return _equipment_from_json(json_data, filename)
|
||||
|
||||
|
||||
def _update_trx_osnr(equipment):
|
||||
"""add sys_margins to all Transceivers OSNR values"""
|
||||
for trx in equipment['Transceiver'].values():
|
||||
for m in trx.mode:
|
||||
m['OSNR'] = m['OSNR'] + equipment['SI']['default'].sys_margins
|
||||
return equipment
|
||||
|
||||
|
||||
def _update_dual_stage(equipment):
|
||||
edfa_dict = equipment['Edfa']
|
||||
for edfa in edfa_dict.values():
|
||||
@@ -268,6 +262,21 @@ def _roadm_restrictions_sanity_check(equipment):
|
||||
raise EquipmentConfigError(f'ROADM restriction {amp_name} does not refer to a defined EDFA name')
|
||||
|
||||
|
||||
def _check_fiber_vs_raman_fiber(equipment):
|
||||
"""Ensure that Fiber and RamanFiber with the same name define common properties equally"""
|
||||
if 'RamanFiber' not in equipment:
|
||||
return
|
||||
for fiber_type in set(equipment['Fiber'].keys()) & set(equipment['RamanFiber'].keys()):
|
||||
for attr in ('dispersion', 'dispersion-slope', 'gamma', 'pmd-coefficient'):
|
||||
fiber = equipment['Fiber'][fiber_type]
|
||||
raman = equipment['RamanFiber'][fiber_type]
|
||||
a = getattr(fiber, attr, None)
|
||||
b = getattr(raman, attr, None)
|
||||
if a != b:
|
||||
raise EquipmentConfigError(f'WARNING: Fiber and RamanFiber definition of "{fiber_type}" '
|
||||
f'disagrees for "{attr}": {a} != {b}')
|
||||
|
||||
|
||||
def _equipment_from_json(json_data, filename):
|
||||
"""build global dictionnary eqpt_library that stores all eqpt characteristics:
|
||||
edfa type type_variety, fiber type_variety
|
||||
@@ -298,7 +307,7 @@ def _equipment_from_json(json_data, filename):
|
||||
equipment[key][subkey] = RamanFiber(**entry)
|
||||
else:
|
||||
raise EquipmentConfigError(f'Unrecognized network element type "{key}"')
|
||||
equipment = _update_trx_osnr(equipment)
|
||||
_check_fiber_vs_raman_fiber(equipment)
|
||||
equipment = _update_dual_stage(equipment)
|
||||
_roadm_restrictions_sanity_check(equipment)
|
||||
return equipment
|
||||
@@ -358,7 +367,7 @@ def network_from_json(json_data, equipment):
|
||||
temp = merge_amplifier_restrictions(temp, extra_params.__dict__)
|
||||
el_config['params'] = temp
|
||||
el_config['type_variety'] = variety
|
||||
elif typ in ['Edfa', 'Fiber', 'RamanFiber']: # catch it now because the code will crash later!
|
||||
elif (typ in ['Fiber', 'RamanFiber']) or (typ == 'Edfa' and variety not in ['default', '']):
|
||||
raise ConfigurationError(f'The {typ} of variety type {variety} was not recognized:'
|
||||
'\nplease check it is properly defined in the eqpt_config json file')
|
||||
el = cls(**el_config)
|
||||
@@ -431,7 +440,10 @@ def requests_from_json(json_data, equipment):
|
||||
params['bidir'] = req['bidirectional']
|
||||
params['destination'] = req['destination']
|
||||
params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type']
|
||||
params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode']
|
||||
if 'trx_mode' in req['path-constraints']['te-bandwidth'].keys():
|
||||
params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode']
|
||||
else:
|
||||
params['trx_mode'] = None
|
||||
params['format'] = params['trx_mode']
|
||||
params['spacing'] = req['path-constraints']['te-bandwidth']['spacing']
|
||||
try:
|
||||
@@ -466,8 +478,12 @@ def requests_from_json(json_data, equipment):
|
||||
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'])
|
||||
_check_one_request(params, f_max_from_si)
|
||||
|
||||
if 'effective-freq-slot' in req['path-constraints']['te-bandwidth']:
|
||||
# temporarily reads only the first slot
|
||||
params['effective_freq_slot'] = req['path-constraints']['te-bandwidth']['effective-freq-slot'][0]
|
||||
else:
|
||||
params['effective_freq_slot'] = None
|
||||
params['blocking_reason'] = _check_one_request(params, f_max_from_si)
|
||||
try:
|
||||
params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth']
|
||||
except KeyError:
|
||||
@@ -489,7 +505,7 @@ def _check_one_request(params, f_max_from_si):
|
||||
f'{params["min_spacing"]*1e-9}GHz.\nComputation stopped'
|
||||
print(msg)
|
||||
_logger.critical(msg)
|
||||
raise ServiceError(msg)
|
||||
return 'MODE_BAUDRATE_NOT_CONSISTENT_WITH_SPACING'
|
||||
if f_max > f_max_from_si:
|
||||
msg = f'''Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz
|
||||
and requested spacing {params["spacing"]*1e-9}GHz is not consistent with frequency range
|
||||
@@ -497,6 +513,7 @@ def _check_one_request(params, f_max_from_si):
|
||||
max recommanded nb of channels is {max_recommanded_nb_channels}.'''
|
||||
_logger.critical(msg)
|
||||
raise ServiceError(msg)
|
||||
return None
|
||||
|
||||
|
||||
def disjunctions_from_json(json_data):
|
||||
@@ -504,11 +521,7 @@ def disjunctions_from_json(json_data):
|
||||
of requested disjunctions for this set of requests
|
||||
"""
|
||||
disjunctions_list = []
|
||||
try:
|
||||
temp_test = json_data['synchronization']
|
||||
except KeyError:
|
||||
temp_test = []
|
||||
if temp_test:
|
||||
if 'synchronization' in json_data:
|
||||
for snc in json_data['synchronization']:
|
||||
params = {}
|
||||
params['disjunction_id'] = snc['synchronization-id']
|
||||
|
||||
@@ -13,10 +13,14 @@ from networkx import draw_networkx_nodes, draw_networkx_edges, draw_networkx_lab
|
||||
from gnpy.core.elements import Transceiver
|
||||
|
||||
|
||||
def _try_city(node):
|
||||
return node.location.city if node.location.city else node.uid
|
||||
|
||||
|
||||
def plot_baseline(network):
|
||||
edges = set(network.edges())
|
||||
pos = {n: (n.lng, n.lat) for n in network.nodes()}
|
||||
labels = {n: n.location.city for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
labels = {n: _try_city(n) for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
city_labels = set(labels.values())
|
||||
for n in network.nodes():
|
||||
if n.location.city and n.location.city not in city_labels:
|
||||
@@ -33,14 +37,15 @@ def plot_baseline(network):
|
||||
show()
|
||||
|
||||
|
||||
def plot_results(network, path, source, destination, infos):
|
||||
def plot_results(network, path, source, destination):
|
||||
path_edges = set(zip(path[:-1], path[1:]))
|
||||
edges = set(network.edges()) - path_edges
|
||||
pos = {n: (n.lng, n.lat) for n in network.nodes()}
|
||||
nodes = {}
|
||||
for k, (x, y) in pos.items():
|
||||
nodes.setdefault((round(x, 1), round(y, 1)), []).append(k)
|
||||
labels = {n: n.location.city for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
|
||||
labels = {n: _try_city(n) for n in network.nodes() if isinstance(n, Transceiver)}
|
||||
city_labels = set(labels.values())
|
||||
for n in network.nodes():
|
||||
if n.location.city and n.location.city not in city_labels:
|
||||
@@ -56,7 +61,7 @@ def plot_results(network, path, source, destination, infos):
|
||||
draw_networkx_edges(network, edgelist=edges, edge_color='#ababab', **kwargs)
|
||||
draw_networkx_edges(network, edgelist=path_edges, edge_color='#ff0000', **kwargs)
|
||||
draw_networkx_labels(network, labels=labels, font_size=14, **{**kwargs, 'pos': label_pos})
|
||||
title(f'Propagating from {source.loc.city} to {destination.loc.city}')
|
||||
title(f'Propagating from {_try_city(source)} to {_try_city(destination)}')
|
||||
axis('off')
|
||||
|
||||
heading = 'Spectral Information\n\n'
|
||||
|
||||
@@ -127,7 +127,7 @@ class Request_element(Element):
|
||||
'technology': 'flexi-grid',
|
||||
'trx_type': self.trx_type,
|
||||
'trx_mode': self.mode,
|
||||
'effective-freq-slot': [{'N': 'null', 'M': 'null'}],
|
||||
'effective-freq-slot': [{'N': None, 'M': None}],
|
||||
'spacing': self.spacing,
|
||||
'max-nb-of-channel': self.nb_channel,
|
||||
'output-power': self.power
|
||||
|
||||
@@ -35,7 +35,7 @@ LOGGER = getLogger(__name__)
|
||||
RequestParams = namedtuple('RequestParams', 'request_id source destination bidir trx_type' +
|
||||
' trx_mode nodes_list loose_list spacing power nb_channel f_min' +
|
||||
' f_max format baud_rate OSNR bit_rate roll_off tx_osnr' +
|
||||
' min_spacing cost path_bandwidth')
|
||||
' min_spacing cost path_bandwidth effective_freq_slot blocking_reason')
|
||||
DisjunctionParams = namedtuple('DisjunctionParams', 'disjunction_id relaxable link' +
|
||||
'_diverse node_diverse disjunctions_req')
|
||||
|
||||
@@ -68,6 +68,11 @@ class PathRequest:
|
||||
self.min_spacing = params.min_spacing
|
||||
self.cost = params.cost
|
||||
self.path_bandwidth = params.path_bandwidth
|
||||
if params.effective_freq_slot is not None:
|
||||
self.N = params.effective_freq_slot['N']
|
||||
self.M = params.effective_freq_slot['M']
|
||||
if params.blocking_reason is not None:
|
||||
self.blocking_reason = params.blocking_reason
|
||||
|
||||
def __str__(self):
|
||||
return '\n\t'.join([f'{type(self).__name__} {self.request_id}',
|
||||
@@ -127,7 +132,7 @@ class Disjunction:
|
||||
|
||||
BLOCKING_NOPATH = ['NO_PATH', 'NO_PATH_WITH_CONSTRAINT',
|
||||
'NO_FEASIBLE_BAUDRATE_WITH_SPACING',
|
||||
'NO_COMPUTED_SNR']
|
||||
'NO_COMPUTED_SNR', 'MODE_BAUDRATE_NOT_CONSISTENT_WITH_SPACING']
|
||||
BLOCKING_NOMODE = ['NO_FEASIBLE_MODE', 'MODE_NOT_FEASIBLE']
|
||||
BLOCKING_NOSPECTRUM = 'NO_SPECTRUM'
|
||||
|
||||
@@ -333,23 +338,17 @@ def propagate(path, req, equipment):
|
||||
si = create_input_spectral_information(
|
||||
req.f_min, req.f_max, req.roll_off, req.baud_rate,
|
||||
req.power, req.spacing)
|
||||
for el in path:
|
||||
si = el(si)
|
||||
path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr)
|
||||
return path
|
||||
|
||||
|
||||
def propagate2(path, req, equipment):
|
||||
si = create_input_spectral_information(
|
||||
req.f_min, req.f_max, req.roll_off, req.baud_rate,
|
||||
req.power, req.spacing)
|
||||
infos = {}
|
||||
for el in path:
|
||||
before_si = si
|
||||
after_si = si = el(si)
|
||||
infos[el] = before_si, after_si
|
||||
path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr)
|
||||
return infos
|
||||
for i, el in enumerate(path):
|
||||
if isinstance(el, Roadm):
|
||||
si = el(si, degree=path[i+1].uid)
|
||||
else:
|
||||
si = el(si)
|
||||
path[0].update_snr(req.tx_osnr)
|
||||
if any(isinstance(el, Roadm) for el in path):
|
||||
path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr)
|
||||
else:
|
||||
path[-1].update_snr(req.tx_osnr)
|
||||
return si
|
||||
|
||||
|
||||
def propagate_and_optimize_mode(path, req, equipment):
|
||||
@@ -375,19 +374,26 @@ def propagate_and_optimize_mode(path, req, equipment):
|
||||
spc_info = create_input_spectral_information(req.f_min, req.f_max,
|
||||
equipment['SI']['default'].roll_off,
|
||||
this_br, req.power, req.spacing)
|
||||
for el in path:
|
||||
spc_info = el(spc_info)
|
||||
for i, el in enumerate(path):
|
||||
if isinstance(el, Roadm):
|
||||
spc_info = el(spc_info, degree=path[i+1].uid)
|
||||
else:
|
||||
spc_info = el(spc_info)
|
||||
for this_mode in modes_to_explore:
|
||||
if path[-1].snr is not None:
|
||||
path[-1].update_snr(this_mode['tx_osnr'], equipment['Roadm']['default'].add_drop_osnr)
|
||||
if round(min(path[-1].snr + lin2db(this_br / (12.5e9))), 2) > this_mode['OSNR']:
|
||||
path[0].update_snr(this_mode['tx_osnr'])
|
||||
if any(isinstance(el, Roadm) for el in path):
|
||||
path[-1].update_snr(this_mode['tx_osnr'], equipment['Roadm']['default'].add_drop_osnr)
|
||||
else:
|
||||
path[-1].update_snr(this_mode['tx_osnr'])
|
||||
if round(min(path[-1].snr + lin2db(this_br / (12.5e9))), 2) \
|
||||
> this_mode['OSNR'] + equipment['SI']['default'].sys_margins:
|
||||
return path, this_mode
|
||||
else:
|
||||
last_explored_mode = this_mode
|
||||
else:
|
||||
req.blocking_reason = 'NO_COMPUTED_SNR'
|
||||
return path, None
|
||||
|
||||
# only get to this point if no baudrate/mode satisfies OSNR requirement
|
||||
|
||||
# returns the last propagated path and mode
|
||||
@@ -817,23 +823,21 @@ def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list):
|
||||
# step 5 select the first combination that works
|
||||
pathreslist_disjoint = {}
|
||||
for dis in disjunctions_list:
|
||||
test_sol = True
|
||||
while test_sol:
|
||||
# print('coucou')
|
||||
if candidates[dis.disjunction_id]:
|
||||
for pth in candidates[dis.disjunction_id][0]:
|
||||
if allpaths[id(pth)].req in pathreqlist_disjt:
|
||||
# print(f'selected path:{pth} for req {allpaths[id(pth)].req.request_id}')
|
||||
pathreslist_disjoint[allpaths[id(pth)].req] = allpaths[id(pth)].pth
|
||||
pathreqlist_disjt.remove(allpaths[id(pth)].req)
|
||||
candidates = remove_candidate(candidates, allpaths, allpaths[id(pth)].req, pth)
|
||||
test_sol = False
|
||||
else:
|
||||
msg = f'No disjoint path found with added constraint'
|
||||
LOGGER.critical(msg)
|
||||
print(f'{msg}\nComputation stopped.')
|
||||
# TODO in this case: replay step 5 with the candidate without constraints
|
||||
raise DisjunctionError(msg)
|
||||
if candidates[dis.disjunction_id]:
|
||||
for pth in candidates[dis.disjunction_id][0]:
|
||||
if allpaths[id(pth)].req in pathreqlist_disjt:
|
||||
# print(f'selected path:{pth} for req {allpaths[id(pth)].req.request_id}')
|
||||
pathreslist_disjoint[allpaths[id(pth)].req] = allpaths[id(pth)].pth
|
||||
# remove request from list of requests (in case of duplicate)
|
||||
pathreqlist_disjt.remove(allpaths[id(pth)].req)
|
||||
# remove duplicated candidates
|
||||
candidates = remove_candidate(candidates, allpaths, allpaths[id(pth)].req, pth)
|
||||
else:
|
||||
msg = f'No disjoint path found with added constraint'
|
||||
LOGGER.critical(msg)
|
||||
print(f'{msg}\nComputation stopped.')
|
||||
# TODO in this case: replay step 5 with the candidate without constraints
|
||||
raise DisjunctionError(msg)
|
||||
|
||||
# for i in disjunctions_list:
|
||||
# print(i.disjunction_id)
|
||||
@@ -1095,13 +1099,13 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist):
|
||||
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)
|
||||
propagate(total_path, pathreq, equipment)
|
||||
temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2)
|
||||
if temp_snr01nm < pathreq.OSNR:
|
||||
if temp_snr01nm < pathreq.OSNR + equipment['SI']['default'].sys_margins:
|
||||
msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
|
||||
f' {pathreq.source} to {pathreq.destination} does not pass with' +\
|
||||
f' {pathreq.tsp_mode}\n\tcomputedSNR in 0.1nm = {temp_snr01nm} ' +\
|
||||
f'- required osnr {pathreq.OSNR}'
|
||||
f'- required osnr {pathreq.OSNR} + {equipment["SI"]["default"].sys_margins} margin'
|
||||
print(msg)
|
||||
LOGGER.warning(msg)
|
||||
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
|
||||
@@ -1133,25 +1137,28 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist):
|
||||
|
||||
# reversed path is needed for correct spectrum assignment
|
||||
reversed_path = find_reversed_path(pathlist[i])
|
||||
if pathreq.bidir:
|
||||
if pathreq.bidir and pathreq.baud_rate is not None:
|
||||
# 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)
|
||||
propagate(rev_p, pathreq, equipment)
|
||||
propagated_reversed_path = rev_p
|
||||
temp_snr01nm = round(mean(propagated_reversed_path[-1].snr +\
|
||||
lin2db(pathreq.baud_rate/(12.5e9))), 2)
|
||||
if temp_snr01nm < pathreq.OSNR:
|
||||
if temp_snr01nm < pathreq.OSNR + equipment['SI']['default'].sys_margins:
|
||||
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}'
|
||||
f'\tcomputedSNR in 0.1nm = {temp_snr01nm} -' \
|
||||
f' required osnr {pathreq.OSNR} + {equipment["SI"]["default"].sys_margins} margin'
|
||||
print(msg)
|
||||
LOGGER.warning(msg)
|
||||
# TODO selection of mode should also be on reversed direction !!
|
||||
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
|
||||
if not hasattr(pathreq, 'blocking_reason'):
|
||||
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
|
||||
else:
|
||||
propagated_reversed_path = []
|
||||
else:
|
||||
|
||||
@@ -395,7 +395,7 @@ def pth_assign_spectrum(pths, rqs, oms_list, rpths):
|
||||
try:
|
||||
if rqs[i].blocking_reason:
|
||||
rqs[i].blocked = True
|
||||
rqs[i].N = 0
|
||||
rqs[i].N = None
|
||||
rqs[i].M = 0
|
||||
except AttributeError:
|
||||
nb_wl = ceil(rqs[i].path_bandwidth / rqs[i].bit_rate)
|
||||
@@ -404,9 +404,25 @@ def pth_assign_spectrum(pths, rqs, oms_list, rpths):
|
||||
# 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
|
||||
if hasattr(rqs[i], 'M') and rqs[i].M is not None:
|
||||
# Consistency check between the requested M and path_bandwidth
|
||||
# M value should be bigger than the computed requested_m (simple estimate)
|
||||
# TODO: elaborate a more accurate estimate with nb_wl * tx_osnr + possibly guardbands in case of
|
||||
# superchannel closed packing.
|
||||
if requested_m <= rqs[i].M:
|
||||
requested_m = rqs[i].M
|
||||
else:
|
||||
# TODO : create a specific blocking reason and following process for this case instead of an exception
|
||||
raise SpectrumError(f'requested M {rqs[i].M} number of slots for request {rqs[i].request_id} ' +
|
||||
f'should be greater than {requested_m} to support request ' +
|
||||
f'{rqs[i].path_bandwidth * 1e-9} Gbit/s with {rqs[i].tsp} {rqs[i].tsp_mode}')
|
||||
# else: there is no M value so the programs uses the requested_m one
|
||||
if hasattr(rqs[i], 'N'):
|
||||
requested_n = rqs[i].N
|
||||
else:
|
||||
requested_n = None
|
||||
(center_n, startn, stopn), path_oms = spectrum_selection(pth + rpths[i], oms_list, requested_m,
|
||||
requested_n=None)
|
||||
requested_n)
|
||||
# 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
|
||||
@@ -426,6 +442,6 @@ def pth_assign_spectrum(pths, rqs, oms_list, rpths):
|
||||
rqs[i].M = requested_m
|
||||
else:
|
||||
rqs[i].blocked = True
|
||||
rqs[i].N = 0
|
||||
rqs[i].N = None
|
||||
rqs[i].M = 0
|
||||
rqs[i].blocking_reason = 'NO_SPECTRUM'
|
||||
|
||||
310
gnpy/yang/api-equipment.json
Normal file
310
gnpy/yang/api-equipment.json
Normal file
@@ -0,0 +1,310 @@
|
||||
{
|
||||
"Edfa":[{
|
||||
"type_variety": "high_detail_model_example",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25.0,
|
||||
"gain_min": 15.0,
|
||||
"p_max": 21.0,
|
||||
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
}, {
|
||||
"type_variety": "Juniper_BoosterHG",
|
||||
"type_def": "advanced_model",
|
||||
"gain_flatmax": 25.0,
|
||||
"gain_min": 10.0,
|
||||
"p_max": 21.0,
|
||||
"advanced_config_from_json": "Juniper-BoosterHG.json",
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "operator_model_example",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26.0,
|
||||
"gain_min": 15.0,
|
||||
"p_max": 23.0,
|
||||
"nf_min": 6.0,
|
||||
"nf_max": 10.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "low_noise",
|
||||
"type_def": "openroadm",
|
||||
"gain_flatmax": 27.0,
|
||||
"gain_min": 12.0,
|
||||
"p_max": 22.0,
|
||||
"nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62],
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "standard",
|
||||
"type_def": "openroadm",
|
||||
"gain_flatmax": 27.0,
|
||||
"gain_min": 12.0,
|
||||
"p_max": 22.0,
|
||||
"nf_coef": [-5.952e-4,-6.250e-2,-1.071,28.99],
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "std_high_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 35.0,
|
||||
"gain_min": 25.0,
|
||||
"p_max": 21.0,
|
||||
"nf_min": 5.5,
|
||||
"nf_max": 7.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_medium_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 26.0,
|
||||
"gain_min": 15.0,
|
||||
"p_max": 23.0,
|
||||
"nf_min": 6.0,
|
||||
"nf_max": 10.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "std_low_gain",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16.0,
|
||||
"gain_min": 8.0,
|
||||
"p_max": 23.0,
|
||||
"nf_min": 6.5,
|
||||
"nf_max": 11.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "high_power",
|
||||
"type_def": "variable_gain",
|
||||
"gain_flatmax": 16.0,
|
||||
"gain_min": 8.0,
|
||||
"p_max": 25.0,
|
||||
"nf_min": 9.0,
|
||||
"nf_max": 15.0,
|
||||
"out_voa_auto": false,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "std_fixed_gain",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21.0,
|
||||
"gain_min": 20.0,
|
||||
"p_max": 21.0,
|
||||
"nf0": 5.5,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "4pumps_raman",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 12.0,
|
||||
"gain_min": 12.0,
|
||||
"p_max": 21.0,
|
||||
"nf0": -1.0,
|
||||
"allowed_for_design": false
|
||||
},
|
||||
{
|
||||
"type_variety": "hybrid_4pumps_lowgain",
|
||||
"type_def": "dual_stage",
|
||||
"raman": true,
|
||||
"gain_min": 25.0,
|
||||
"preamp_variety": "4pumps_raman",
|
||||
"booster_variety": "std_low_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "hybrid_4pumps_mediumgain",
|
||||
"type_def": "dual_stage",
|
||||
"raman": true,
|
||||
"gain_min": 25.0,
|
||||
"preamp_variety": "4pumps_raman",
|
||||
"booster_variety": "std_medium_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "medium+low_gain",
|
||||
"type_def": "dual_stage",
|
||||
"gain_min": 25.0,
|
||||
"preamp_variety": "std_medium_gain",
|
||||
"booster_variety": "std_low_gain",
|
||||
"allowed_for_design": true
|
||||
},
|
||||
{
|
||||
"type_variety": "medium+high_power",
|
||||
"type_def": "dual_stage",
|
||||
"gain_min": 25.0,
|
||||
"preamp_variety": "std_medium_gain",
|
||||
"booster_variety": "high_power",
|
||||
"allowed_for_design": false
|
||||
}
|
||||
],
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127,
|
||||
"pmd_coef": 1.265e-15
|
||||
},
|
||||
{
|
||||
"type_variety": "NZDF",
|
||||
"dispersion": 0.5e-05,
|
||||
"gamma": 0.00146,
|
||||
"pmd_coef": 1.265e-15
|
||||
},
|
||||
{
|
||||
"type_variety": "LOF",
|
||||
"dispersion": 2.2e-05,
|
||||
"gamma": 0.000843,
|
||||
"pmd_coef": 1.265e-15
|
||||
}
|
||||
],
|
||||
"RamanFiber":[{
|
||||
"type_variety": "SSMF",
|
||||
"dispersion": 1.67e-05,
|
||||
"gamma": 0.00127,
|
||||
"pmd_coef": 1.265e-15,
|
||||
"raman_efficiency": {
|
||||
"cr":[
|
||||
0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119,
|
||||
0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469,
|
||||
0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826,
|
||||
0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05,
|
||||
7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05,
|
||||
2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05,
|
||||
2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05,
|
||||
1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05,
|
||||
1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07,
|
||||
2E-07, 1E-07
|
||||
],
|
||||
"frequency_offset":[
|
||||
0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12,
|
||||
7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12,
|
||||
13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12,
|
||||
17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12,
|
||||
22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12,
|
||||
28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12,
|
||||
35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12,
|
||||
41.5e12, 42e12
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"Span":[{
|
||||
"power_mode":true,
|
||||
"delta_power_range_db": [-2.0, 3.0, 0.5],
|
||||
"max_fiber_lineic_loss_for_raman": 0.25,
|
||||
"target_extended_gain": 2.5,
|
||||
"max_length": 150.0,
|
||||
"length_units": "km",
|
||||
"max_loss": 28.0,
|
||||
"padding": 10.0,
|
||||
"EOL": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
}
|
||||
],
|
||||
"Roadm":[{
|
||||
"target_pch_out_db": -20.0,
|
||||
"add_drop_osnr": 38.0,
|
||||
"pmd": 0.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list":[],
|
||||
"booster_variety_list":[]
|
||||
}
|
||||
}],
|
||||
"SI":[{
|
||||
"f_min": 191.3e12,
|
||||
"baud_rate": 32e9,
|
||||
"f_max":195.1e12,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0.0,
|
||||
"power_range_db": [0.0,0.0,1.0],
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"sys_margins": 2.0
|
||||
}],
|
||||
"Transceiver":[
|
||||
{
|
||||
"type_variety": "vendorA_trx-type1",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[{
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 11.0,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 37.5e9,
|
||||
"cost":1.0
|
||||
},
|
||||
{
|
||||
"format": "mode 2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 15.0,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1.0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type_variety": "Voyager",
|
||||
"frequency":{
|
||||
"min": 191.35e12,
|
||||
"max": 196.1e12
|
||||
},
|
||||
"mode":[
|
||||
{
|
||||
"format": "mode 1",
|
||||
"baud_rate": 32e9,
|
||||
"OSNR": 12.0,
|
||||
"bit_rate": 100e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 37.5e9,
|
||||
"cost":1.0
|
||||
},
|
||||
{
|
||||
"format": "mode 3",
|
||||
"baud_rate": 44e9,
|
||||
"OSNR": 18.0,
|
||||
"bit_rate": 300e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 62.5e9,
|
||||
"cost":1.0
|
||||
},
|
||||
{
|
||||
"format": "mode 2",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 21.0,
|
||||
"bit_rate": 400e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1.0
|
||||
},
|
||||
{
|
||||
"format": "mode 4",
|
||||
"baud_rate": 66e9,
|
||||
"OSNR": 16.0,
|
||||
"bit_rate": 200e9,
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 40.0,
|
||||
"min_spacing": 75e9,
|
||||
"cost":1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
180
gnpy/yang/api-request-with-id.json
Normal file
180
gnpy/yang/api-request-with-id.json
Normal file
@@ -0,0 +1,180 @@
|
||||
{
|
||||
"gnpy-api:service":{
|
||||
"path-request": [
|
||||
{
|
||||
"request-id": "0",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": 0,
|
||||
"M": 12
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "1",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "2",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": false,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 2",
|
||||
"spacing": 100000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "3",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
},
|
||||
"explicit-route-objects": {
|
||||
"route-object-include-exclude": [
|
||||
{
|
||||
"explicit-route-usage": "route-include-ero",
|
||||
"index": 0,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "roadm Carol",
|
||||
"link-tp-id": "link-tp-id is not used",
|
||||
"hop-type": "LOOSE"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "4",
|
||||
"source": "trx Alice",
|
||||
"destination": "trx Bob",
|
||||
"src-tp-id": "trx Alice",
|
||||
"dst-tp-id": "trx Bob",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": -284,
|
||||
"M": 12
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "5",
|
||||
"source": "trx Bob1",
|
||||
"destination": "trx Carol1",
|
||||
"src-tp-id": "trx Bob1",
|
||||
"dst-tp-id": "trx Carol1",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "vendorA_trx-type1",
|
||||
"spacing": 100000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "6",
|
||||
"source": "trx Bob1",
|
||||
"destination": "trx Carol1",
|
||||
"src-tp-id": "trx Bob1",
|
||||
"dst-tp-id": "trx Carol1",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"request-id": "7",
|
||||
"source": "trx Bob1",
|
||||
"destination": "trx Carol",
|
||||
"src-tp-id": "trx Bob1",
|
||||
"dst-tp-id": "trx Carol",
|
||||
"bidirectional": true,
|
||||
"path-constraints": {
|
||||
"te-bandwidth": {
|
||||
"technology": "flexi-grid",
|
||||
"trx_type": "Voyager",
|
||||
"trx_mode": "mode 1",
|
||||
"spacing": 50000000000.0,
|
||||
"path_bandwidth": 100000000000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"synchronization": [
|
||||
{
|
||||
"synchronization-id": "1",
|
||||
"svec": {
|
||||
"relaxable": false,
|
||||
"disjointness": "node link",
|
||||
"request-id-number": [
|
||||
"1",
|
||||
"0"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"gnpy-api:topology_id": "5cf39d4b-be10-4ee9-b38b-7f4db7403db7",
|
||||
"gnpy-api:equipment_id": "9ed86e34-9d41-41b2-b8e4-984ca0901d47"
|
||||
|
||||
}
|
||||
2777
gnpy/yang/api-request.json
Normal file
2777
gnpy/yang/api-request.json
Normal file
File diff suppressed because it is too large
Load Diff
901
gnpy/yang/api-topology.json
Normal file
901
gnpy/yang/api-topology.json
Normal file
@@ -0,0 +1,901 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx Alice",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx Bob",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx Carol",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx Bob1",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx Carol1",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Alice",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Bob",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Carol",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Bob1",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm Carol1",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Alice → Bob)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 75.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Bob → Carol)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Bob1 → Carol1)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.5,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Carol → Dan)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 83.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Dan → Alice)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Bob → Alice)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 75.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Carol → Bob)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Carol1 → Bob1)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.5,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Dan → Carol)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 83.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (Alice → Dan)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 60.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0.0,
|
||||
"con_in": 0.0,
|
||||
"con_out": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "null",
|
||||
"region": "null"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Alice to Bob",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.5,
|
||||
"delta_p": -1.5,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Bob to Carol",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Bob1 to Carol1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Carol to Dan",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Dan to Alice",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 15.600000000000001,
|
||||
"delta_p": -2.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Dan",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Bob to Alice",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.5,
|
||||
"delta_p": -1.5,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Alice to Dan",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 18.0,
|
||||
"delta_p": -2.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Carol to Bob",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Carol1 to Bob1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 19.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Alice to Bob",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.5,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Bob to Carol",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Bob1 to Carol1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Carol to Dan",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.6,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Dan to Alice",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 13.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Dan",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Bob to Alice",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.5,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Bob",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Alice to Dan",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 14.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Alice",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Carol to Bob",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in Carol1 to Bob1",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0.0,
|
||||
"out_voa": 0.0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "Carol",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "trx Alice",
|
||||
"to_node": "roadm Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Bob",
|
||||
"to_node": "roadm Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Bob1",
|
||||
"to_node": "roadm Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Carol",
|
||||
"to_node": "roadm Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "trx Carol1",
|
||||
"to_node": "roadm Carol1"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Alice",
|
||||
"to_node": "east edfa in Alice to Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Alice",
|
||||
"to_node": "east edfa in Alice to Dan"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Alice",
|
||||
"to_node": "trx Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob",
|
||||
"to_node": "east edfa in Bob to Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob1",
|
||||
"to_node": "east edfa in Bob1 to Carol1"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob",
|
||||
"to_node": "east edfa in Bob to Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob",
|
||||
"to_node": "trx Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Bob1",
|
||||
"to_node": "trx Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol",
|
||||
"to_node": "east edfa in Carol to Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol1",
|
||||
"to_node": "east edfa in Carol1 to Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol",
|
||||
"to_node": "east edfa in Carol to Dan"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol",
|
||||
"to_node": "trx Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm Carol1",
|
||||
"to_node": "trx Carol1"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Alice → Bob)-",
|
||||
"to_node": "west edfa in Bob to Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Bob → Carol)-",
|
||||
"to_node": "west edfa in Carol to Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Bob1 → Carol1)-",
|
||||
"to_node": "west edfa in Carol1 to Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Carol → Dan)-",
|
||||
"to_node": "east edfa in Dan to Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Dan → Alice)-",
|
||||
"to_node": "west edfa in Alice to Dan"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Bob → Alice)-",
|
||||
"to_node": "west edfa in Alice to Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Carol → Bob)-",
|
||||
"to_node": "west edfa in Bob to Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Carol1 → Bob1)-",
|
||||
"to_node": "west edfa in Bob1 to Carol1"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Dan → Carol)-",
|
||||
"to_node": "west edfa in Carol to Dan"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (Alice → Dan)-",
|
||||
"to_node": "west edfa in Dan to Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Alice to Bob",
|
||||
"to_node": "fiber (Alice → Bob)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Bob to Carol",
|
||||
"to_node": "fiber (Bob → Carol)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Bob1 to Carol1",
|
||||
"to_node": "fiber (Bob1 → Carol1)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Carol to Dan",
|
||||
"to_node": "fiber (Carol → Dan)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Dan to Alice",
|
||||
"to_node": "fiber (Dan → Alice)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Bob to Alice",
|
||||
"to_node": "fiber (Bob → Alice)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Alice to Dan",
|
||||
"to_node": "fiber (Alice → Dan)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Carol to Bob",
|
||||
"to_node": "fiber (Carol → Bob)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in Carol1 to Bob1",
|
||||
"to_node": "fiber (Carol1 → Bob1)-"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Alice to Bob",
|
||||
"to_node": "roadm Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Bob to Carol",
|
||||
"to_node": "roadm Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Bob1 to Carol1",
|
||||
"to_node": "roadm Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Bob1 to Carol1",
|
||||
"to_node": "roadm Bob1"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Carol to Dan",
|
||||
"to_node": "roadm Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Dan to Alice",
|
||||
"to_node": "fiber (Dan → Carol)-"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Bob to Alice",
|
||||
"to_node": "roadm Bob"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Alice to Dan",
|
||||
"to_node": "roadm Alice"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Carol to Bob",
|
||||
"to_node": "roadm Carol"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in Carol1 to Bob1",
|
||||
"to_node": "roadm Carol1"
|
||||
}
|
||||
]
|
||||
}
|
||||
53
gnpy/yang/gnpy-api@2020-10-22.yang
Normal file
53
gnpy/yang/gnpy-api@2020-10-22.yang
Normal file
@@ -0,0 +1,53 @@
|
||||
module gnpy-api {
|
||||
yang-version 1.1;
|
||||
namespace "gnpy:gnpy-api";
|
||||
prefix gnpyapi;
|
||||
|
||||
import gnpy-network-topology {
|
||||
prefix gnpynt;
|
||||
}
|
||||
import gnpy-path-computation-simplified {
|
||||
prefix gnpypc;
|
||||
}
|
||||
import gnpy-eqpt-config {
|
||||
prefix gnpyeqpt;
|
||||
}
|
||||
|
||||
organization
|
||||
"Telecom Infra Project OOPT PSE Working Group";
|
||||
contact
|
||||
"WG Web: <https://github.com/Telecominfraproject/oopt-gnpy>
|
||||
contact: <mailto:ahmed.triki@orange.com>
|
||||
contact: <mailto:esther.lerouzic@orange.com>
|
||||
";
|
||||
description
|
||||
"YANG model for gnpy api input for path computation - TransportPCE preversion";
|
||||
|
||||
revision 2020-10-22 {
|
||||
description
|
||||
"draft for experimental/2020-candi";
|
||||
reference
|
||||
"YANG model for api input for path computation with gnpy";
|
||||
}
|
||||
|
||||
container service {
|
||||
description
|
||||
"Describe the service file to connect to gnpy";
|
||||
uses gnpypc:service;
|
||||
}
|
||||
container result {
|
||||
uses gnpypc:result;
|
||||
description
|
||||
"Describe the response object to gnpy";
|
||||
}
|
||||
container topology {
|
||||
description
|
||||
"Describe the topology file to connect to gnpy";
|
||||
uses gnpynt:topo;
|
||||
}
|
||||
container equipment {
|
||||
description
|
||||
"Describe the equipment library to connect to gnpy";
|
||||
uses gnpyeqpt:eqpt;
|
||||
}
|
||||
}
|
||||
78
gnpy/yang/gnpy-api@2021-01-06.yang
Normal file
78
gnpy/yang/gnpy-api@2021-01-06.yang
Normal file
@@ -0,0 +1,78 @@
|
||||
module gnpy-api {
|
||||
yang-version 1.1;
|
||||
namespace "gnpy:gnpy-api";
|
||||
prefix gnpyapi;
|
||||
|
||||
import gnpy-network-topology {
|
||||
prefix gnpynt;
|
||||
}
|
||||
import gnpy-path-computation-simplified {
|
||||
prefix gnpypc;
|
||||
}
|
||||
import gnpy-eqpt-config {
|
||||
prefix gnpyeqpt;
|
||||
}
|
||||
import ietf-yang-types {
|
||||
prefix ietftypes;
|
||||
}
|
||||
|
||||
organization
|
||||
"Telecom Infra Project OOPT PSE Working Group";
|
||||
contact
|
||||
"WG Web: <https://github.com/Telecominfraproject/oopt-gnpy>
|
||||
contact: <mailto:ahmed.triki@orange.com>
|
||||
contact: <mailto:esther.lerouzic@orange.com>
|
||||
";
|
||||
description
|
||||
"YANG model for gnpy api input for path computation - TransportPCE preversion";
|
||||
|
||||
revision 2021-01-06 {
|
||||
description
|
||||
"draft for experimental/2020-candi.
|
||||
Add the possibility to use a topology_id or an equipment_id
|
||||
";
|
||||
reference
|
||||
"YANG model for api input for path computation with gnpy";
|
||||
}
|
||||
|
||||
container service {
|
||||
description
|
||||
"Describe the service file to connect to gnpy";
|
||||
uses gnpypc:service;
|
||||
}
|
||||
container result {
|
||||
uses gnpypc:result;
|
||||
description
|
||||
"Describe the response object to gnpy";
|
||||
}
|
||||
choice topo {
|
||||
case explicit {
|
||||
container topology {
|
||||
description
|
||||
"Describe the topology file to connect to gnpy";
|
||||
uses gnpynt:topo;
|
||||
}
|
||||
}
|
||||
case id {
|
||||
leaf topology_id {
|
||||
type ietftypes:uuid;
|
||||
mandatory true;
|
||||
}
|
||||
}
|
||||
}
|
||||
choice eqpt {
|
||||
case explicit {
|
||||
container equipment {
|
||||
description
|
||||
"Describe the equipment library to connect to gnpy";
|
||||
uses gnpyeqpt:eqpt;
|
||||
}
|
||||
}
|
||||
case id {
|
||||
leaf equipment_id {
|
||||
type ietftypes:uuid;
|
||||
mandatory true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
442
gnpy/yang/gnpy-eqpt-config@2020-10-22.yang
Normal file
442
gnpy/yang/gnpy-eqpt-config@2020-10-22.yang
Normal file
@@ -0,0 +1,442 @@
|
||||
module gnpy-eqpt-config {
|
||||
yang-version 1;
|
||||
namespace "gnpy:gnpy-eqpt-config";
|
||||
|
||||
prefix "gnpyeqpt";
|
||||
|
||||
organization
|
||||
"Telecom Infra Project OOPT PSE
|
||||
Working Group";
|
||||
|
||||
contact
|
||||
"WG Web: <https://github.com/Telecominfraproject/oopt-gnpy>
|
||||
contact: <mailto:ahmed.triki@orange.com>
|
||||
contact: <mailto:esther.lerouzic@orange.com>
|
||||
";
|
||||
|
||||
description "Base YANG model for gnpy equipment library input for path computation - 2020 - candi preversion";
|
||||
|
||||
revision "2020-10-22" {
|
||||
description "draft for experimental/2020-candi";
|
||||
reference "Base YANG model for equipment library input for path computation with gnpy";
|
||||
}
|
||||
|
||||
/*
|
||||
* Identities
|
||||
|
||||
|
||||
identity edfa-type-def {
|
||||
description "base identity for variable gain and fixed gain";
|
||||
}
|
||||
|
||||
identity variable-gain{
|
||||
base edfa-type-def ;
|
||||
description "'variable_gain' is a simplified model simulating a 2-coil
|
||||
EDFA with internal, input and output VOAs. The NF vs gain response is calculated
|
||||
accordingly based on the input parameters: nf_min, nf_max, and gain_flatmax. It
|
||||
is not a simple interpolation but a 2-stage NF calculation.";
|
||||
}
|
||||
|
||||
identity fixed-gain{
|
||||
base edfa-type-def ;
|
||||
description "'fixed_gain' is a fixed gain model. NF == Cte == nf0 if gain_min < gain < gain_flatmax";
|
||||
}
|
||||
|
||||
identity fiber-variety {
|
||||
description "base identity for fiber variety";
|
||||
}
|
||||
|
||||
identity transceiver-variety {
|
||||
description "base identity for transceiver variety";
|
||||
}
|
||||
|
||||
*/
|
||||
grouping variable-gain {
|
||||
leaf nf_min {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB;
|
||||
}
|
||||
leaf nf_max {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB;
|
||||
}
|
||||
leaf out_voa_auto{
|
||||
type boolean ;
|
||||
description "auto_design feature to optimize the amplifier output VOA. If true, output VOA is present
|
||||
and will be used to push amplifier gain to its maximum, within EOL power margins.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping fixed-gain{
|
||||
leaf nf0 {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB;
|
||||
}
|
||||
}
|
||||
|
||||
grouping no-type-def{
|
||||
leaf advanced_config_from_json {
|
||||
type string ;
|
||||
description " filename with json edfa";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
grouping openroadm{
|
||||
leaf-list nf_coef {
|
||||
type decimal64 {
|
||||
fraction-digits 5;
|
||||
}
|
||||
//default [8.1e-4,6.142e-2,1.558,19.97] ;
|
||||
}
|
||||
}
|
||||
|
||||
grouping dual-stage {
|
||||
leaf raman {
|
||||
type boolean;
|
||||
}
|
||||
leaf preamp_variety {
|
||||
type leafref {
|
||||
path "../../Edfa/type_variety";
|
||||
}
|
||||
}
|
||||
leaf booster_variety {
|
||||
type leafref {
|
||||
path "../../Edfa/type_variety";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouping edfa-common {
|
||||
leaf allowed_for_design{
|
||||
type boolean ;
|
||||
description "If false, the amplifier will not be picked by auto-design but it can still be used as a
|
||||
manual input (from JSON or Excel template topology files.)";
|
||||
}
|
||||
leaf gain_flatmax {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB;
|
||||
}
|
||||
leaf gain_min {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB;
|
||||
}
|
||||
leaf p_max {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dBm;
|
||||
}
|
||||
leaf type_def {
|
||||
type identityref{
|
||||
base edfa-type-def ;
|
||||
}
|
||||
}
|
||||
choice type_of_model {
|
||||
case variable-gain {
|
||||
when "type_def = 'variable-gain'";
|
||||
uses variable-gain ;
|
||||
}
|
||||
case fixed-gain{
|
||||
when "type_def = 'fixed-gain'";
|
||||
uses fixed-gain;
|
||||
}
|
||||
case no-type-def{
|
||||
when "type_def = 'no-type-def'";
|
||||
uses no-type-def;
|
||||
}
|
||||
case openroadm{
|
||||
when "type_def = 'openroadm'";
|
||||
uses openroadm;
|
||||
}
|
||||
case dual_stage {
|
||||
when "type_def = 'dual_stage'";
|
||||
uses dual-stage ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouping common-fiber {
|
||||
description "common parameters for fiber and raman fiber";
|
||||
leaf type_variety {
|
||||
type string ;
|
||||
}
|
||||
description "a unique name to ID the fiber in the JSON or Excel template topology input file";
|
||||
leaf dispersion{
|
||||
type decimal64 {
|
||||
fraction-digits 8;
|
||||
}
|
||||
units s.m-1.m-1;
|
||||
}
|
||||
leaf gamma{
|
||||
type decimal64 {
|
||||
fraction-digits 8;
|
||||
}
|
||||
units w-1.m-1 ;
|
||||
description "2pi.n2/(lambda*Aeff) (w-2.m-1)";
|
||||
}
|
||||
leaf pmd_coef{
|
||||
type decimal64 {
|
||||
fraction-digits 16;
|
||||
}
|
||||
units s.sqrt(m)-1;
|
||||
}
|
||||
}
|
||||
|
||||
grouping eqpt{
|
||||
list Edfa {
|
||||
key type_variety;
|
||||
leaf type_variety {
|
||||
type string;
|
||||
description "a unique name to ID the amplifier in the JSON/Excel template topology input file";
|
||||
}
|
||||
uses edfa-common;
|
||||
}
|
||||
|
||||
list Fiber {
|
||||
key type_variety;
|
||||
uses common-fiber;
|
||||
}
|
||||
|
||||
list RamanFiber {
|
||||
uses common-fiber;
|
||||
container raman_efficiency {
|
||||
leaf-list cr {
|
||||
type decimal64 {
|
||||
fraction-digits 8;
|
||||
}
|
||||
}
|
||||
leaf-list frequency_offset {
|
||||
type decimal64 {
|
||||
fraction-digits 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list Span {
|
||||
leaf power_mode {
|
||||
type boolean ;
|
||||
}
|
||||
leaf-list delta_power_range_db {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf max_length {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units km;
|
||||
default 150.0 ;
|
||||
}
|
||||
leaf max_loss {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB;
|
||||
}
|
||||
leaf max_fiber_lineic_loss_for_raman {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB.km-1;
|
||||
}
|
||||
leaf target_extended_gain {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB;
|
||||
}
|
||||
leaf length_units{
|
||||
type string ;
|
||||
default "km";
|
||||
}
|
||||
leaf padding{
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
default 10.0 ;
|
||||
}
|
||||
leaf EOL{
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
default 0.0 ;
|
||||
}
|
||||
leaf con_in{
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
default 0.0 ;
|
||||
}
|
||||
leaf con_out{
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
default 0.0 ;
|
||||
}
|
||||
}
|
||||
|
||||
list Roadm {
|
||||
leaf target_pch_out_db {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf add_drop_osnr {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf pmd {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
container restrictions {
|
||||
leaf-list preamp_variety_list {
|
||||
type string;
|
||||
}
|
||||
leaf-list booster_variety_list {
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list SI {
|
||||
leaf f_min {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf f_max {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf baud_rate {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf spacing {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf power_dbm {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf-list power_range_db {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf roll_off {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf tx_osnr {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
leaf sys_margins {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list Transceiver {
|
||||
leaf type_variety {
|
||||
type string ;
|
||||
description "a unique name to ID the transceiver in the JSON or Excel template topology input file";
|
||||
}
|
||||
container frequency {
|
||||
leaf min {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units Hz ;
|
||||
}
|
||||
leaf max {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units Hz ;
|
||||
}
|
||||
description "Min/max frequency of transponder eg 191.35e12 and 196.1e12";
|
||||
}
|
||||
list mode {
|
||||
leaf format {
|
||||
type string ;
|
||||
description "unique name of the mode";
|
||||
}
|
||||
leaf baud_rate {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units baud ;
|
||||
description "baud_rate";
|
||||
}
|
||||
leaf OSNR {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB ;
|
||||
description "min required OSNR in 0.1nm (dB)";
|
||||
}
|
||||
leaf tx_osnr {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units dB ;
|
||||
description "min required OSNR in 0.1nm (dB)";
|
||||
}
|
||||
leaf min_spacing {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units GHz ;
|
||||
description "...";
|
||||
}
|
||||
leaf bit_rate {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units bit/s ;
|
||||
description "bit rate";
|
||||
}
|
||||
leaf roll_off {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
description "...";
|
||||
}
|
||||
leaf cost {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
description "arbitrary unit";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
300
gnpy/yang/gnpy-network-topology@2020-10-22.yang
Normal file
300
gnpy/yang/gnpy-network-topology@2020-10-22.yang
Normal file
@@ -0,0 +1,300 @@
|
||||
module gnpy-network-topology {
|
||||
yang-version 1.1;
|
||||
namespace "gnpy:gnpy-network-topology";
|
||||
prefix gnpynt;
|
||||
|
||||
organization
|
||||
"Telecom Infra Project OOPT PSE Working Group";
|
||||
contact
|
||||
"WG Web: <https://github.com/Telecominfraproject/oopt-gnpy>
|
||||
contact: <mailto:ahmed.triki@orange.com>
|
||||
contact: <mailto:esther.lerouzic@orange.com>
|
||||
";
|
||||
description
|
||||
"YANG model for gnpy network input for path computation - 2020 - candi preversion";
|
||||
|
||||
revision 2020-10-22 {
|
||||
description
|
||||
"draft for experimental/2020-candi";
|
||||
reference
|
||||
"YANG model for network input for path computation with gnpy";
|
||||
}
|
||||
|
||||
identity type-element {
|
||||
description
|
||||
"Base identity for element type";
|
||||
}
|
||||
|
||||
identity Transceiver {
|
||||
base type-element;
|
||||
description
|
||||
" Transceiver element";
|
||||
}
|
||||
|
||||
identity Fiber {
|
||||
base type-element;
|
||||
description
|
||||
"Fiber element (unidirectional)";
|
||||
}
|
||||
|
||||
identity Roadm {
|
||||
base type-element;
|
||||
description
|
||||
"Roadm element";
|
||||
}
|
||||
|
||||
identity Edfa {
|
||||
base type-element;
|
||||
description
|
||||
"Edfa element";
|
||||
}
|
||||
|
||||
identity Fused {
|
||||
base type-element;
|
||||
description
|
||||
"Fused element ; non amplified connection between two fiber spans ;
|
||||
can be used to model optical distribution frame, or losses due to
|
||||
connectors or fused in a span";
|
||||
}
|
||||
|
||||
identity length-unit {
|
||||
description
|
||||
"length unit";
|
||||
}
|
||||
|
||||
identity km {
|
||||
base length-unit;
|
||||
description
|
||||
"kilometers";
|
||||
}
|
||||
|
||||
identity m {
|
||||
base length-unit;
|
||||
description
|
||||
"meter";
|
||||
}
|
||||
|
||||
typedef Coordinate {
|
||||
type decimal64 {
|
||||
fraction-digits 6;
|
||||
}
|
||||
}
|
||||
|
||||
typedef Coef {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
|
||||
grouping location-attributes {
|
||||
container location {
|
||||
leaf city {
|
||||
type string;
|
||||
mandatory true;
|
||||
}
|
||||
leaf region {
|
||||
type string;
|
||||
mandatory true;
|
||||
}
|
||||
leaf latitude {
|
||||
type Coordinate;
|
||||
mandatory true;
|
||||
}
|
||||
leaf longitude {
|
||||
type Coordinate;
|
||||
mandatory true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouping fiber-params {
|
||||
description
|
||||
".....";
|
||||
leaf length {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
mandatory true;
|
||||
}
|
||||
leaf loss_coef {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
mandatory true;
|
||||
units db/km;
|
||||
description "Loss coefficient of the fiber span (dB/km)";
|
||||
}
|
||||
leaf length_units {
|
||||
type identityref {
|
||||
base length-unit;
|
||||
}
|
||||
mandatory true;
|
||||
}
|
||||
leaf att_in {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units "dB";
|
||||
mandatory true;
|
||||
}
|
||||
leaf con_in {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units "dB";
|
||||
mandatory true;
|
||||
}
|
||||
leaf con_out {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units "dB";
|
||||
mandatory true;
|
||||
}
|
||||
}
|
||||
|
||||
grouping edfa-params {
|
||||
container operational {
|
||||
description
|
||||
"Operational values for the Edfa ";
|
||||
leaf gain_target {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units "dB";
|
||||
mandatory true;
|
||||
description
|
||||
"gain target of the amplifier (before VOA and after att_in)";
|
||||
}
|
||||
leaf tilt_target {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
mandatory true;
|
||||
description
|
||||
"..";
|
||||
}
|
||||
leaf out_voa {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units "dB";
|
||||
mandatory true;
|
||||
description
|
||||
"..";
|
||||
}
|
||||
leaf delta_p {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units "dB";
|
||||
mandatory true;
|
||||
description
|
||||
"per channel target output power delta with respect to power setting in SI";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouping roadm-params {
|
||||
leaf target_pch_out_db {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units "dB";
|
||||
description
|
||||
"..";
|
||||
}
|
||||
container restrictions {
|
||||
leaf-list preamp_variety_list {
|
||||
type string;
|
||||
description
|
||||
"List of authorized preamp type-variety";
|
||||
}
|
||||
leaf-list booster_variety_list {
|
||||
type string;
|
||||
description
|
||||
"List of authorized booster type-variety";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouping transceiver-params;
|
||||
|
||||
grouping fused-params{
|
||||
leaf loss {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
units "dB";
|
||||
description
|
||||
"Concentrated loss of the fused element";
|
||||
}
|
||||
}
|
||||
|
||||
grouping element-type-choice {
|
||||
choice element-type {
|
||||
case Edfa {
|
||||
when "type = 'Edfa'";
|
||||
uses edfa-params;
|
||||
}
|
||||
case FiberRoadm {
|
||||
container params {
|
||||
choice fiberroadmfused {
|
||||
case Fiber {
|
||||
when "type = 'Fiber'";
|
||||
uses fiber-params;
|
||||
}
|
||||
case Roadm {
|
||||
when "type = 'Roadm'";
|
||||
uses roadm-params;
|
||||
}
|
||||
case Fused {
|
||||
when "type = 'Fused'";
|
||||
uses fused-params;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case Transceiver {
|
||||
when "type = 'Transceiver'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
grouping topo {
|
||||
list elements {
|
||||
key "uid";
|
||||
leaf uid {
|
||||
type string;
|
||||
}
|
||||
leaf type {
|
||||
type identityref {
|
||||
base type-element;
|
||||
}
|
||||
mandatory true;
|
||||
}
|
||||
leaf type_variety {
|
||||
type string;
|
||||
mandatory false;
|
||||
}
|
||||
container metadata {
|
||||
uses location-attributes;
|
||||
}
|
||||
uses element-type-choice;
|
||||
}
|
||||
list connections {
|
||||
config false;
|
||||
leaf from_node {
|
||||
type leafref {
|
||||
path "../../elements/uid";
|
||||
}
|
||||
}
|
||||
leaf to_node {
|
||||
type leafref {
|
||||
path "../../elements/uid";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
559
gnpy/yang/gnpy-path-computation-simplified@2020-10-22.yang
Normal file
559
gnpy/yang/gnpy-path-computation-simplified@2020-10-22.yang
Normal file
@@ -0,0 +1,559 @@
|
||||
module gnpy-path-computation-simplified {
|
||||
yang-version 1.1;
|
||||
namespace "gnpy:path";
|
||||
|
||||
prefix "gnpypc";
|
||||
|
||||
organization
|
||||
"Telecom Infra Project OOPT PSE Working Group";
|
||||
|
||||
contact
|
||||
"WG Web: <https://github.com/Telecominfraproject/oopt-gnpy>
|
||||
contact: <mailto:ahmed.triki@orange.com>
|
||||
contact: <mailto:esther.lerouzic@orange.com>
|
||||
";
|
||||
|
||||
description "YANG model for gnpy path computation simplified for - 2020 - candi preversion";
|
||||
|
||||
revision "2020-10-22" {
|
||||
description
|
||||
"draft for experimental/2020-candi";
|
||||
reference
|
||||
"YANG model for path computation with gnpy inputs";
|
||||
}
|
||||
|
||||
grouping effective-freq-slot{
|
||||
/* content copied from ietf-flexi-grid-media-channel, because only M and N are needed
|
||||
from the initial grouping.
|
||||
*/
|
||||
description "The effective frequency slot is an attribute
|
||||
of a media channel and, being a frequency slot, it is
|
||||
described by its nominal central frequency and slot
|
||||
width";
|
||||
reference "rfc7698";
|
||||
leaf N {
|
||||
type uint32;
|
||||
description
|
||||
"Is used to determine the Nominal Central
|
||||
Frequency. The set of nominal central frequencies
|
||||
can be built using the following expression:
|
||||
f = 193.1 THz + n x 0.00625 THz,
|
||||
where 193.1 THz is ITU-T ''anchor frequency'' for
|
||||
transmission over the C band, n is a positive or
|
||||
negative integer including 0.";
|
||||
reference "rfc7698";
|
||||
}
|
||||
leaf M {
|
||||
type int32;
|
||||
description
|
||||
"Is used to determine the slot width. A slot width
|
||||
is constrained to be M x SWG (that is, M x 12.5 GHz),
|
||||
where M is an integer greater than or equal to 1.";
|
||||
reference "rfc7698";
|
||||
}
|
||||
}
|
||||
|
||||
grouping gnpy-specific-parameters{
|
||||
description
|
||||
"This grouping defines the gnpy specific parameters for requests.";
|
||||
leaf technology {
|
||||
type string;
|
||||
default "flexi-grid";
|
||||
description
|
||||
"Data plane technology type.";
|
||||
}
|
||||
leaf trx_type {
|
||||
type string ;
|
||||
mandatory true;
|
||||
description "name of the transponder type (to be read from equipment library";
|
||||
|
||||
}
|
||||
leaf trx_mode {
|
||||
type string;
|
||||
description "name of the transponder mode (to be read from equipment library";
|
||||
|
||||
}
|
||||
list effective-freq-slot {
|
||||
key "N";
|
||||
uses effective-freq-slot ;
|
||||
}
|
||||
leaf spacing {
|
||||
mandatory true;
|
||||
type decimal64 {
|
||||
fraction-digits 1;
|
||||
}
|
||||
units Hz;
|
||||
description
|
||||
"It is the spacing between channels assuming full load with
|
||||
same channels as the requested one. multiple of 12.5 GHz";
|
||||
|
||||
}
|
||||
leaf max-nb-of-channel{
|
||||
type int32;
|
||||
description "Nb of channel to take into account for the full load case.
|
||||
";
|
||||
|
||||
}
|
||||
leaf output-power{
|
||||
type decimal64 {
|
||||
fraction-digits 5;
|
||||
}
|
||||
units W;
|
||||
description "optical power setting to be used for the propagation";
|
||||
|
||||
}
|
||||
leaf path_bandwidth{
|
||||
type decimal64 {
|
||||
fraction-digits 5;
|
||||
}
|
||||
mandatory true;
|
||||
units bit/s;
|
||||
description "Capacity required";
|
||||
}
|
||||
}
|
||||
|
||||
identity SNR-bandwidth {
|
||||
base path-metric-type;
|
||||
description
|
||||
"A metric that records SNR in signal bandwidth";
|
||||
}
|
||||
|
||||
identity OSNR-bandwidth {
|
||||
base path-metric-type;
|
||||
description
|
||||
"A metric that records OSNR in signal bandwidth";
|
||||
}
|
||||
|
||||
identity SNR-0.1nm {
|
||||
base path-metric-type;
|
||||
description
|
||||
"A metric that records SNR in 0.1nm";
|
||||
}
|
||||
|
||||
identity OSNR-0.1nm {
|
||||
base path-metric-type;
|
||||
description
|
||||
"A metric that records OSNR in 0.1nm";
|
||||
}
|
||||
|
||||
identity reference_power {
|
||||
base path-metric-type;
|
||||
description
|
||||
"to be revised";
|
||||
}
|
||||
|
||||
identity path_bandwidth {
|
||||
base path-metric-type;
|
||||
description
|
||||
"to be revised";
|
||||
}
|
||||
|
||||
grouping transponder{
|
||||
leaf transponder-type {
|
||||
type string ;
|
||||
description
|
||||
"transponder type.";
|
||||
}
|
||||
leaf transponder-mode {
|
||||
type string ;
|
||||
description
|
||||
"transponder mode.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping hop-attribute{
|
||||
description
|
||||
"This grouping defines the hop attribute parameters for request or response";
|
||||
choice hop-type{
|
||||
case tsp {
|
||||
container transponder{
|
||||
uses transponder ;
|
||||
}
|
||||
}
|
||||
case regen {
|
||||
container regenerator{
|
||||
leaf regenerator-id{
|
||||
type string ;
|
||||
}
|
||||
uses transponder ;
|
||||
}
|
||||
}
|
||||
case pow {
|
||||
container optical-power{
|
||||
leaf optical-power{
|
||||
type decimal64 {
|
||||
fraction-digits 5;
|
||||
}
|
||||
units W;
|
||||
description "not used yet. hop output (input??) power";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
identity path-metric-type {
|
||||
description
|
||||
"Base identity for path metric type";
|
||||
}
|
||||
|
||||
identity route-usage-type {
|
||||
description
|
||||
"Base identity for route usage";
|
||||
}
|
||||
|
||||
identity route-include-ero {
|
||||
base route-usage-type;
|
||||
description
|
||||
"Include ERO from route";
|
||||
}
|
||||
|
||||
identity route-exclude-ero {
|
||||
base route-usage-type;
|
||||
description
|
||||
"Exclude ERO from route";
|
||||
}
|
||||
|
||||
identity route-exclude-srlg {
|
||||
base route-usage-type;
|
||||
description
|
||||
"Exclude SRLG from route";
|
||||
}
|
||||
|
||||
typedef te-hop-type {
|
||||
type enumeration {
|
||||
enum LOOSE {
|
||||
description
|
||||
"loose hop in an explicit path";
|
||||
}
|
||||
enum STRICT {
|
||||
description
|
||||
"strict hop in an explicit path";
|
||||
}
|
||||
}
|
||||
description
|
||||
"enumerated type for specifying loose or strict
|
||||
paths";
|
||||
reference "RFC3209: section-4.3.2";
|
||||
}
|
||||
|
||||
typedef te-path-disjointness {
|
||||
type bits {
|
||||
bit node {
|
||||
position 0;
|
||||
description "Node disjoint.";
|
||||
}
|
||||
bit link {
|
||||
position 1;
|
||||
description "Link disjoint.";
|
||||
}
|
||||
bit srlg {
|
||||
position 2;
|
||||
description "SRLG (Shared Risk Link Group) disjoint.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"Type of the resource disjointness for a TE tunnel path.";
|
||||
reference
|
||||
"RFC4872: RSVP-TE Extensions in Support of End-to-End
|
||||
Generalized Multi-Protocol Label Switching (GMPLS)
|
||||
Recovery";
|
||||
} // te-path-disjointness
|
||||
|
||||
typedef accumulated-metric-type {
|
||||
type union {
|
||||
type uint64;
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
}
|
||||
description
|
||||
"type useable for accumulative-value";
|
||||
}
|
||||
|
||||
grouping path-route-objects {
|
||||
description
|
||||
"List of EROs to be included or excluded when performing
|
||||
the path computation.";
|
||||
container explicit-route-objects {
|
||||
description
|
||||
"Container for the route object list";
|
||||
list route-object-include-exclude {
|
||||
description
|
||||
"List of explicit route objects to include or
|
||||
exclude in path computation";
|
||||
leaf explicit-route-usage {
|
||||
type identityref {
|
||||
base route-usage-type;
|
||||
}
|
||||
description "Explicit-route usage.";
|
||||
}
|
||||
key "index";
|
||||
uses explicit-route-hop ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouping generic-path-disjointness {
|
||||
description "Path disjointness grouping";
|
||||
leaf disjointness {
|
||||
type te-path-disjointness;
|
||||
description
|
||||
"The type of resource disjointness.
|
||||
Under primary path, disjointness level applies to
|
||||
all secondary LSPs. Under secondary, disjointness
|
||||
level overrides the one under primary";
|
||||
}
|
||||
}
|
||||
|
||||
grouping common-path-constraints-attributes {
|
||||
description
|
||||
"Common path constraints configuration grouping";
|
||||
uses common-constraints_config;
|
||||
}
|
||||
|
||||
grouping generic-path-constraints {
|
||||
description
|
||||
"Global named path constraints configuration
|
||||
grouping";
|
||||
container path-constraints {
|
||||
description "TE named path constraints container";
|
||||
uses common-path-constraints-attributes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
grouping explicit-route-hop {
|
||||
description
|
||||
"The explicit route subobject grouping";
|
||||
leaf index {
|
||||
type uint32;
|
||||
description "ERO subobject index";
|
||||
}
|
||||
choice type {
|
||||
description
|
||||
"The explicit route subobject type";
|
||||
case num-unnum-hop {
|
||||
container num-unnum-hop {
|
||||
leaf node-id {
|
||||
//type te-node-id;
|
||||
type string;
|
||||
description
|
||||
"The identifier of a node in the TE topology.";
|
||||
}
|
||||
leaf link-tp-id {
|
||||
//type te-tp-id;
|
||||
type string;
|
||||
description
|
||||
"TE link termination point identifier. The combination
|
||||
of TE link ID and the TE node ID is used to identify an
|
||||
unnumbered TE link.";
|
||||
}
|
||||
leaf hop-type {
|
||||
type te-hop-type;
|
||||
description "strict or loose hop";
|
||||
}
|
||||
description
|
||||
"Numbered and Unnumbered link/node explicit route
|
||||
subobject";
|
||||
}
|
||||
}
|
||||
case label {
|
||||
container label-hop {
|
||||
description "Label hop type";
|
||||
uses effective-freq-slot;
|
||||
}
|
||||
description
|
||||
"The Label ERO subobject";
|
||||
}
|
||||
case hop-attribute{
|
||||
uses gnpypc:hop-attribute ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouping common-constraints_config {
|
||||
description
|
||||
"Common constraints grouping that can be set on
|
||||
a constraint set or directly on the tunnel";
|
||||
|
||||
container te-bandwidth {
|
||||
uses gnpy-specific-parameters ;
|
||||
description
|
||||
"A requested bandwidth to use for path computation";
|
||||
}
|
||||
}
|
||||
|
||||
grouping end-points {
|
||||
description
|
||||
"Common grouping to define the TE tunnel end-points";
|
||||
|
||||
leaf source {
|
||||
type string;
|
||||
description "TE tunnel source address.";
|
||||
}
|
||||
leaf destination {
|
||||
type string;
|
||||
description "P2P tunnel destination address";
|
||||
}
|
||||
leaf src-tp-id {
|
||||
type string;
|
||||
description "TE tunnel source termination point identifier.";
|
||||
}
|
||||
leaf dst-tp-id {
|
||||
type string;
|
||||
description "TE tunnel destination termination point
|
||||
identifier.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping synchronization-info {
|
||||
description "Information for sync";
|
||||
list synchronization {
|
||||
key "synchronization-id";
|
||||
description "sync list";
|
||||
leaf synchronization-id {
|
||||
type string;
|
||||
description "index";
|
||||
}
|
||||
container svec {
|
||||
description
|
||||
"Synchronization VECtor";
|
||||
leaf relaxable {
|
||||
type boolean;
|
||||
default true;
|
||||
description
|
||||
"If this leaf is true, path computation process is free
|
||||
to ignore svec content.
|
||||
otherwise it must take into account this svec.";
|
||||
}
|
||||
uses generic-path-disjointness;
|
||||
leaf-list request-id-number {
|
||||
type string;
|
||||
description "This list reports the set of M path computation requests that must be synchronized.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouping path-metric {
|
||||
description "TE path metric type";
|
||||
leaf metric-type {
|
||||
type identityref {
|
||||
base path-metric-type;
|
||||
}
|
||||
description "TE path metric type";
|
||||
}
|
||||
leaf accumulative-value {
|
||||
type decimal64 {
|
||||
fraction-digits 2;
|
||||
}
|
||||
description "TE path metric accumulative value";
|
||||
}
|
||||
}
|
||||
grouping generic-path-properties {
|
||||
description "TE generic path properties grouping";
|
||||
container path-properties {
|
||||
config false;
|
||||
description "The TE path properties";
|
||||
list path-metric {
|
||||
key metric-type;
|
||||
uses path-metric;
|
||||
}
|
||||
list z-a-path-metric {
|
||||
key metric-type;
|
||||
uses path-metric;
|
||||
}
|
||||
list path-route-objects {
|
||||
description
|
||||
"Container for the list of route objects either returned by
|
||||
the computation engine or actually used by an LSP";
|
||||
container path-route-object {
|
||||
description
|
||||
"List of route objects either returned by the computation
|
||||
engine or actually used by an LSP";
|
||||
uses explicit-route-hop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
grouping path-info {
|
||||
uses generic-path-properties;
|
||||
description "Path computation output information";
|
||||
}
|
||||
|
||||
// adding some blocking reasons and info on path in case of blocking
|
||||
|
||||
grouping no-path-info {
|
||||
description "no-path-info";
|
||||
container no-path {
|
||||
presence "Response without path information, due to failure
|
||||
performing the path computation";
|
||||
leaf no-path {
|
||||
type string;
|
||||
mandatory true ;
|
||||
description
|
||||
"returned blocking reasons:
|
||||
NO_PATH
|
||||
NO_COMPUTED_SNR
|
||||
NO_FEASIBLE_BAUDRATE_WITH_SPACING
|
||||
NO_PATH_WITH_CONSTRAINT
|
||||
NO_FEASIBLE_MODE
|
||||
MODE_NOT_FEASIBLE
|
||||
NO_SPECTRUM
|
||||
";
|
||||
}
|
||||
uses generic-path-properties ;
|
||||
description "if path computation cannot identify a path,
|
||||
rpc returns no path.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping service {
|
||||
list path-request {
|
||||
key "request-id";
|
||||
description "request-list";
|
||||
leaf request-id {
|
||||
type string;
|
||||
mandatory true;
|
||||
description "Each path computation request is uniquely identified by the request-id-number.";
|
||||
}
|
||||
leaf bidirectional {
|
||||
type boolean;
|
||||
mandatory true;
|
||||
description "Specify the bidirectionality of the path";
|
||||
}
|
||||
|
||||
uses end-points;
|
||||
uses path-route-objects;
|
||||
uses generic-path-constraints;
|
||||
}
|
||||
uses synchronization-info;
|
||||
}
|
||||
|
||||
grouping result {
|
||||
list response {
|
||||
key response-id;
|
||||
config false;
|
||||
description "response";
|
||||
leaf response-id {
|
||||
type string;
|
||||
description
|
||||
"The list key that has to reuse request-id-number.";
|
||||
}
|
||||
choice response-type {
|
||||
config false;
|
||||
description "response-type";
|
||||
case no-path-case {
|
||||
uses no-path-info;
|
||||
}
|
||||
case path-case {
|
||||
uses path-info;
|
||||
description "Path computation service.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,18 @@
|
||||
alabaster>=0.7.12,<1
|
||||
docutils==0.15.2
|
||||
matplotlib>=3.1.0,<4
|
||||
networkx>=2.3,<3
|
||||
numpy>=1.16.1,<2
|
||||
pandas==0.24.2
|
||||
pbr>=5.4.4,<6
|
||||
Pygments>=2.4.2,<3
|
||||
scipy>=1.3.0,<2
|
||||
Sphinx>=2.4.4,<3
|
||||
matplotlib>=3.3.3,<4
|
||||
networkx>=2.5,<3
|
||||
numpy>=1.19.4,<2
|
||||
pandas>=1.1.5,<2
|
||||
pbr>=5.5.1,<6
|
||||
Pygments>=2.7.4,<3
|
||||
scipy>=1.5.4,<2
|
||||
Sphinx>=3.5.0,<4
|
||||
sphinxcontrib-bibtex>=0.4.2,<1
|
||||
xlrd>=1.2.0,<2
|
||||
flask>=1.1.2
|
||||
gnpy~=2.2.0
|
||||
cryptography~=3.3.1
|
||||
Werkzeug~=1.0.1
|
||||
setuptools~=50.3.1
|
||||
Flask-Injector
|
||||
@@ -12,7 +12,7 @@ project_urls =
|
||||
Documentation = https://gnpy.readthedocs.io/
|
||||
python-requires = >=3.6
|
||||
classifier =
|
||||
Development Status :: 3 - Alpha
|
||||
Development Status :: 5 - Production/Stable
|
||||
Intended Audience :: Developers
|
||||
Intended Audience :: Science/Research
|
||||
Intended Audience :: Telecommunications Industry
|
||||
@@ -21,9 +21,12 @@ classifier =
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 3 :: Only
|
||||
Programming Language :: Python :: 3.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: Implementation :: CPython
|
||||
Topic :: Scientific/Engineering
|
||||
Topic :: Scientific/Engineering :: Physics
|
||||
Topic :: System :: Networking
|
||||
keywords =
|
||||
optics
|
||||
network
|
||||
@@ -48,3 +51,4 @@ console_scripts =
|
||||
gnpy-transmission-example = gnpy.tools.cli_examples:transmission_main_example
|
||||
gnpy-path-request = gnpy.tools.cli_examples:path_requests_run
|
||||
gnpy-convert-xls = gnpy.tools.convert:_do_convert
|
||||
gnpy-rest = gnpy.api.rest_example:main
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
BIN
tests/data/perdegreemeshTopologyExampleV2.xls
Normal file
BIN
tests/data/perdegreemeshTopologyExampleV2.xls
Normal file
Binary file not shown.
1671
tests/data/perdegreemeshTopologyExampleV2_auto_design_expected.json
Normal file
1671
tests/data/perdegreemeshTopologyExampleV2_auto_design_expected.json
Normal file
File diff suppressed because it is too large
Load Diff
1258
tests/data/perdegreemeshTopologyExampleV2_expected.json
Normal file
1258
tests/data/perdegreemeshTopologyExampleV2_expected.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,8 +14,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -39,8 +39,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -64,8 +64,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -79,8 +79,8 @@
|
||||
"path-route-object": {
|
||||
"index": 5,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa1_roadm Lorient_KMA",
|
||||
"link-tp-id": "Edfa1_roadm Lorient_KMA"
|
||||
"node-id": "east edfa in Lorient_KMA to Vannes_KBE",
|
||||
"link-tp-id": "east edfa in Lorient_KMA to Vannes_KBE"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -115,8 +115,8 @@
|
||||
"path-route-object": {
|
||||
"index": 9,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055",
|
||||
"link-tp-id": "Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055"
|
||||
"node-id": "west edfa in Vannes_KBE to Lorient_KMA",
|
||||
"link-tp-id": "west edfa in Vannes_KBE to Lorient_KMA"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -256,8 +256,8 @@
|
||||
"path-route-object": {
|
||||
"index": 5,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_roadm Brest_KLA",
|
||||
"link-tp-id": "Edfa0_roadm Brest_KLA"
|
||||
"node-id": "east edfa in Brest_KLA to Morlaix",
|
||||
"link-tp-id": "east edfa in Brest_KLA to Morlaix"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -472,8 +472,8 @@
|
||||
"path-route-object": {
|
||||
"index": 29,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_fiber (Loudeac → Lorient_KMA)-F054",
|
||||
"link-tp-id": "Edfa0_fiber (Loudeac → Lorient_KMA)-F054"
|
||||
"node-id": "west edfa in Lorient_KMA to Loudeac",
|
||||
"link-tp-id": "west edfa in Lorient_KMA to Loudeac"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -508,8 +508,8 @@
|
||||
"path-route-object": {
|
||||
"index": 33,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa1_roadm Lorient_KMA",
|
||||
"link-tp-id": "Edfa1_roadm Lorient_KMA"
|
||||
"node-id": "east edfa in Lorient_KMA to Vannes_KBE",
|
||||
"link-tp-id": "east edfa in Lorient_KMA to Vannes_KBE"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -544,8 +544,8 @@
|
||||
"path-route-object": {
|
||||
"index": 37,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055",
|
||||
"link-tp-id": "Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055"
|
||||
"node-id": "west edfa in Vannes_KBE to Lorient_KMA",
|
||||
"link-tp-id": "west edfa in Vannes_KBE to Lorient_KMA"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -757,8 +757,8 @@
|
||||
"path-route-object": {
|
||||
"index": 13,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_fiber (Stbrieuc → Rennes_STA)-F057",
|
||||
"link-tp-id": "Edfa0_fiber (Stbrieuc → Rennes_STA)-F057"
|
||||
"node-id": "west edfa in Rennes_STA to Stbrieuc",
|
||||
"link-tp-id": "west edfa in Rennes_STA to Stbrieuc"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -898,8 +898,8 @@
|
||||
"path-route-object": {
|
||||
"index": 5,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa1_roadm Rennes_STA",
|
||||
"link-tp-id": "Edfa1_roadm Rennes_STA"
|
||||
"node-id": "east edfa in Rennes_STA to Ploermel",
|
||||
"link-tp-id": "east edfa in Rennes_STA to Ploermel"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -970,8 +970,8 @@
|
||||
"path-route-object": {
|
||||
"index": 13,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_fiber (Ploermel → Vannes_KBE)-",
|
||||
"link-tp-id": "Edfa0_fiber (Ploermel → Vannes_KBE)-"
|
||||
"node-id": "west edfa in Vannes_KBE to Ploermel",
|
||||
"link-tp-id": "west edfa in Vannes_KBE to Ploermel"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1006,8 +1006,8 @@
|
||||
"path-route-object": {
|
||||
"index": 17,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_roadm Vannes_KBE",
|
||||
"link-tp-id": "Edfa0_roadm Vannes_KBE"
|
||||
"node-id": "east edfa in Vannes_KBE to Lorient_KMA",
|
||||
"link-tp-id": "east edfa in Vannes_KBE to Lorient_KMA"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1042,8 +1042,8 @@
|
||||
"path-route-object": {
|
||||
"index": 21,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055",
|
||||
"link-tp-id": "Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055"
|
||||
"node-id": "west edfa in Lorient_KMA to Vannes_KBE",
|
||||
"link-tp-id": "west edfa in Lorient_KMA to Vannes_KBE"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1078,8 +1078,8 @@
|
||||
"path-route-object": {
|
||||
"index": 25,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_roadm Lorient_KMA",
|
||||
"link-tp-id": "Edfa0_roadm Lorient_KMA"
|
||||
"node-id": "east edfa in Lorient_KMA to Loudeac",
|
||||
"link-tp-id": "east edfa in Lorient_KMA to Loudeac"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1327,8 +1327,8 @@
|
||||
"path-route-object": {
|
||||
"index": 5,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_roadm Rennes_STA",
|
||||
"link-tp-id": "Edfa0_roadm Rennes_STA"
|
||||
"node-id": "east edfa in Rennes_STA to Stbrieuc",
|
||||
"link-tp-id": "east edfa in Rennes_STA to Stbrieuc"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1363,8 +1363,8 @@
|
||||
"path-route-object": {
|
||||
"index": 9,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_fiber (Rennes_STA → Stbrieuc)-F057",
|
||||
"link-tp-id": "Edfa0_fiber (Rennes_STA → Stbrieuc)-F057"
|
||||
"node-id": "west edfa in Stbrieuc to Rennes_STA",
|
||||
"link-tp-id": "west edfa in Stbrieuc to Rennes_STA"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1399,8 +1399,8 @@
|
||||
"path-route-object": {
|
||||
"index": 13,
|
||||
"num-unnum-hop": {
|
||||
"node-id": "Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056",
|
||||
"link-tp-id": "Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056"
|
||||
"node-id": "west edfa in Lannion_CAS to Stbrieuc",
|
||||
"link-tp-id": "west edfa in Lannion_CAS to Stbrieuc"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
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,,,,,,,,,,,
|
||||
|
||||
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 | east edfa in Lorient_KMA to Vannes_KBE | fiber (Lorient_KMA → Vannes_KBE)-F055 | west edfa in Vannes_KBE to Lorient_KMA | 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 | east edfa in Brest_KLA to Morlaix | 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 | west edfa in Lorient_KMA to Loudeac | roadm Lorient_KMA | east edfa in Lorient_KMA to Vannes_KBE | fiber (Lorient_KMA → Vannes_KBE)-F055 | west edfa in Vannes_KBE to Lorient_KMA | 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 | west edfa in Rennes_STA to Stbrieuc | 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 | east edfa in Rennes_STA to Ploermel | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | west edfa in Vannes_KBE to Ploermel | roadm Vannes_KBE | east edfa in Vannes_KBE to Lorient_KMA | fiber (Vannes_KBE → Lorient_KMA)-F055 | west edfa in Lorient_KMA to Vannes_KBE | roadm Lorient_KMA | east edfa in Lorient_KMA to Loudeac | 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 | east edfa in Rennes_STA to Stbrieuc | fiber (Rennes_STA → Stbrieuc)-F057 | west edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Lannion_CAS)-F056 | west edfa in Lannion_CAS to Stbrieuc | 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.
|
@@ -14,8 +14,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -39,8 +39,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -104,8 +104,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -129,8 +129,8 @@
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
@@ -154,8 +154,8 @@
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
@@ -179,8 +179,8 @@
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"N": "null",
|
||||
"M": "null"
|
||||
"N": null,
|
||||
"M": null
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -39,8 +39,8 @@
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -64,8 +64,8 @@
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -89,8 +89,8 @@
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -135,8 +135,8 @@
|
||||
"trx_mode": "mode 2 - fake",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
@@ -160,8 +160,8 @@
|
||||
"trx_mode": "mode 2",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
@@ -185,8 +185,8 @@
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -223,8 +223,8 @@
|
||||
"trx_mode": "mode 3",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 62500000000.0,
|
||||
@@ -261,8 +261,8 @@
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -286,8 +286,8 @@
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -332,8 +332,8 @@
|
||||
"trx_mode": "16QAM",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -357,8 +357,8 @@
|
||||
"trx_mode": "mode 1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -382,8 +382,8 @@
|
||||
"trx_mode": "PS_SP64_1",
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -407,8 +407,8 @@
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -453,8 +453,8 @@
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -478,8 +478,8 @@
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
@@ -503,8 +503,8 @@
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 50000000000.0,
|
||||
@@ -528,8 +528,8 @@
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 75000000000.0,
|
||||
@@ -553,8 +553,8 @@
|
||||
"trx_mode": null,
|
||||
"effective-freq-slot": [
|
||||
{
|
||||
"n": "null",
|
||||
"m": "null"
|
||||
"N":"null",
|
||||
"M":"null"
|
||||
}
|
||||
],
|
||||
"spacing": 30000000000.0,
|
||||
|
||||
BIN
tests/data/testTopologyconvert.xls
Normal file
BIN
tests/data/testTopologyconvert.xls
Normal file
Binary file not shown.
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"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_tolerance": 0.1,
|
||||
"raman_computed_channels": [1, 18, 37, 56, 75]
|
||||
}
|
||||
}
|
||||
813
tests/data/twohops_roadm_power_test.json
Normal file
813
tests/data/twohops_roadm_power_test.json
Normal file
@@ -0,0 +1,813 @@
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"uid": "trx node A",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "node A",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx node B",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "node B",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "trx node C",
|
||||
"type": "Transceiver",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "node C",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm node A",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -15,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
},
|
||||
"per_degree_pch_out_db": {
|
||||
"Edfa0_roadm node A": -15
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "node A",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm node B",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20.0,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
},
|
||||
"per_degree_pch_out_db": {
|
||||
"east edfa in node B to ila2": -20,
|
||||
"Edfa0_roadm node B": -20
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "node B",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "roadm node C",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -15,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
},
|
||||
"per_degree_pch_out_db": {
|
||||
"Edfa0_roadm node C": -15
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "node C",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (node A → ila1)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (ila1 → ila2)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (ila2 → node B)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 100.0,
|
||||
"loss_coef": 0.22,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (node B → ila3)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (ila3 → ila4)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (ila4 → node C)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (ila1 → node A)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (ila2 → ila1)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (node B → ila2)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 100.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (ila3 → node B)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (ila4 → ila3)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "fiber (node C → ila4)-",
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 80.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in node B to ila2",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_medium_gain",
|
||||
"operational": {
|
||||
"gain_target": 35.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "node B",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "west edfa in node B to ila2",
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0,
|
||||
"longitude": 0,
|
||||
"city": "node B",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_roadm node A",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 14.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "node A",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_roadm node 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.0,
|
||||
"longitude": 0.0,
|
||||
"city": "node B",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_roadm node C",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 14.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": "node C",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (node A → ila1)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (ila1 → ila2)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (node B → ila3)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (ila3 → ila4)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (ila4 → node C)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (ila1 → node A)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (ila2 → ila1)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (node B → ila2)-",
|
||||
"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.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (ila3 → node B)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 17.0,
|
||||
"delta_p": 0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (ila4 → ila3)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_fiber (node C → ila4)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": 16.0,
|
||||
"delta_p": -1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 0.0,
|
||||
"longitude": 0.0,
|
||||
"city": null,
|
||||
"region": null
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
{
|
||||
"from_node": "trx node A",
|
||||
"to_node": "roadm node A"
|
||||
},
|
||||
{
|
||||
"from_node": "trx node B",
|
||||
"to_node": "roadm node B"
|
||||
},
|
||||
{
|
||||
"from_node": "trx node C",
|
||||
"to_node": "roadm node C"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm node A",
|
||||
"to_node": "trx node A"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm node A",
|
||||
"to_node": "Edfa0_roadm node A"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm node B",
|
||||
"to_node": "east edfa in node B to ila2"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm node B",
|
||||
"to_node": "trx node B"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm node B",
|
||||
"to_node": "Edfa0_roadm node B"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm node C",
|
||||
"to_node": "trx node C"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm node C",
|
||||
"to_node": "Edfa0_roadm node C"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (node A → ila1)-",
|
||||
"to_node": "Edfa0_fiber (node A → ila1)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (ila1 → ila2)-",
|
||||
"to_node": "Edfa0_fiber (ila1 → ila2)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (ila2 → node B)-",
|
||||
"to_node": "west edfa in node B to ila2"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (node B → ila3)-",
|
||||
"to_node": "Edfa0_fiber (node B → ila3)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (ila3 → ila4)-",
|
||||
"to_node": "Edfa0_fiber (ila3 → ila4)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (ila4 → node C)-",
|
||||
"to_node": "Edfa0_fiber (ila4 → node C)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (ila1 → node A)-",
|
||||
"to_node": "Edfa0_fiber (ila1 → node A)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (ila2 → ila1)-",
|
||||
"to_node": "Edfa0_fiber (ila2 → ila1)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (node B → ila2)-",
|
||||
"to_node": "Edfa0_fiber (node B → ila2)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (ila3 → node B)-",
|
||||
"to_node": "Edfa0_fiber (ila3 → node B)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (ila4 → ila3)-",
|
||||
"to_node": "Edfa0_fiber (ila4 → ila3)-"
|
||||
},
|
||||
{
|
||||
"from_node": "fiber (node C → ila4)-",
|
||||
"to_node": "Edfa0_fiber (node C → ila4)-"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in node B to ila2",
|
||||
"to_node": "fiber (node B → ila2)-"
|
||||
},
|
||||
{
|
||||
"from_node": "west edfa in node B to ila2",
|
||||
"to_node": "roadm node B"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_roadm node A",
|
||||
"to_node": "fiber (node A → ila1)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_roadm node B",
|
||||
"to_node": "fiber (node B → ila3)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_roadm node C",
|
||||
"to_node": "fiber (node C → ila4)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (node A → ila1)-",
|
||||
"to_node": "fiber (ila1 → ila2)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (ila1 → ila2)-",
|
||||
"to_node": "fiber (ila2 → node B)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (node B → ila3)-",
|
||||
"to_node": "fiber (ila3 → ila4)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (ila3 → ila4)-",
|
||||
"to_node": "fiber (ila4 → node C)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (ila4 → node C)-",
|
||||
"to_node": "roadm node C"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (ila1 → node A)-",
|
||||
"to_node": "roadm node A"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (ila2 → ila1)-",
|
||||
"to_node": "fiber (ila1 → node A)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (node B → ila2)-",
|
||||
"to_node": "fiber (ila2 → ila1)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (ila3 → node B)-",
|
||||
"to_node": "roadm node B"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (ila4 → ila3)-",
|
||||
"to_node": "fiber (ila3 → node B)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_fiber (node C → ila4)-",
|
||||
"to_node": "fiber (ila4 → ila3)-"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -144,11 +144,11 @@ Computed path (roadms):['roadm Lannion_CAS', 'roadm Lorient_KMA']
|
||||
|
||||
[1;34;40mResult summary[0m
|
||||
req id demand snr@bandwidth A-Z (Z-A) snr@0.1nm A-Z (Z-A) Receiver minOSNR mode Gbit/s nb of tsp pairs N,M or blocking reason
|
||||
0 trx Lorient_KMA to trx Vannes_KBE : 24.62 28.7 14 mode 1 100.0 1 (-284,4)
|
||||
1 trx Brest_KLA to trx Vannes_KBE : 17.59 21.68 14 mode 1 200.0 2 (-272,8)
|
||||
0 trx Lorient_KMA to trx Vannes_KBE : 24.83 28.92 14 mode 1 100.0 1 (-284,4)
|
||||
1 trx Brest_KLA to trx Vannes_KBE : 17.75 21.83 14 mode 1 200.0 2 (-272,8)
|
||||
3 trx Lannion_CAS to trx Rennes_STA : 22.21 26.29 13 mode 1 60.0 1 (-284,4)
|
||||
4 trx Rennes_STA to trx Lannion_CAS : 15.94 23.16 17 mode 2 150.0 1 (-258,6)
|
||||
5 trx Rennes_STA to trx Lannion_CAS : 20.22 27.44 17 mode 2 20.0 1 (-274,6)
|
||||
7 | 6 trx Lannion_CAS to trx Lorient_KMA : 19.24 23.32 14 mode 1 700.0 7 (-224,28)
|
||||
7b trx Lannion_CAS to trx Lorient_KMA : 19.32 23.4 14 mode 1 400.0 4 (-172,24)
|
||||
4 trx Rennes_STA to trx Lannion_CAS : 16.07 23.29 17 mode 2 150.0 1 (-258,6)
|
||||
5 trx Rennes_STA to trx Lannion_CAS : 20.31 27.54 17 mode 2 20.0 1 (-274,6)
|
||||
7 | 6 trx Lannion_CAS to trx Lorient_KMA : 19.52 23.61 14 mode 1 700.0 7 (-224,28)
|
||||
7b trx Lannion_CAS to trx Lorient_KMA : 19.61 23.69 14 mode 1 400.0 4 (-172,24)
|
||||
[1;33;40mResult summary shows mean SNR and OSNR (average over all channels)[0m
|
||||
|
||||
@@ -8,10 +8,10 @@ Now propagating between Site_A and Site_B:
|
||||
|
||||
Propagating with input power = [1;36;40m0.00 dBm[0m:
|
||||
Transceiver Site_A
|
||||
OSNR ASE (0.1nm, dB): inf
|
||||
OSNR ASE (signal bw, dB): inf
|
||||
SNR total (signal bw, dB): inf
|
||||
SNR total (0.1nm, dB): inf
|
||||
OSNR ASE (0.1nm, dB): 40.00
|
||||
OSNR ASE (signal bw, dB): 35.92
|
||||
SNR total (signal bw, dB): 35.92
|
||||
SNR total (0.1nm, dB): 40.00
|
||||
CD (ps/nm): 0.00
|
||||
PMD (ps): 0.00
|
||||
Fiber Span1
|
||||
@@ -36,15 +36,15 @@ Edfa Edfa1
|
||||
effective pch (dBm): -2.0
|
||||
output VOA (dB): 0.00
|
||||
Transceiver Site_B
|
||||
OSNR ASE (0.1nm, dB): 32.03
|
||||
OSNR ASE (signal bw, dB): 27.95
|
||||
SNR total (signal bw, dB): 26.27
|
||||
SNR total (0.1nm, dB): 30.35
|
||||
OSNR ASE (0.1nm, dB): 33.30
|
||||
OSNR ASE (signal bw, dB): 29.21
|
||||
SNR total (signal bw, dB): 27.09
|
||||
SNR total (0.1nm, dB): 31.17
|
||||
CD (ps/nm): 1336.00
|
||||
PMD (ps): 0.36
|
||||
|
||||
Transmission result for input power = 0.00 dBm:
|
||||
Final SNR total (0.1 nm): [1;36;40m30.35 dB[0m
|
||||
Final SNR total (0.1 nm): [1;36;40m31.17 dB[0m
|
||||
|
||||
(No source node specified: picked Site_A)
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ Now propagating between Site_A and Site_B:
|
||||
|
||||
Propagating with input power = [1;36;40m0.00 dBm[0m:
|
||||
Transceiver Site_A
|
||||
OSNR ASE (0.1nm, dB): inf
|
||||
OSNR ASE (signal bw, dB): inf
|
||||
SNR total (signal bw, dB): inf
|
||||
SNR total (0.1nm, dB): inf
|
||||
OSNR ASE (0.1nm, dB): 40.00
|
||||
OSNR ASE (signal bw, dB): 35.92
|
||||
SNR total (signal bw, dB): 35.92
|
||||
SNR total (0.1nm, dB): 40.00
|
||||
CD (ps/nm): 0.00
|
||||
PMD (ps): 0.00
|
||||
RamanFiber Span1
|
||||
@@ -36,94 +36,94 @@ Edfa Edfa1
|
||||
effective pch (dBm): -2.0
|
||||
output VOA (dB): 0.00
|
||||
Transceiver Site_B
|
||||
OSNR ASE (0.1nm, dB): 32.65
|
||||
OSNR ASE (signal bw, dB): 28.57
|
||||
SNR total (signal bw, dB): 26.48
|
||||
SNR total (0.1nm, dB): 30.56
|
||||
OSNR ASE (0.1nm, dB): 34.18
|
||||
OSNR ASE (signal bw, dB): 30.10
|
||||
SNR total (signal bw, dB): 27.35
|
||||
SNR total (0.1nm, dB): 31.43
|
||||
CD (ps/nm): 1336.00
|
||||
PMD (ps): 0.36
|
||||
|
||||
Transmission result for input power = 0.00 dBm:
|
||||
Final SNR total (0.1 nm): [1;36;40m30.56 dB[0m
|
||||
Final SNR total (0.1 nm): [1;36;40m31.43 dB[0m
|
||||
|
||||
The total SNR per channel at the end of the line is:
|
||||
Ch. # Channel frequency (THz) Channel power (dBm) OSNR ASE (signal bw, dB) SNR NLI (signal bw, dB) SNR total (signal bw, dB)
|
||||
1 191.35 0.21 29.57 31.47 27.41
|
||||
2 191.40 0.17 29.56 31.38 27.36
|
||||
3 191.45 0.14 29.54 31.30 27.32
|
||||
4 191.50 0.10 29.53 31.22 27.28
|
||||
5 191.55 0.04 29.51 31.14 27.24
|
||||
6 191.60 -0.02 29.49 31.06 27.19
|
||||
7 191.65 -0.08 29.47 30.98 27.15
|
||||
8 191.70 -0.14 29.45 30.90 27.11
|
||||
9 191.75 -0.20 29.43 30.83 27.06
|
||||
10 191.80 -0.26 29.41 30.75 27.02
|
||||
11 191.85 -0.33 29.39 30.68 26.97
|
||||
12 191.90 -0.39 29.36 30.61 26.93
|
||||
13 191.95 -0.46 29.34 30.54 26.89
|
||||
14 192.00 -0.52 29.32 30.47 26.84
|
||||
15 192.05 -0.59 29.30 30.40 26.80
|
||||
16 192.10 -0.66 29.27 30.33 26.76
|
||||
17 192.15 -0.72 29.25 30.26 26.72
|
||||
18 192.20 -0.79 29.22 30.20 26.67
|
||||
19 192.25 -0.86 29.20 30.21 26.66
|
||||
20 192.30 -0.94 29.17 30.21 26.65
|
||||
21 192.35 -1.01 29.14 30.22 26.64
|
||||
22 192.40 -1.09 29.11 30.23 26.62
|
||||
23 192.45 -1.16 29.08 30.23 26.61
|
||||
24 192.50 -1.24 29.05 30.24 26.60
|
||||
25 192.55 -1.31 29.03 30.25 26.58
|
||||
26 192.60 -1.38 29.00 30.25 26.57
|
||||
27 192.65 -1.45 28.97 30.26 26.56
|
||||
28 192.70 -1.52 28.94 30.27 26.55
|
||||
29 192.75 -1.59 28.91 30.28 26.53
|
||||
30 192.80 -1.66 28.89 30.28 26.52
|
||||
31 192.85 -1.73 28.86 30.29 26.50
|
||||
32 192.90 -1.80 28.83 30.30 26.49
|
||||
33 192.95 -1.87 28.80 30.30 26.48
|
||||
34 193.00 -1.94 28.77 30.31 26.46
|
||||
35 193.05 -2.01 28.74 30.32 26.45
|
||||
36 193.10 -2.08 28.71 30.33 26.43
|
||||
37 193.15 -2.15 28.68 30.33 26.42
|
||||
38 193.20 -2.22 28.65 30.35 26.41
|
||||
39 193.25 -2.29 28.62 30.37 26.40
|
||||
40 193.30 -2.36 28.59 30.39 26.38
|
||||
41 193.35 -2.43 28.56 30.40 26.37
|
||||
42 193.40 -2.49 28.53 30.42 26.36
|
||||
43 193.45 -2.56 28.49 30.44 26.35
|
||||
44 193.50 -2.63 28.46 30.46 26.34
|
||||
45 193.55 -2.70 28.43 30.47 26.32
|
||||
46 193.60 -2.78 28.40 30.49 26.31
|
||||
47 193.65 -2.85 28.36 30.51 26.29
|
||||
48 193.70 -2.92 28.33 30.53 26.28
|
||||
49 193.75 -2.99 28.29 30.54 26.26
|
||||
50 193.80 -3.06 28.26 30.56 26.25
|
||||
51 193.85 -3.14 28.22 30.58 26.23
|
||||
52 193.90 -3.21 28.19 30.60 26.22
|
||||
53 193.95 -3.28 28.15 30.62 26.20
|
||||
54 194.00 -3.35 28.12 30.64 26.19
|
||||
55 194.05 -3.42 28.08 30.65 26.17
|
||||
56 194.10 -3.50 28.04 30.67 26.15
|
||||
57 194.15 -3.57 28.01 30.73 26.15
|
||||
58 194.20 -3.64 27.97 30.79 26.14
|
||||
59 194.25 -3.72 27.93 30.85 26.14
|
||||
60 194.30 -3.79 27.89 30.91 26.13
|
||||
61 194.35 -3.86 27.86 30.97 26.13
|
||||
62 194.40 -3.93 27.82 31.03 26.12
|
||||
63 194.45 -4.01 27.78 31.09 26.12
|
||||
64 194.50 -4.08 27.74 31.15 26.11
|
||||
65 194.55 -4.14 27.71 31.22 26.11
|
||||
66 194.60 -4.21 27.67 31.28 26.10
|
||||
67 194.65 -4.28 27.63 31.35 26.09
|
||||
68 194.70 -4.34 27.60 31.41 26.09
|
||||
69 194.75 -4.41 27.56 31.48 26.08
|
||||
70 194.80 -4.47 27.52 31.55 26.08
|
||||
71 194.85 -4.54 27.49 31.62 26.07
|
||||
72 194.90 -4.60 27.45 31.69 26.06
|
||||
73 194.95 -4.67 27.41 31.77 26.06
|
||||
74 195.00 -4.73 27.38 31.84 26.05
|
||||
75 195.05 -4.80 27.34 31.91 26.04
|
||||
76 195.10 -4.86 27.30 31.91 26.01
|
||||
1 191.35 0.21 31.56 31.47 28.50
|
||||
2 191.40 0.17 31.54 31.38 28.45
|
||||
3 191.45 0.14 31.52 31.30 28.40
|
||||
4 191.50 0.10 31.50 31.22 28.34
|
||||
5 191.55 0.04 31.47 31.14 28.29
|
||||
6 191.60 -0.02 31.44 31.06 28.23
|
||||
7 191.65 -0.08 31.41 30.98 28.18
|
||||
8 191.70 -0.14 31.37 30.90 28.12
|
||||
9 191.75 -0.20 31.34 30.83 28.07
|
||||
10 191.80 -0.26 31.31 30.75 28.01
|
||||
11 191.85 -0.33 31.27 30.68 27.96
|
||||
12 191.90 -0.39 31.24 30.61 27.90
|
||||
13 191.95 -0.46 31.20 30.54 27.85
|
||||
14 192.00 -0.52 31.17 30.47 27.79
|
||||
15 192.05 -0.59 31.13 30.40 27.74
|
||||
16 192.10 -0.66 31.10 30.33 27.69
|
||||
17 192.15 -0.72 31.06 30.26 27.63
|
||||
18 192.20 -0.79 31.02 30.20 27.58
|
||||
19 192.25 -0.86 30.98 30.21 27.57
|
||||
20 192.30 -0.94 30.94 30.21 27.55
|
||||
21 192.35 -1.01 30.90 30.22 27.54
|
||||
22 192.40 -1.09 30.86 30.23 27.52
|
||||
23 192.45 -1.16 30.81 30.23 27.50
|
||||
24 192.50 -1.24 30.77 30.24 27.49
|
||||
25 192.55 -1.31 30.73 30.25 27.47
|
||||
26 192.60 -1.38 30.69 30.25 27.46
|
||||
27 192.65 -1.45 30.65 30.26 27.44
|
||||
28 192.70 -1.52 30.61 30.27 27.42
|
||||
29 192.75 -1.59 30.56 30.28 27.41
|
||||
30 192.80 -1.66 30.52 30.28 27.39
|
||||
31 192.85 -1.73 30.48 30.29 27.37
|
||||
32 192.90 -1.80 30.44 30.30 27.36
|
||||
33 192.95 -1.87 30.39 30.30 27.34
|
||||
34 193.00 -1.94 30.35 30.31 27.32
|
||||
35 193.05 -2.01 30.31 30.32 27.30
|
||||
36 193.10 -2.08 30.27 30.33 27.29
|
||||
37 193.15 -2.15 30.22 30.33 27.27
|
||||
38 193.20 -2.22 30.18 30.35 27.25
|
||||
39 193.25 -2.29 30.14 30.37 27.24
|
||||
40 193.30 -2.36 30.09 30.39 27.23
|
||||
41 193.35 -2.43 30.05 30.40 27.21
|
||||
42 193.40 -2.49 30.01 30.42 27.20
|
||||
43 193.45 -2.56 29.96 30.44 27.18
|
||||
44 193.50 -2.63 29.92 30.46 27.17
|
||||
45 193.55 -2.70 29.87 30.47 27.15
|
||||
46 193.60 -2.78 29.83 30.49 27.13
|
||||
47 193.65 -2.85 29.78 30.51 27.12
|
||||
48 193.70 -2.92 29.73 30.53 27.10
|
||||
49 193.75 -2.99 29.68 30.54 27.08
|
||||
50 193.80 -3.06 29.64 30.56 27.06
|
||||
51 193.85 -3.14 29.59 30.58 27.05
|
||||
52 193.90 -3.21 29.54 30.60 27.03
|
||||
53 193.95 -3.28 29.49 30.62 27.01
|
||||
54 194.00 -3.35 29.44 30.64 26.99
|
||||
55 194.05 -3.42 29.39 30.65 26.97
|
||||
56 194.10 -3.50 29.34 30.67 26.95
|
||||
57 194.15 -3.57 29.29 30.73 26.94
|
||||
58 194.20 -3.64 29.24 30.79 26.94
|
||||
59 194.25 -3.72 29.19 30.85 26.93
|
||||
60 194.30 -3.79 29.14 30.91 26.93
|
||||
61 194.35 -3.86 29.09 30.97 26.92
|
||||
62 194.40 -3.93 29.04 31.03 26.91
|
||||
63 194.45 -4.01 28.99 31.09 26.90
|
||||
64 194.50 -4.08 28.94 31.15 26.90
|
||||
65 194.55 -4.14 28.89 31.22 26.89
|
||||
66 194.60 -4.21 28.85 31.28 26.88
|
||||
67 194.65 -4.28 28.80 31.35 26.88
|
||||
68 194.70 -4.34 28.75 31.41 26.87
|
||||
69 194.75 -4.41 28.70 31.48 26.86
|
||||
70 194.80 -4.47 28.66 31.55 26.86
|
||||
71 194.85 -4.54 28.61 31.62 26.85
|
||||
72 194.90 -4.60 28.56 31.69 26.84
|
||||
73 194.95 -4.67 28.51 31.77 26.83
|
||||
74 195.00 -4.73 28.47 31.84 26.82
|
||||
75 195.05 -4.80 28.42 31.91 26.81
|
||||
76 195.10 -4.86 28.37 31.91 26.78
|
||||
|
||||
(No source node specified: picked Site_A)
|
||||
|
||||
|
||||
@@ -129,6 +129,8 @@ def create_rq(equipment, srce, dest, bdir, nd_list, ls_list):
|
||||
f_max_from_si = params['f_max']
|
||||
params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
|
||||
params['path_bandwidth'] = 100000000000.0
|
||||
params['effective_freq_slot'] = None
|
||||
params['blocking_reason'] = None
|
||||
requests_list.append(PathRequest(**params))
|
||||
return requests_list
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ DATA_DIR = TEST_DIR / 'data'
|
||||
|
||||
|
||||
def test_sim_parameters():
|
||||
j = load_json(DATA_DIR / 'test_sim_params.json')
|
||||
j = load_json(DATA_DIR / 'sim_params.json')
|
||||
sim_params = SimParams(**j)
|
||||
Simulation.set_params(sim_params)
|
||||
s1 = Simulation.get_simulation()
|
||||
|
||||
@@ -19,6 +19,7 @@ from pathlib import Path
|
||||
from os import unlink
|
||||
import shutil
|
||||
from pandas import read_csv
|
||||
from xlrd import open_workbook
|
||||
import pytest
|
||||
from tests.compare import compare_networks, compare_services
|
||||
from copy import deepcopy
|
||||
@@ -29,7 +30,8 @@ from gnpy.topology.request import (jsontocsv, requests_aggregation, compute_path
|
||||
compute_path_with_disjunction, ResultElement, PathRequest)
|
||||
from gnpy.topology.spectrum_assignment import build_oms_list, pth_assign_spectrum
|
||||
from gnpy.tools.convert import convert_file
|
||||
from gnpy.tools.json_io import load_json, load_network, save_network, load_equipment, requests_from_json, disjunctions_from_json
|
||||
from gnpy.tools.json_io import (load_json, load_network, save_network, load_equipment, requests_from_json,
|
||||
disjunctions_from_json, network_to_json, network_from_json)
|
||||
from gnpy.tools.service_sheet import read_service_sheet, correct_xls_route_list
|
||||
|
||||
TEST_DIR = Path(__file__).parent
|
||||
@@ -41,6 +43,8 @@ equipment = load_equipment(eqpt_filename)
|
||||
@pytest.mark.parametrize('xls_input,expected_json_output', {
|
||||
DATA_DIR / 'CORONET_Global_Topology.xlsx': DATA_DIR / 'CORONET_Global_Topology_expected.json',
|
||||
DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_expected.json',
|
||||
DATA_DIR / 'perdegreemeshTopologyExampleV2.xls': DATA_DIR / 'perdegreemeshTopologyExampleV2_expected.json'
|
||||
|
||||
}.items())
|
||||
def test_excel_json_generation(tmpdir, xls_input, expected_json_output):
|
||||
""" tests generation of topology json
|
||||
@@ -104,20 +108,22 @@ def test_auto_design_generation_fromxlsgainmode(tmpdir, xls_input, expected_json
|
||||
# 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, power_mode',
|
||||
{DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json':
|
||||
DATA_DIR / 'CORONET_Global_Topology_auto_design_expected.json',
|
||||
False,
|
||||
DATA_DIR / 'testTopology_auto_design_expected.json':
|
||||
DATA_DIR / 'testTopology_auto_design_expected.json',
|
||||
False,
|
||||
DATA_DIR / 'perdegreemeshTopologyExampleV2_auto_design_expected.json':
|
||||
True
|
||||
}.items())
|
||||
def test_auto_design_generation_fromjson(tmpdir, json_input, expected_json_output):
|
||||
def test_auto_design_generation_fromjson(tmpdir, json_input, power_mode):
|
||||
"""test that autodesign creates same file as an input file already autodesigned
|
||||
"""
|
||||
equipment = load_equipment(eqpt_filename)
|
||||
network = load_network(json_input, equipment)
|
||||
# in order to test the Eqpt sheet and load gain target,
|
||||
# change the power-mode to False (to be in gain mode)
|
||||
equipment['Span']['default'].power_mode = False
|
||||
equipment['Span']['default'].power_mode = power_mode
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
@@ -128,7 +134,7 @@ def test_auto_design_generation_fromjson(tmpdir, json_input, expected_json_outpu
|
||||
save_network(network, actual_json_output)
|
||||
actual = load_json(actual_json_output)
|
||||
unlink(actual_json_output)
|
||||
expected = load_json(expected_json_output)
|
||||
expected = load_json(json_input)
|
||||
|
||||
results = compare_networks(expected, actual)
|
||||
assert not results.elements.missing
|
||||
@@ -361,9 +367,9 @@ def test_json_response_generation(xls_input, expected_response_file):
|
||||
('trx Lannion_CAS', 'trx Lorient_KMA', 'Ploermel | Vannes_KBE', 'LOOSE',
|
||||
['east edfa in Ploermel to Vannes_KBE', 'roadm Vannes_KBE']),
|
||||
('trx Rennes_STA', 'trx Brest_KLA', 'Vannes_KBE | Quimper | Brest_KLA', 'LOOSE',
|
||||
['roadm Vannes_KBE', 'Edfa0_fiber (Lorient_KMA → Quimper)-', 'roadm Brest_KLA']),
|
||||
['roadm Vannes_KBE', 'west edfa in Quimper to Lorient_KMA', 'roadm Brest_KLA']),
|
||||
('trx Brest_KLA', 'trx Rennes_STA', 'Brest_KLA | Quimper | Lorient_KMA', 'LOOSE',
|
||||
['roadm Brest_KLA', 'Edfa0_fiber (Brest_KLA → Quimper)-', 'roadm Lorient_KMA']),
|
||||
['roadm Brest_KLA', 'east edfa in Quimper to Lorient_KMA', 'roadm Lorient_KMA']),
|
||||
('Brest_KLA', 'trx Rennes_STA', '', 'LOOSE', 'Fail'),
|
||||
('trx Brest_KLA', 'Rennes_STA', '', 'LOOSE', 'Fail'),
|
||||
('Brest_KLA', 'Rennes_STA', '', 'LOOSE', 'Fail'),
|
||||
@@ -418,6 +424,8 @@ def test_excel_ila_constraints(source, destination, route_list, hoptype, expecte
|
||||
'nb_channel': 0,
|
||||
'power': 0,
|
||||
'path_bandwidth': 0,
|
||||
'effective_freq_slot': None,
|
||||
'blocking_reason': None
|
||||
}
|
||||
request = PathRequest(**params)
|
||||
|
||||
@@ -427,3 +435,136 @@ def test_excel_ila_constraints(source, destination, route_list, hoptype, expecte
|
||||
else:
|
||||
with pytest.raises(ServiceError):
|
||||
[request] = correct_xls_route_list(service_xls_input, network, [request])
|
||||
|
||||
|
||||
def setup_per_degree(case):
|
||||
""" common setup for degree: returns the dict network for different cases
|
||||
"""
|
||||
json_network = load_json(DATA_DIR / 'testTopology_expected.json')
|
||||
json_network_auto = load_json(DATA_DIR / 'testTopology_auto_design_expected.json')
|
||||
if case == 'no':
|
||||
return json_network
|
||||
elif case == 'all':
|
||||
return json_network_auto
|
||||
elif case == 'Lannion_CAS and all':
|
||||
elem = next(e for e in json_network['elements'] if e['uid'] == 'roadm Lannion_CAS')
|
||||
elem['params'] = {'per_degree_pch_out_db': {
|
||||
"east edfa in Lannion_CAS to Corlay": -17,
|
||||
"east edfa in Lannion_CAS to Stbrieuc": -18,
|
||||
"east edfa in Lannion_CAS to Morlaix": -21}}
|
||||
return json_network
|
||||
elif case == 'Lannion_CAS and one':
|
||||
elem = next(e for e in json_network['elements'] if e['uid'] == 'roadm Lannion_CAS')
|
||||
elem['params'] = {'per_degree_pch_out_db': {
|
||||
"east edfa in Lannion_CAS to Corlay": -17,
|
||||
"east edfa in Lannion_CAS to Stbrieuc": -18}}
|
||||
return json_network
|
||||
|
||||
|
||||
@pytest.mark.parametrize('case', ['no', 'all', 'Lannion_CAS and all', 'Lannion_CAS and one'])
|
||||
def test_target_pch_out_db_global(case):
|
||||
""" check that per degree attributes are correctly created with global values if none are given
|
||||
"""
|
||||
json_network = setup_per_degree(case)
|
||||
per_degree = {}
|
||||
for elem in json_network['elements']:
|
||||
if 'type' in elem.keys() and elem['type'] == 'Roadm' and 'params' in elem.keys() \
|
||||
and 'per_degree_pch_out_db' in elem['params']:
|
||||
# records roadms that have a per degree target
|
||||
per_degree[elem['uid']] = {k: v for k, v in elem['params']['per_degree_pch_out_db'].items()}
|
||||
network = network_from_json(json_network, equipment)
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,
|
||||
equipment['SI']['default'].f_max,
|
||||
equipment['SI']['default'].spacing))
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
|
||||
data = network_to_json(network)
|
||||
for elem in data['elements']:
|
||||
if 'type' in elem.keys() and elem['type'] == 'Roadm':
|
||||
# check that power target attributes exist and are filled with correct values
|
||||
# first check that global 'target_pch_out_db' is correctly filled
|
||||
assert elem['params']['target_pch_out_db'] == equipment['Roadm']['default'].target_pch_out_db
|
||||
for degree, power in elem['params']['per_degree_pch_out_db'].items():
|
||||
if elem['uid'] not in per_degree.keys():
|
||||
# second: check that per degree 'target_pch_out_db' is correctly filled with global value
|
||||
# when there was no per degree specification on network input
|
||||
assert power == equipment['Roadm']['default'].target_pch_out_db
|
||||
else:
|
||||
if degree not in per_degree[elem['uid']].keys():
|
||||
# third: check that per degree 'target_pch_out_db' is correctly filled with global value
|
||||
# on degrees that had no specification when other degrees are filled
|
||||
assert power == equipment['Roadm']['default'].target_pch_out_db
|
||||
else:
|
||||
# fourth: check that per degree 'target_pch_out_db' is correctly filled with specified values
|
||||
assert power == per_degree[elem['uid']][degree]
|
||||
|
||||
|
||||
def all_rows(sh, start=0):
|
||||
""" reads excel sheet row per row
|
||||
"""
|
||||
return (sh.row(x) for x in range(start, sh.nrows))
|
||||
|
||||
|
||||
class Amp:
|
||||
""" Node element contains uid, list of connected nodes and eqpt type
|
||||
"""
|
||||
|
||||
def __init__(self, uid, to_node, eqpt=None, west=None):
|
||||
self.uid = uid
|
||||
self.to_node = to_node
|
||||
self.eqpt = eqpt
|
||||
self.west = west
|
||||
|
||||
|
||||
def test_eqpt_creation(tmpdir):
|
||||
""" tests that convert correctly creates equipment according to equipment sheet
|
||||
including all cominations in testTopologyconvert.xls: if a line exists the amplifier
|
||||
should be created even if no values are provided.
|
||||
"""
|
||||
xls_input = DATA_DIR / 'testTopologyconvert.xls'
|
||||
|
||||
xls_copy = Path(tmpdir) / xls_input.name
|
||||
shutil.copyfile(xls_input, xls_copy)
|
||||
convert_file(xls_copy)
|
||||
|
||||
actual_json_output = xls_copy.with_suffix('.json')
|
||||
actual = load_json(actual_json_output)
|
||||
unlink(actual_json_output)
|
||||
|
||||
connections = {elem['from_node']: elem['to_node'] for elem in actual['connections']}
|
||||
jsonconverted = {}
|
||||
for elem in actual['elements']:
|
||||
if 'type' in elem.keys() and elem['type'] == 'Edfa':
|
||||
print(elem['uid'])
|
||||
if 'type_variety' in elem.keys():
|
||||
jsonconverted[elem['uid']] = Amp(elem['uid'], connections[elem['uid']], elem['type_variety'])
|
||||
else:
|
||||
jsonconverted[elem['uid']] = Amp(elem['uid'], connections[elem['uid']])
|
||||
|
||||
with open_workbook(xls_input) as wobo:
|
||||
# reading Eqpt sheet assuming header is node A, Node Z, amp variety
|
||||
# fused should not be recorded as an amp
|
||||
eqpt_sheet = wobo.sheet_by_name('Eqpt')
|
||||
raw_eqpts = {}
|
||||
for row in all_rows(eqpt_sheet, start=5):
|
||||
if row[0].value not in raw_eqpts.keys():
|
||||
raw_eqpts[row[0].value] = Amp(row[0].value, [row[1].value], [row[2].value], [row[7].value])
|
||||
else:
|
||||
raw_eqpts[row[0].value].to_node.append(row[1].value)
|
||||
raw_eqpts[row[0].value].eqpt.append(row[2].value)
|
||||
raw_eqpts[row[0].value].west.append(row[7].value)
|
||||
# create the possible names similarly to what convert should do
|
||||
possiblename = [f'east edfa in {xlsname} to {node}' for xlsname, value in raw_eqpts.items()
|
||||
for i, node in enumerate(value.to_node) if value.eqpt[i] != 'fused'] +\
|
||||
[f'west edfa in {xlsname} to {node}' for xlsname, value in raw_eqpts.items()
|
||||
for i, node in enumerate(value.to_node) if value.west[i] != 'fused']
|
||||
# check that all lines in eqpt sheet correctly converts to an amp element
|
||||
for name in possiblename:
|
||||
assert name in jsonconverted.keys()
|
||||
# check that all amp in the converted files corresponds to an eqpt line
|
||||
for ampuid in jsonconverted.keys():
|
||||
assert ampuid in possiblename
|
||||
|
||||
@@ -13,11 +13,7 @@ from pathlib import Path
|
||||
from networkx import dijkstra_path
|
||||
from numpy import mean, sqrt, ones
|
||||
|
||||
#network_file_name = 'tests/test_network.json'
|
||||
network_file_name = Path(__file__).parent.parent / 'tests/LinkforTest.json'
|
||||
# TODO: note that this json entries has a weird topology since EDfa1 has a possible branch on a receiver B
|
||||
# this might not pass future tests/ code updates
|
||||
#network_file_name = Path(__file__).parent.parent / 'examples/edfa_example_network.json'
|
||||
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,10 @@ from gnpy.core.utils import lin2db, automatic_nch
|
||||
from gnpy.core.elements import Fused, Roadm, Edfa
|
||||
from gnpy.core.network import build_network
|
||||
from gnpy.tools.json_io import network_from_json, load_equipment, load_json, Amp
|
||||
|
||||
from gnpy.core.equipment import trx_mode_params
|
||||
from gnpy.topology.request import PathRequest, compute_constrained_path
|
||||
from gnpy.core.info import create_input_spectral_information
|
||||
from gnpy.core.utils import db2lin
|
||||
|
||||
TEST_DIR = Path(__file__).parent
|
||||
EQPT_LIBRARY_NAME = TEST_DIR / 'data/eqpt_config.json'
|
||||
@@ -202,3 +205,73 @@ def test_restrictions(restrictions, equipment):
|
||||
not roadms[next(network.successors(amp)).uid].restrictions['preamp_variety_list']:
|
||||
if amp.params.type_variety not in restrictions['preamp_variety_list']:
|
||||
raise AssertionError()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('prev_node_type, effective_pch_out_db', [('edfa', -20.0), ('fused', -22.0)])
|
||||
def test_roadm_target_power(prev_node_type, effective_pch_out_db):
|
||||
''' Check that egress power of roadm is equal to target power if input power is greater
|
||||
than target power else, that it is equal to input power. Use a simple two hops A-B-C topology
|
||||
for the test where the prev_node in ROADM B is either an amplifier or a fused, so that the target
|
||||
power can not be met in this last case.
|
||||
'''
|
||||
equipment = load_equipment(EQPT_LIBRARY_NAME)
|
||||
json_network = load_json(TEST_DIR / 'data/twohops_roadm_power_test.json')
|
||||
prev_node = next(n for n in json_network['elements'] if n['uid'] == 'west edfa in node B to ila2')
|
||||
json_network['elements'].remove(prev_node)
|
||||
if prev_node_type == 'edfa':
|
||||
prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Edfa'}
|
||||
elif prev_node_type == 'fused':
|
||||
prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Fused'}
|
||||
prev_node['params'] = {'loss': 0}
|
||||
json_network['elements'].append(prev_node)
|
||||
network = network_from_json(json_network, equipment)
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,
|
||||
equipment['SI']['default'].f_max,
|
||||
equipment['SI']['default'].spacing))
|
||||
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
|
||||
params = {}
|
||||
params['request_id'] = 0
|
||||
params['trx_type'] = ''
|
||||
params['trx_mode'] = ''
|
||||
params['source'] = 'trx node A'
|
||||
params['destination'] = 'trx node C'
|
||||
params['bidir'] = False
|
||||
params['nodes_list'] = ['trx node C']
|
||||
params['loose_list'] = ['strict']
|
||||
params['format'] = ''
|
||||
params['path_bandwidth'] = 100e9
|
||||
params['effective_freq_slot'] = None
|
||||
params['blocking_reason'] = None
|
||||
trx_params = trx_mode_params(equipment)
|
||||
params.update(trx_params)
|
||||
req = PathRequest(**params)
|
||||
path = compute_constrained_path(network, req)
|
||||
si = create_input_spectral_information(
|
||||
req.f_min, req.f_max, req.roll_off, req.baud_rate,
|
||||
req.power, req.spacing)
|
||||
for i, el in enumerate(path):
|
||||
if isinstance(el, Roadm):
|
||||
carriers_power_in_roadm = min([c.power.signal + c.power.nli + c.power.ase for c in si.carriers])
|
||||
si = el(si, degree=path[i+1].uid)
|
||||
if el.uid == 'roadm node B':
|
||||
print('input', carriers_power_in_roadm)
|
||||
assert el.effective_pch_out_db == effective_pch_out_db
|
||||
for carrier in si.carriers:
|
||||
print(carrier.power.signal + carrier.power.nli + carrier.power.ase)
|
||||
power = carrier.power.signal + carrier.power.nli + carrier.power.ase
|
||||
if prev_node_type == 'edfa':
|
||||
# edfa prev_node sets input power to roadm to a high enough value:
|
||||
# Check that egress power of roadm is equal to target power
|
||||
assert power == pytest.approx(db2lin(effective_pch_out_db - 30), rel=1e-3)
|
||||
elif prev_node_type == 'fused':
|
||||
# fused prev_node does reamplfy power after fiber propagation, so input power
|
||||
# to roadm is low.
|
||||
# Check that egress power of roadm is equalized to the min carrier input power.
|
||||
assert power == pytest.approx(carriers_power_in_roadm, rel=1e-3)
|
||||
else:
|
||||
si = el(si)
|
||||
|
||||
|
||||
@@ -2,24 +2,25 @@
|
||||
# -*- 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.
|
||||
Checks that RamanFiber propagates properly the spectral information. In this way, also the RamanSolver and the NliSolver
|
||||
are tested.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
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.parameters import SimParams
|
||||
from gnpy.core.science_utils import Simulation
|
||||
from gnpy.tools.json_io import load_json
|
||||
from pathlib import Path
|
||||
|
||||
TEST_DIR = Path(__file__).parent
|
||||
|
||||
|
||||
def test_raman_fiber():
|
||||
""" Test the accuracy of propagating the RamanFiber.
|
||||
"""
|
||||
""" Test the accuracy of propagating the RamanFiber."""
|
||||
# spectral information generation
|
||||
power = 1e-3
|
||||
eqpt_params = load_json(TEST_DIR / 'data' / 'eqpt_config.json')
|
||||
@@ -41,7 +42,7 @@ def test_raman_fiber():
|
||||
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')
|
||||
expected_results = read_csv(TEST_DIR / 'data' / 'test_science_utils_expected_results.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