561 Commits

Author SHA1 Message Date
Jan Kundrát
049b077ee4 Use real IP addresses for the US-based Cassinis
Change-Id: I158bb84261a56d71074155880c4359033b2f1044
2020-03-07 16:49:39 -08:00
Jan Kundrát
ab2080a805 Update IP addresses and hostnames for the OFC2020 demo
Change-Id: Ie8d30d56f94d1ce14f8ac62ceec7f0e57a3486b2
2020-02-12 17:32:07 +01:00
Jan Kundrát
8ab54e76df Merge branch 'develop' into experimental/2020-ofc
Change-Id: I4f7d3cc91734a03251b4ad4d82b05aad68d0ef5f
2020-02-12 17:18:50 +01:00
Mohammed Naser
0465397b1d Add empty Zuul configuration
In order for Zuul to start self-testing, it must actually have
a project entry listed.  This is the initial commit.

Change-Id: I017cb036f3191e46446d82b96a6acd35f2adcd0e
2020-01-28 20:20:41 +01:00
Jan Kundrát
d3ec39d506 Bump version to 2.1
Change-Id: I9f27fc87c5ca43e473fe212d2ee3dad7b12d4061
2020-01-15 00:09:48 +01:00
Jan Kundrát
bfe68a5948 Merge branch 'develop'
Change-Id: If7860b243cb504613a7fffafad2d601510000af7
2020-01-15 00:09:18 +01:00
Esther Le Rouzic
2ea3363613 Merge pull request #331 from Orange-OpenSource/pr_test
update version setup.py
2020-01-14 13:02:32 +00:00
DELFOUR Emmanuelle TGI/DATA-IA
89cce6e6a3 update version setup.py 2020-01-14 10:22:21 +01:00
Jan Kundrát
0f10ac706c Merge pull request #267 from Orange-OpenSource/capacity_planning-part2
This brings in the concept of OMS (so far created implicitly based on the input topology), spectrum assignment, etc.
2020-01-13 09:30:45 +00:00
EstherLerouzic
66d26f0ffa add the missing else to handle non first_fit policy
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2020-01-10 18:46:24 +00:00
EstherLerouzic
3c96914482 replace todo with TODO
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2020-01-10 18:41:40 +00:00
EstherLerouzic
61b1e73362 remove unused testing piece of code
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2020-01-10 18:40:18 +00:00
Jan Kundrát
03435079cc refactoring: simplify code flow a bit
- no need to explicitly log exceptions that are about to be raised
- kill some extra commented-out prints

Change-Id: I73bae5a2456644c4d4ff45bd984d44c27bc22ec4
2020-01-10 18:17:44 +00:00
Jan Kundrát
6661907c1d fix typos
Change-Id: I0a4d2c14c5e873dd521736525bb9b10c9b70975b
2020-01-10 18:17:17 +00:00
Jan Kundrát
fe811f725c tests: use native pytest features for exception handling
Using `with pytest.raises` is better than open coding the equivalent
feature. Similarly, when a block is not expected to raise an exception,
let's just let it run outside of a `try` block and rely on the test
framework to report a possible failure when hitting an unhandled
exception.

Change-Id: Icb1bb83e649733b56fcdc9168cabf88c9cf8d478
2020-01-10 18:16:53 +00:00
Jan Kundrát
f015c6abed ROADM module replacements 2020-01-07 16:29:15 +01:00
Jan Kundrát
8598e6591f Merge pull request #304 from Orange-OpenSource/user_error_catching_improvment
Give more useful comment for user to correct topology
2019-12-12 16:11:54 +00:00
EstherLerouzic
5e2259062c Give more useful comment for user to correct topology
when a node name used in link does not correspond to listed
node names, Bad link msg was thrown without info on which link
causes the problem.
This change gives additional info on link and catch the user error
more cleanly.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-12-12 15:55:33 +00:00
Alessio Ferrari
d483802a86 Edn-to-end test for Raman amplification 2019-12-12 12:52:59 +00:00
Jan Kundrát
9a0eece69c Merge pull request #322 from jktjkt/master
docs: Link to the updated meeting event
2019-12-09 20:46:06 +00:00
Jan Kundrát
1657bfd05f build: workaround Sphinx doc build failure
Upstream bugreport at sphinx-doc/sphinx#6887 suggests that this is due
to pip picking up the 0.16b0.dev0 pre-release...

Bug: https://github.com/sphinx-doc/sphinx/issues/6887
2019-12-09 16:05:56 +01:00
Jan Kundrát
49bf558916 docs: Link to the updated meeting event 2019-12-09 14:31:59 +01:00
Jan Kundrát
99f44a597b Merge remote-tracking branch 'origin/develop' 2019-11-13 19:59:35 +01:00
Jan Kundrát
71293c1c18 demo: reduce the spectrum so that it's safely and conveniently deep in the C-band 2019-11-12 20:26:08 +01:00
Jan Kundrát
bd7c70f902 demo: Fix ONOS dev-id mapping for Ams-L2
A duplicate key in the dict means that bad things happen.
2019-11-12 13:20:16 +01:00
Jan Kundrát
20c92d4338 demo: add an endpoint which return success so that ONOS can verify connectivity 2019-11-12 11:53:41 +01:00
Jan Kundrát
f0158e7202 demo: fix transponder name and type 2019-11-12 11:41:51 +01:00
Jan Kundrát
62408ddc98 demo: hardcode the device IP addresses 2019-11-11 16:48:53 +01:00
Jan Kundrát
b4f87b36db REST API: output detailed info about the reversed path for bidi requests 2019-11-08 15:27:21 +01:00
Jan Kundrát
9f49a115a1 Add a path-route-object with EDFA-specific per-channel power and output VOA settings
...once again. for the demo.
2019-11-08 13:00:40 +01:00
Jan Kundrát
c7d2305589 REST: return element type for EDFA, TXP and ROADM elements
...as requested by Andrea during today's call.
2019-11-08 12:50:11 +01:00
Jan Kundrát
5826a649de sync topology with Esther's proposal 2019-11-05 13:52:16 +01:00
EstherLerouzic
fa826391f6 Add some tests to support partial per degree target power definition
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-11-05 13:18:37 +01:00
EstherLerouzic
3481ba8ee3 add the degree info of next node during path propagation
when node is a roadm, add the degree info of next node during
path propagation.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-11-05 13:18:18 +01:00
EstherLerouzic
b4ab0b55de use the per degree target_pch_out_db for the target power in network build
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-11-05 13:17:02 +01:00
EstherLerouzic
0370b45d8a Add per degree power information in ROADM
- add the per degree info using the EXACT next node uid as identifier
  of the degree
- add the degree identifier on the propagate and call functions

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-11-05 13:16:54 +01:00
EstherLerouzic
468e689094 Add per channel power target out
Works OK only for roadms that face the line.... but maybe a problem
for the express path ....
to be checked

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-11-05 13:16:38 +01:00
Jan Kundrát
aafd82b16d Docker: run the TIP Summit 2019 demo by default
Here's a TL;DR of how to use this. First, start the container so that it
exports its port 5000 for incoming HTTP requests:

- docker run -P -it --rm telecominfraproject/oopt-gnpy-experimental:$SOME_VERSION

You'll have to replace `$SOME_VERSION` by acn actual version. There is
no default `latest` tag on this particular Docker repository.

Then find out what port Docker currently uses:

```console-session
~ # docker ps
CONTAINER ID        IMAGE                                                          COMMAND                  CREATED             STATUS              PORTS                     NAMES
6004c3a9b741        telecominfraproject/oopt-gnpy-experimental:v1.8-114-g7e0abc4   "/oopt-gnpy/.docker-…"   48 seconds ago      Up 44 seconds       0.0.0.0:32768->5000/tcp   eloquent_hawking
~ # docker port 6004c3a9b741
5000/tcp -> 0.0.0.0:32768
```

Path computation can then be requested like this:

- curl -v -X POST -H "Content-Type: application/json" -d @examples/2019-demo-services.json http://127.0.0.1:32768/gnpy-experimental

This one will try to compute two disjoint optical paths and output their
respective optical performance.
2019-10-28 17:02:08 +01:00
Jan Kundrát
60ee331153 demo: simplify the REST API interface
The topology will be provisioned out-of-band, so let's simplify the REST
API so that it reflects that design. Also, let's make it obvious that
the API is subject to change and should not be relied upon at this time.
It's meant to be an experimental interface with data I/O format which
*will* change as we adapt a proper YANG model for both directions.
2019-10-28 15:59:18 +01:00
Jan Kundrát
3a8ce74355 topologies and service requests for the TIP Summit demo
The examples/2019-generate-tip-demo.py helper script can be used to
generate a ring topology where each "ROADM node" consists of three
separate ROADMs and two pairs of booster+preamp EDFAs. This will be used
at the TIP Summit to show integration between ONOS and GNPy.

The topology *and the equipment library) more or less corresponds to the
CzechLight OLS that is planned for the exhibition.
2019-10-28 15:55:18 +01:00
Jan Kundrát
fd44463238 REST: do not use HTTP auth
I do not think that proof-of-concept demos should implement HTTP auth
because GNPy has no concept of access lists.  If people want to use this
in a "real scenario", they will likely wrap Python's HTTP server behind
a real HTTP reverse proxy, and they can then implement proper ACL at
that layer.
2019-10-28 15:45:36 +01:00
Jan Kundrát
a21f3fe6ee Merge pull request #312 from jktjkt/fixes
Small refactoring for itufs/utifl
2019-10-16 05:38:37 +00:00
Jan Kundrát
0ccbb2960c Merge pull request #277 from Orange-OpenSource/capacity_planning-part1
Capacity planning part1
2019-10-16 05:36:56 +00:00
EstherLerouzic
84ba2da553 add 400 return with msg in case of service error
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-14 17:04:07 +01:00
EstherLerouzic
e693d96ca1 limit generators to support fused in preamps
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-14 15:52:49 +01:00
EstherLerouzic
81cb7f8133 corrections due to codacy report
- remove unused abort and marshall
- change variable names to conform to upper letter rule,
  [a-z_][a-z0-9_]{2,30}$
- add docstrings

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-14 15:37:58 +01:00
EstherLerouzic
3471969956 add flesk_restfull and flask_httpauth packages to requirements
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 12:21:28 +01:00
EstherLerouzic
7a0985c362 add flask import in requirements
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 12:21:28 +01:00
EstherLerouzic
b79a9e2e67 example of result in json format
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 12:21:28 +01:00
EstherLerouzic
1e037fe6f5 add example for simple topology
on the same topology file find the triangle topology

        site_c
        /  \
       /    \
site_a ------ Site_b

and the simple parallel link
site_a ------ Site_b
        \  /
         --

this topo includes only sinple span hops and roadm have boosters and amplifiers

the serviceDemov1.json gives the example of how the requests must be formulated

- 0 simple one
- 1 request with the forced Span (case of parallel link)
- 2 request with the forced roadm (case of triangle topo)
- 3 and 4 request with the disjunction

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 12:21:10 +01:00
EstherLerouzic
0897be57c1 use a default topology file when api input topo is empty
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:04:36 +01:00
EstherLerouzic
4172b06b19 Update service and result json templates
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:04:36 +01:00
ahmed
32a4875e46 add the option "rest" to activate the api-rest
- add the function launch_cli to launch the "cli" mode
- add the the class Gnpy_API to launch the "api" mode
- modify the main to enable the launch of Gnpy with two
modes "rest" and "cli"

Signed-off-by: ahmed <ahmed.triki@orange.com>
2019-10-10 09:04:36 +01:00
EstherLerouzic
c577a75725 second set of modification due to codacy report
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
8827e0cf6f Codacy report: minor changes (trailing spaces, ...)
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
b0012fe399 SA: process also the case when pth is empty
TODO: a path containing only transceivers and no roadms leads to no oms
this has not been properly taken into account. (single link w/o ROADM
has no SA complexity and is out of the scope)

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
31e634615b Complete test on response comparison
If request is bidir and 'z-a-path-metric' is missing, raises an error
If present, should not raise an error

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
8300a55e39 Update test file with correct SA (due to bidir correction)
reversed path must be taken into account for spectrum assignment

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
5b939bc57a Improve compare_response function
remove some prints, indicate which from actual or expected key
is printed.
preceise that test is also performed on values

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
2f1ab9cc50 Create a SpectrumError(Exception)
Create an exception in case of an error due to spectrum problem
eg if spectrum request is not correct
if values are not correct.
use it instead of exit

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
42ba3eb98d Add a test to go through the new added code in spectrum_assignment
Verify that there is no raised error if M=0 and the blocking attribute is there

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
9eb87fc8e1 raise error instead of exit in assignment
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
8fab9bb945 Raise exceptions instead of exit()
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
1ead232a78 Do not block computation if M=0
When a request is blocked and its M value is set to 0. This should not raise
an error in pth_assign_spectrum. This test verify that the function
does not raise an error.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
b15c8c60ab Add a DisjunctionError exception and use it
in case there is no possible disjoint path with the added constraints
(most of the time due to an inconsistant user request)

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
66bdeb0e4d Catch Service error exception in the main program
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
1a2e090104 Move all main program into a main function in path_requests_run.py
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
a8e280e29b Remove exit and use ServiceError exception instead
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:19 +01:00
EstherLerouzic
edb54b02ac Another time improving formatting (codacy report)
removing unused import
change short variable names to conform to [a-z_][a-z0-9_]{2,30}$
change main variable names to conform to [A-Z_][A-Z0-9_]{2,30}$
add or remove spaces
add docstrings
correct comments and indents of cut lines

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-10 09:02:02 +01:00
EstherLerouzic
83d3f32fe0 Precise type of exception (codacy report)
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
085a379592 improving formatting (codacy report) of path_requests_run, request and service_sheet
remove unused imports
add docstrings
conform to '[A-Z_][A-Z0-9_]{2,30}$' pattern in main
remove trailing spaces
add/remove extra spaces

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
37bd5d0404 Code coverage improvement test service creation w/o sync vector
add one service  only excel file + changes on compare.py to support
no synchronization vector case

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
f788b81d21 Code coverage improvement test json generation with Z to A direction
add aa bidir request to test service error handling
TODO: write more detailed test on the bidir case

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
2ff1ce6b34 Code coverage improvement test ServiceError handling
For this purpose we create a wrong request with M=0 and verify
that serviceError is correctly raised when calling Result_element class

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
41a1e40d14 Update test data files
- add the bidir information on json expected services,
- add the label object in json responses
- add the spectrum on csv responses
- add the no-path container when relevant

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
921e8d2d3c Call build_oms_list in test_disjunction
Call bbuild_oms_list in the test to correctly build all attributes
of network elements

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
c009d28f7d Update test_disjunction and test_automaticmodefeature
Add bidir argument on load_requests calls

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
898eada097 Update test_parser
function compute_path_with_disjunction needs an additional reversed path
and results must contain wavelength assignment

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
bdfc55e801 Add a ServiceError(Exception) for malformed user requests
For example requested bandwidth should be >0

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
57f264bedb Change name of br and pw variables to brate and pwr
To conform to [a-z_][a-z0-9_]{2,30}$

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
fbe4fa3cf0 add docstring to functions
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
b2ef345f35 Cut long lines over 100 char
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:17:35 +01:00
EstherLerouzic
471ea7dfba Correct indentation due to linter report
using pylinter3
recommends having cut lines with same indent as starting [ or (

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
1b52f638ff other time: Correct indentation due to linter report
using pylinter3
recommends having cut lines with same indent as starting [ or (

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
84ab38a75f Another time: removing extra spaces, trailing spaces and adding missing spaces
for codacy report:
     removing extra spces before , and :
     adding spaces after , :
     adding spaces around < > =

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
916e5377f8 change variables d, el to conform to '[a-z_][a-z0-9_]{2,30}$' pattern
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
534bfd881e correction of the assignment: SA must be bidir
reversed path must be computed even if bidir is not requested
because in WDM system service are all bidir.
bidir option is only to avoid lengthy propagation of reversed
path when it is not needed

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
7c4015324d Moving reversed path computation and propagation to compute_path_with_disjunction function
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
8499ee52f4 Removing extra spaces, trailing spaces and adding missing spaces
for codacy report:
 removing extra spces before , and :
 adding spaces after , :
 adding spaces around < > =

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
cc1123863c Cut long lines to 100char max due to linter report
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
ca382806f6 Reordering imports due to codacy report
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
3559fc61c2 Introduce reversed path on CSV output
- use a new jsontopath_metric function to collect metrics
  out of a json response.
- path metrics must be shown also for reversed path if this
  is requested. This function avoids repeading code here
- add reversed metrics on the last columns of the CSV

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
33581cdcc9 Add bidir as a parameter in path_requests_run.py
if --bidir option is used on the main program, the field is set
  to true for all demands in case demands are expressed in an excel
  sheet. --bidir option does not change bidir field if the service
  file is in json format.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
991eb02964 Add the bidir parameter in transmission_main_example.py
Update the program because the same Path_request class is called
that now includes this bidir field

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
286e321a2d Add "bidirectional" field in the json service file example
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
56f158113d Implementation of bidirectional field per request
- Instead applying bidir option independantly from service demands (json or xls)
  the "bidirectional" attribute is introduced per request in the json.
  This enables bidirectional option per requests.
  if --bidir option is used on the main program, the field is set
  to true for all demands in case demands are expressed in an excel
  sheet. --bidir option does not change bidir field if the service
  file is in json format.
  Default value of "bidirectional" attribute is False.
- As a result the reversed path is propagated only if the birectional
  field of the request is True. (remember that the reversed path must
  be computed whatever the option because it is needed to compute
  spectral occupation on both directions).

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
024f6ff963 Simplification de stdout
all BLOCKING_NOMODE requests have the same type of response
so the test is simplified to account for this

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
8118a0f4f4 Second minor cosmetic changes
- add some help input for the main program arguments
- move and correct comments
- add empty lines on stdout to have a nice printing

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
eb89d8fd86 Minor cosmetic changes
correct typos in comments and precise that results show the
mean value of SNR of all channels on stdout

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
a938c1738b add reversed path information on stdout
the information on reversed path snr is shown in parenthesis

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
4f88882513 Use reversed path as a constraint for spectrum assignment
in path assignment function, path elements and reversed path
elements are concatenated to compute the overall spectrum
availability on all elements

in main program, assignment is performed after computing reversed paths

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
4e8d8b7ddd adding reversed path in the main program
add a bidir argument to make bidir propagation as optional.
Reversed path computation is not optional because it is needed
for spectrum assignment.
for all requests, if a path could be computed  a reversed path is
computed and propagation is performed on it if bidir option is on.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
afb7d75749 Add the reversed path as an attribute of Result_element
the objects contains also the information of reversed path if
the path is not None

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
488d0e1fe8 Change find_reversed_path function using the reversed_oms
reversed_oms was introdused in order to identify which oms correspond
to the reversed direction of a given oms.
with this commit we make use of this functionality and avoid the cumbersome
way that recovered the reversed path by computing shortest path in the
reversed way for each roadm. This function could not support multiple links
in parallel between two roadms, and was adding computation time.

The function first lists the oms of the pth in the reversed order and then
appends all its elements to the path. the first and last elements are transponder
and are append separately.

Unidir topology are not supported: if there is no reversed path, this raises an error

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
708442e4cd Introduce a reverse_oms function to identify bidir oms
reversed_oms is introdused in order to identify which oms correspond
to the reversed direction of a given oms. Indeed for spectrum assignment
it is mandatory to mark spectrum resources occupation on both directions
(requests are supposed bidirectional in WDM).

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
a2d905dfb1 Add N and M info in the stdout
the couple (N,M) is displayed only when a request is not blocked

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
d564fe3e2a Add N and M info in the CSV
The couple (N,M) is added on the last column only for non blocked requests
an empty string is used instead

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
ea21cce1c0 Add N and M in the json response
The label object is added in the response. It contains center index N value
and number of slots M, required by the request according to the computation.

The label object directly follows the hop attribute as detailed in
draft-ietf-teas-yang-path-computation.

If the path is not blocked this changes the index of the last hop information
(-3 instead of -2) and the index of the transponder for the first hop
(2 instead of 1)

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
aa9b4aefbe Add the N and M information in request when spectrum is assigned
N is the center frequency index (on G694.1 grid) and M the number of slots M,
required by the request according to the computation.
for convenience, we use N and M = 0 when request are blocked. Note that N= 0 is
a valid index when M is not 0.

If the number of slot required by a request is not feasible, the request is marked
as blocked with 'NO_SPECTRUM' as blocking reason

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
8655030e59 Add blocking reason on stdout
previous way only check path existence to know if the request was blocked
or not.
Now the printing checks if the blocking_reason attribute exist, and if so
adapts the printing accordingly. The reason for blocking is added on the
output.

if no path could be computed, snr, osnr and other metrics depending on path
are replaced by empty strings
else, the metrics corresponding to the computed path are shown

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
8107ddeb79 Change the processing of blocking case in main program
In compute_path_with_disjunction, and in case user mode is not feasible
returns the blocking reason instead of an empty path.
If the user does not give the mode and the automatic selection does not
give any feasible mode instead of checking if a mode exists, the function
now checks the presence of a blocking reason.

if the blocking reason is among BLOCKING_NOPATH reasons, than an empty
path is returned
if the blocking reason is among BLOCKING_NOMODE, then a path could be computed
and the mode information correspond to the last explored mode.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
76c8e55f06 CSV generation is adapted to include blocking info
creation of a function to avoid code duplication: json_param creates
the relevant parameters to show on the csv based on json input

replace try/except by a test on keys:
previous way tried to get pth_el['no-path'] and is the path was
not blocked this raised a key error. Now the there is a simple check if
the key is present.

Besides, as the no-path has been change to 'no-path' container containing
a 'no-path' attribute with the blocking reason, the test is made on the
attribute so on pth_el['no-path']['no-path'].

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
a7b1ab47d8 adapt response from the compute_constrained_path to return info in case of blocking
if the path is empty NO_PATH reason or NO_PATH_WITH_CONSTRAINT reason
is returned in the blocking_reason attribute

if no mode is feasible, the last explored mode is returned with the path (and
implicitly the last computed SNR). The baud_rate is derived from this last
mode

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
879f587ab9 Adapt json response to include information in case of blocking
if path could be computed: gives details of the path and on the propagation.
  - the 'no-path' attribute is changed to a 'no-path' container that contains:
      o the 'response-id' attribute
      o a 'no-path' attribute with blocking reason
      o if a path could be computed, the 'path-properties' of the path that
        was computed with the metrics

Note that this proposal to add information for blocking in the json output (instead
of a bare NO PATH) corresponds to the way PCEp is working in general, but is not yet
integrated in draft-ietf-teas-yang-path-computation model. Returning the whole path
in case of blocking in addition to blocking reason is a novelty from GNPy and was a
request from the users
        TODO : use correct ietf model when ready

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
8af2d80219 Add reasons for blocking in the response
Before continuing on spectrum assignment we need to clearly identify blocking reasons:
implicit previous way (a path is missing) is not satisfactory because in case of
spectrum blocking a path was possible. So we introduce a 'blocking_reason' attribute
on requests, that will only exist if the path is blocked during the process.
The 'blocking_reason' attribute refflects the blocking reason.
Commit defines blocking types and group them depending on the existence of path or
feasibility:
    'NO_PATH': no path was computed,
    'NO_PATH_WITH_CONSTRAINT': no path was computed with this constraint
    'NO_FEASIBLE_BAUDRATE_WITH_SPACING': no path was computed due to the spacing constraint
    'NO_COMPUTED_SNR': the computed path could not give any SNR result
    'NO_FEASIBLE_MODE': the user let the program choose a mode and path was computed but
                        no mode was feasible for the set of constraints
    'MODE_NOT_FEASIBLE': the user imposed a mode, a path was computed but this mode
                         is not feasible
    'NO_SPECTRUM': a path, a mode were selected but there is not enough spectrum available on
                   this path

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
315eea1f55 Calling assignment in main program path_requests_run.py
function is called to assign spectrum to each request
result shows an additionnal column for blocking reason

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
b61e541e15 pth_assign_spectrum function assigns wl for all demands
this is a first function that assigns spectrum following the order
of requests according to the selected mode and nb of channels computed
based on requested path_bandwidth

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
81f88e78c7 Creation of the OMS structure to record spectrum assignment
oms_list contains the list of OMS and each OMS contains the list
of uid of each crossed elements (ordered)
each element is updated with the oms_id to which it belongs
each oms contains a bitmap with frequency slots according to
frequency min max defined in eqpt_config.json (SI) and in case oms
are defined elsewhere, there is an alignment of grids to ease computation
the build_OMS_list function builds the OMS list and implements oms attributes
in all network elements
Commit also contains basic functions to handle spectrum bitmap and indexes

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:16:36 +01:00
EstherLerouzic
87cc3dac00 Corrections with respect to second review
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 19:09:39 +01:00
EstherLerouzic
89e28cc7be Correct bug in parser: csv header comparison
previous comparison was done on the result from .sort(), ie None.
list.sort() method modifies the list in-place and returns None.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
2ba29a78c5 Bug: constraint not correctly interpreted
if name of the constraint input from user is not part of networks names
(typically in the case of excel input), then the program try to find a
name that is clode to the user name and  that is in the network list of
names. This list must not include trnasponder names (because transponder
end points are already listed as constraints and transponder in the
middle of a path are not supported yet)
This will be improved in the PR Ila names in constraints #278
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
f990a6c1be Correct some remaining strict loose into STRICT LOOSE
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
424e5a4786 change variable n to nel to conform to '[a-z_][a-z0-9_]{2,30}$' pattern
from codacy report

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
6a7a04ebb1 syntax correction
reordering imports call according to pylint3 + extra line removed

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
0366fc2956 Adding a no-path case for test coverage
add a no path case (request 6) in requests and expected responses.
response is also generated if path is not feasible: checks
that it is correctly handled in csv and json responses

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
48b7d71f02 add a test on json response generation
create a json response based on test file and compare it to expected
response.
comparison first checks that there is no missing or extra key
and then compares keys

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
715baf2a1c correct compare response header test to support any order
previous test assumed same order for header fields.
order the headers in the same way in order to support different
types of order

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
e55cea776e improve code quality
remove unused import, use correct indents, remove extra spaces
add function description

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
b388d143fd change assert to raise AssertionError
use
  if not condition:
  raise AssertionError()
instead of
  assert condition
according to codacy recommendation

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
c592c572d8 adding a test for the csv creation
using pandas package to have an easy ordering of response column:
- test that the generated header is as expected
- read the response. In order to support different orders wrt
  response answer and field answer, test function frst orders lines
  according to response index and then to columns (fields of the answer).
check that the answers are as expected

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
dfa0a26a28 changes to improve quality
minor name refactor
indent corrections
minor fixes for spacing

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
609cd94798 Remove @ in path-metric names and extend type to decimal64
@ character not correctly read with OpenDayLight yang tool used
for transportPCE project.
https://docs.opendaylight.org/en/stable-nitrogen/developer-guide/yang-tools.html#working-with-yang-model.
Changed the names of path metrics from osnr@ to osnr-

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
022f743db1 Change the exception handling if sync vector is absent
previous try section encompass errors that should not be silently
ignored. Correction pointed a default in a test file.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
1957beb1b6 Remove the filtering on transponders for correcting explicit route
anytype supported including transponder in order to accept well formed
route list from user (if user enters 'trx Lannion_CAS' this should be
accepted even if it repeats the source name)

A later PR better handles route list names

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
9ca72d6105 Correct csv creation in case of no path
previously reported the requested bandwidth, now fill it with an
empty string, same as the other fields.

This will be updated in a later PR when different kind of blocking
will be supported

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
e8e126a6ce update existing test files and examples according to ietf yang model
- 'loose' and 'strict' changed to 'LOOSE' and 'STRICT'
    - n, m changed to N, M
    - unused objects removed
    - ...
Also correct templates for service and response

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
7849782173 Change content of source and destination to transponder end-points
previously contained the user-given source name in excel file,
but could differ from effective transponder source/destination

now both source and src-tp-id contain the same info (gnpy does
not make difference between node and ports)

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
149a0da8c9 update csv creation according to all model changes
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
1e7c70a59b remove the restriction on fiber for the constraint
restriction was placed on fiber type because when using excel input
autodesign create fiber names with syntax not explicit to the user, so that
entering a node name as constraint may end up to a wrong interpretation eg
aa -bb  -> create names such as 'fiber aa to bb', fiber bb to aa' .
If user constraint is bb there is a ambiguity on the fiber direction.
However this is not a problem for user using json format.
So I preferred to removed this constraint now.
a later PR solves this naming ambiguity

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
c9d8282e7f enable power and nch to be optional in requests definition
since empty attributes have been removed from the requests with
previous changes, some aditional tests are needed to continue supporting
optional power and nch. (previous objects were created with null
or default values)
user may enter requests without specifying these fields, in this
case the object 'output-power' or 'max-nb-of-channel' do not exist.
the try /except form handles the corresponding exception and
default values are kept else.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:42 +01:00
EstherLerouzic
e7084a2c29 correction to support empty vectors of constraints
change the naming according to model update +
previous changes authorize empty list of nodes.
This new test supports this case

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:41 +01:00
EstherLerouzic
d79d2e0724 change the way transponder and mode are returned in the response
previous way used a wrong interpretation of hop-type. in ietf model,
hop-type is reserved for LOOSE or STRICT constraint description.
Instead, transponder info , according to ietf usual way, should be
included aas a new path-route-object type. such object is not yet
defined in IETF so this is a GNPY proposal to use a 'transponder' object
with type and mode attributes.
this is what has been ecncoded in reques.py for requests and for answers

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:41 +01:00
EstherLerouzic
402155c225 change the 'no path' case answer
in case no path could be computed, the answer is changed according to
ietf path-computation model to a simple 'no-path' object

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:41 +01:00
EstherLerouzic
e5ec669419 applying changes in the request dict creation
+ removing (currently) unused direction attribute.
This will be added again in a later PR when it will be used

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:41 +01:00
EstherLerouzic
8f424e8c9d correction of json generation: removing unused objects
- removing empty objects (if no content , removes the object),
    especially
  - removing empty synchronization vector if no disjunction
    constraint exists
  - removing optional power and nb of channel fields, if user
    did not specify any
when reading, first try if the object exists : try:/except to catch
this case
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:41 +01:00
EstherLerouzic
fea2b84bb9 correction of the json generation for path computation
- changes concerning names due to ietf path-computation draft changes:
        - 'unnumbered-hop' changed to 'num-unnum-hop'
        - suppression of 'label-hop'
        - 'link-diverse' and 'node-diverse' changed to 'disjointness'
      - correction because of previously wrong interpretation of the model:
        - detailed succession on include nodes moved from
          'optimisation /explicit-route-include-objects'
          to 'route-object-include-exclude'
          in 'explicit-route-objects' attribute
      - correction of keywords
        - n and m replaced by N and M
        - strict and loose replaces by STRICT and LOOSE strings
        - change the name of respponse from 'path' to 'response'
    example service file  corrected accordingly

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-10-09 15:38:41 +01:00
Jan Kundrát
0c918940c4 Merge pull request #313 from jktjkt/codacy-assert
codacy: do not complain about asserts in the test suite
2019-10-06 21:49:22 +00:00
Jan Kundrát
a63a6ac0ec codacy: do not complain about asserts in the test suite
Writing an explicit comparison followed by argument printing *and*
handling the final error is just backwards. Let the tool be quiet, this
nonsense does not really add any value.
2019-10-06 23:12:09 +02:00
Jan Kundrát
9f58b914d2 Merge pull request #308 from jktjkt/osnr-bw-printing
Rework OSNR printing
2019-10-06 19:17:46 +00:00
Jan Kundrát
029bac4b03 utils: more descriptive name for itufl
This might have nothing to do with the ITU frequency grid (it's really
just about a uniform distribution), so let's give it a more readable
name and more readable parameters.
2019-10-06 20:43:50 +02:00
Jan Kundrát
a27ad57220 Do not use confusing default values
This looks like the root cause of bug #243, the default values suggested
that this function works in THz.

These defaults are not used anywhere, so let's get rid of something
which adds no value and actively confuses people.
2019-10-06 20:38:31 +02:00
Jan Kundrát
8d31d924f2 Remove unused function 2019-10-06 20:37:24 +02:00
Jan Kundrát
8c3b514f90 Only print out "pretty summary" when not doing the power sweep 2019-10-01 15:24:40 +02:00
Jan Kundrát
3df27fe315 Print both ASE-OSNR and final GSNR for both 0.1nm and signal-bw in power sweep
As agreed upon during today's coders call, this is something that is
needed by actual users in the field, so let's prioritize their needs
over clarity of output for demo purposes.
2019-10-01 15:21:34 +02:00
Jan Kundrát
a6087ce354 Highlight OSNR @ 0.1nm
Esther and Jonas pointed out that it is much more common to work with
OSNR measurements per 0.1nm rather than per signal bandwidth. This is in
line with what OSAs usually report and what transponder datasheets
specify.

cc #307
2019-09-27 09:22:08 +02:00
Jan Kundrát
aae0382523 docs: show a DOI
As per Alessio's request, this adds a DOI and makes this software
citeable. DOIs are assigned automatically via Zenodo in a process that's
integrated with GitHub.
2019-09-23 18:11:44 +01:00
Jan Kundrát
b0c2acb1b5 Merge pull request #302 from Telecominfraproject/develop
Merge develop into master in preparation for the 1.8 release

Highlights for the upcoming release:

- Raman simulation
- automatic building of Docker images
- bugfixes, refactoring, CI and docs improvements
2019-09-23 16:51:28 +00:00
Jan Kundrát
a52c96ae2e Merge pull request #301 from jktjkt/develop
docs: Explain how to use Docker
2019-09-22 23:36:44 +00:00
Jan Kundrát
bf28821b5b Merge pull request #299 from jktjkt/cleanup-printing
(This replaces #261 by me and #293 by Jonas.)

##     Remove debug printing from propagate()
This is inspired by #293. The original issue was that the transponder OSNR was not accounted for. Rather than making the `propagate()` and `propagate2()` more complex, let's move the actual path printing to the demo code. There's no secret sauce in there, it's just a simple for-each-print thingy, so let's remove it from the library code.
    
fixes #262

## doc: fiber length summary in km, not meters

Reading `80000m` is a bit more complex than just `80 km`. Also let's add a space between the numebr and the unit for better readability.

## examples: color highlighting of the "most interesting output"
    
I think that this SNR value represents the most important output of this  example. There's plenty of debugging info on display already, so let's make this one more prominent.
    
I was thinking about moving the highlighting to elements' each `__str__()` function, but that felt a bit like layering violation to me. I could have also changed just the transponder's `__str__()`. In the end, I think that repeating just the final GSNR at the link-end transponder makes most sense here. This is aligned with what we talked about at yesterday's (on 2019-09-18 -- note that this is a backport from #261) demo for Microsoft, after all.
2019-09-22 23:32:36 +00:00
Jan Kundrát
328bd6ea71 docs: Explain how to use Docker 2019-09-23 00:29:59 +01:00
Jan Kundrát
ec9eb8d054 examples: commented-out path printing for Esther
I'm trying to clean up the core code while not causing too much trouble
to our users. Esther pointed out that there's an internal use case at
Orange where people are looking at the incremental results when
computing paths.

I strongly dislike commented-out debugging code, but for the sake of
progress, let's put it in here. It's roughly as good as that unused
`show` parameter, and it allowed a refactoring to make the code more
readable.
2019-09-23 00:14:56 +01:00
Jan Kundrát
f8c8526045 docs: remove duplicate cd
This whole section should be nuked, but that will have to wait until
after the next release for simplicity.

fix #283
2019-09-23 01:03:30 +02:00
Jan Kundrát
d8c236bb44 examples: do not measure time taken
This is an equivalent of 9c9e3be967 for
the other example.
2019-09-22 16:44:00 +01:00
Jan Kundrát
33ff0910b8 Remove unused function
This has been marked obsolete for 11 months. It's also one of the
callers of the propagate(), so it's being touched by that change which
removes the `show` parameter. This commit will make it easier to put
debug path printing just where it's really needed.
2019-09-22 12:20:11 +02:00
Jan Kundrát
faa69917d9 example: Use "Total SNR", not "GSNR"
Various presentations from Polito are slowly changing to use "GSNR" as a
"Generalized SNR", but it's true that our code does not use this term
anywhere, and that it is not properly explained. Let's wait a bit for
this term to become a bit more mainstream and for updated docs on our
side; then this commit can be safely reverted.

Thanks to Jonas for reporting this.
2019-09-20 17:11:42 +02:00
Jan Kundrát
d9f5ca9827 docs: Document Transceiver.mode.OSNR
I am not changing the JSON key name now because of the plan to go with a
full-blown YANG model (see #266).

fixes #298
2019-09-19 09:44:56 +02:00
Jan Kundrát
c817ef7335 examples: color highlighting of the "most interesting output"
I think that this SNR value represents the most important output of this
example. There's plenty of debugging info on display already, so let's
make this one more prominent.

I was thinking about moving the highlighting to elements' each __str__()
function, but that felt a bit like layering violation to me. I could
have also changed just the transponder's __str__(). In the end, I think
that repeating just the final GSNR at the link-end transponder makes
most sense here. This is aligned with what we talked about at
yesterday's (on 2019-09-18 -- note that this is a backport from #261)
demo for Microsoft, after all.
2019-09-19 09:25:31 +02:00
Jan Kundrát
07de489d6b doc: fiber length summary in km, not meters
reading "80000m" is a bit more complex than just "80 km". Also let's add
a space between the numebr and the unit for better readability.
2019-09-19 09:25:31 +02:00
Jan Kundrát
acafc78456 Remove debug printing from propagate()
This is inspired by #293. The original issue was that the transponder
OSNR was not accounted for. Rather than making the propagate() and
propagate2() more complex, let's move the actual path printing to the
demo code. There's no secret sauce in there, it's just a simple
for-each-print thingy, so let's remove it from the library code.

fixes #262
2019-09-19 09:25:31 +02:00
Jan Kundrát
325721545e Docker: array for CMD is more idiomatic
Co-Authored-By: Jonas Mårtensson <jonas.martensson@ri.se>
2019-09-18 13:15:20 +02:00
Jan Kundrát
dbe2bf560c Travis: Docker: Fix version numbers
This is due to travis-ci/travis-ci@7422.
2019-09-17 21:03:58 +02:00
Jan Kundrát
7872cc2203 Merge pull request #295 from jktjkt/docker
Automatic building of Docker images
2019-09-17 18:44:50 +00:00
Jan Kundrát
25b4d0e755 Automatic building of Docker images
A third, independent job in Travis CI just for building a container
image (and testing it a little bit). Here's how to use it once it is
built and published and pulled:

  $ docker run -it --rm --volume $(pwd):/shared telecominfraproject/oopt-gnpy

One can also pass commands to it directly. Note that the *relative* path
specifier given as `./` is required. One could possibly get around it by
$PATH manipulation, but hey, I have to stop somewhere.

  $ docker run -it --rm --volume $(pwd):/shared telecominfraproject/oopt-gnpy ./transmission_main_example.py

Thanks to Jonas for the --volume option trick and for that early
non-overwriting copying for the example directory.

The `install` phase in Travis CI is apparently common for all jobs in
the build matrix (it's rather confusing what they call a "stage" and
what is a "job" for them, and what their mutual relation is). Because
there's no point in doing any kind of installation in case when we're
building for Docker, let's just move the old `install` block into
`script`. With just that, however, Travis adds an implicit `pip install
-r requirements.txt` in an attempt at being helpful. In short, having
completely different "stuff to be done as a check" is very painful with
Travis.

The individual "script lines" in a job's `script` section are always
executed, all of them, even if a previous one failed. That's why I moved
the actual Docker invocation into an external shell script. When they
were not in a shell script and the script lines contained `set -e`, then
a failure of a particular command would cause the build to be marked as
"errored" instead of "failed" within Travis. Also, the multiline if
conditionals were rather painful to write.

One commit might end up in different branches. If that happens, the
second build would overwrite the image tag in the docker registry, which
is rather suboptimal. Let's try to fetch that image first, and only
update the latest/stable tags if the image was already available
beforehand.

fixes #260
2019-09-17 20:37:45 +02:00
Jan Kundrát
9af1c90664 Merge pull request #249 from jktjkt/bug-243
Do not mix THz and Hz
2019-09-04 08:18:49 +00:00
Jan Kundrát
6b4d44a3f1 Merge pull request #292 from ojnas/fix-snr-per-channel
Fix per-channel SNR output
2019-09-03 08:23:28 +00:00
Jonas Mårtensson
2faf8d2cdd - Fix output per-channel SNR to include contribution from add_drop_sonr and tx_osnr
- Avoid recalculating SNRs in transmission main example since they are already calculated and stored in the Transceiver class
- Also include per channel power (useful for checking tilt) and OSNR ASE in the output
2019-09-03 09:25:09 +02:00
Jan Kundrát
676c94ddf2 Merge pull request #289 from jktjkt/raman-nli-interpolation 2019-09-02 21:02:58 +00:00
Jan Kundrát
6f93b64f84 Simplify logic for showing per-channel results
As Jonas pointed out, the code used to contain a check for non-nan
values, effectively skipping channels where the Raman gain was not
explicitly computed.

Now that we do not introduce NaNs into some channels anymore, this
shortcut no longer works. We could either add explicit filtering for
only showing those channels which are covered by the Raman engine, but
then the result would be rather confusing in the non-Raman case. One
could also add another column with the simulated vs. approximated NLI,
but when I tried this, the output looked a bit cluttered.

I think that the best course of action for now is to just show info
about all channels (if asked by the user). So this is just a cleanup for
a condition which is now always on.
2019-09-02 21:10:20 +02:00
Jan Kundrát
54bf426472 Raman: linear interpolation of channel NLI
The Raman engine computes NLI just for a subset of channels; this is an
important speed optimization because the computation is rather CPU
heavy. However, the code just left NaNs in place for NLI of those
channels which were not explicitly simulated.

This is wrong because these NaNs propagate all the way to the total
input/output powers per the whole spectrum, etc, leading to NaNs being
shown to the user.

This patch uses a very simple linear approximation just in order to
prevent these NaNs.

fixes #288
2019-09-02 21:10:20 +02:00
Jan Kundrát
1862ce9104 Merge pull request #263 from aleFerrari/raman_feature
Raman feature

I've added some very basic documentation (really, just usage instructions). What is still missing:

- more specific docs
- test coverage (right now, there's none!)

We've agreed to merge this sooner rather than later so that more people can reasonably test it. There's a bunch of things that I'm not happy with, such as the default example showing NaNs as the OSNR to the user at the end transponders, etc (I suspect this is due to simulating just some channels and no approximation, right?), but let's solve these based on feedback from testing.

As proposed in my earlier comments, I went ahead and merged the wrapper example code into the `transmission_main_example.py`. So here's what needs to be done in order to "use Raman amplifiers" in a basic example, with no autodesign:

- replace some `Fiber` elements with a `RamanFiber` in the network topology JSON
- invoke the example with a `--sim examples/sim_params.json` which activates the Raman engine
- pass the `--show-channels` flag so that the actual results are visible

This is more or less covered by the docs skeleton.
2019-08-08 12:05:53 +00:00
Jan Kundrát
3771c13d32 docs: basic usage instructions for Raman 2019-08-08 13:38:23 +02:00
Jan Kundrát
f1d0230dad docs: skeleton of Raman-specific properties
What I'm including here are comments from Vittorio (thanks!). It seems
that the majority of actual numeric parameters which drive the
simulation are undocumented, so this one will have to wait for Alessio
or someone else from the Polito team to fill the blanks.
2019-08-08 13:38:23 +02:00
Jan Kundrát
182929cc96 Unify transmission_main_example and the Raman wrapper
There were just these substantial differences:

- the Raman code showed a per-channel SNR summary, this is now
  controlled via the `--show-channels` option

- when a Raman fiber is used the `--sim` option for specifying input
  simulation parameters is now mandatory

I'm therefore merging these two files even though we've rpeviously
decided not to do this -- consult the review comment at
https://github.com/Telecominfraproject/oopt-gnpy/pull/263#discussion_r310506082
and the discussion during this Tuesday's coders call). If this turns out
to be a problem for autodesign, we can always revert this.

One possible catch is that the final "SNR total" shows NaN for the
default Raman example. That's just the way the simulation engine works
right now, I'm afraid. The `--show-channels` options helps a lot.
2019-08-08 13:38:23 +02:00
Jan Kundrát
81585c5a86 Unify implementations of psi computation
Both of these places referred to "eq. 123 from arXiv:1209.0394", the
only difference (apart from the source of the input parameters, beta2
and asymptotic_length) was calling the two branches "SCI" and "XCI" vs.
"SPM" and "XPM".

In this commit I've only moved the code to a single implementation. The
input data are still being read from the same parameters, of course.
2019-08-08 13:38:23 +02:00
Jan Kundrát
2f52c11589 More intuitive name for list of channels where Raman gain is computed 2019-08-08 11:34:33 +02:00
Jan Kundrát
0f4d8573cf Move Raman parameter propagation to gnpy.core.network
Conceptually, this is just about propagating the input parameters (which
drive the simulation) into all RamanFiber instances. The network module
already contains similar functions, let's move it there.
2019-08-08 11:19:25 +02:00
Jan Kundrát
660b8b3c6e Use lin2db() when we have it 2019-08-08 11:16:26 +02:00
Jan Kundrát
71d6a1138c Fix a typo in my e-mail address
This has been around since before 1a104956, but I haven't noticed the
missing `.com`.
2019-08-08 10:53:52 +02:00
Jan Kundrát
a6e741d8fe Do not copy the whole Fiber class 2019-08-08 09:55:23 +02:00
Jan Kundrát
58bcf65cf6 Raman: do not introduce a new copy of the equipment library
Compared to `eqpt_config.json`, the only extra content in the new copy
was the `RamanFiber` block. There's no disadvantage in just using one
equipment library; the traditional code can easily ignore the RamanFiber
stanza.
2019-08-07 23:45:44 +02:00
Jan Kundrát
27ce55de38 diagnostics: Correctly report all spans, not just the Raman ones
It's just a harmless message, the path traversal was still using all
connections as defined in the topology JSON.
2019-08-07 23:41:30 +02:00
Jan Kundrát
36ca22db9b Raman: use logging instead of debugging print()s 2019-08-07 23:31:20 +02:00
Jan Kundrát
33a8de9b39 Raman: stricter validation of input parameters
The goal here is to try to prevent typos in configuration from slipping
in undetected.
2019-08-07 13:36:21 +02:00
Jan Kundrát
22b76e36db Fix typos in Raman's frequency offset
This is a typo, the parameters were clearly supposed to be uniformly
spaced. It doesn't affect the results (much), because the remaining
values are "close enough" for a reasonable interpolation.

Using the existing Raman example, the difference in OSNR ASE at the
signal bandwidth is 0.01 dB (31.46 -> 31.47 dB).

Perhaps it would make sense to enforce that these offsets are a
monotonic sequence.
2019-08-07 13:19:17 +02:00
Jan Kundrát
528ff31590 CI: Run the Raman example as well 2019-08-06 11:56:01 +02:00
Jan Kundrát
4d6966cbd3 Remove unused imports 2019-08-06 11:51:25 +02:00
Jan Kundrát
9c9e3be967 Do not measure time from the example directly
Let users wrap this with the Unix `time` command if they are interested
in the time spent.
2019-08-06 11:49:32 +02:00
Jan Kundrát
2dd4745ef7 Merge pull request #281 from jktjkt/no-convenience-access
Remove property aliases
2019-08-06 09:46:36 +00:00
Jan Kundrát
4e786a32b5 Merge branch 'no-convenience-access' into raman
This required some adaptations in the new Raman code now that the
property aliases are gone.
2019-08-06 11:43:16 +02:00
Jan Kundrát
6ecb2c85e2 Merge remote-tracking branch 'origin/develop' into raman
Required fix-ups:
- ROADM restrictions
2019-08-06 11:34:39 +02:00
Jan Kundrát
cd234a909b Remove unimplemented and unused code 2019-08-06 11:22:56 +02:00
Jan Kundrát
c249f44ea1 Remove property aliases
For some reason, the code allowed using "convenience names" for
accessing properties since commit 58ac717f. To me, this looks like an
obvious anti-pattern because accessing a single property via three
different names only makes the code less readable. Let's kill this
"feature".

In case of the `Power` class, the code used "ase" and "nli" on the
majority of places, so let's use these abbreviations instead of their
spelt-out variants.

SpectralInformation was "clean" already, but there were calls to the
`update()` wrapper around the `namedtuple._replace`.  Given that there
were no property aliases, it's safe to just call `_replace()` directly.

In case of the `Pref` class, once again always use `p_span0`, `p_spani`
instead of `p0` and `pi` -- it's a trivial change.
2019-08-06 11:14:28 +02:00
Alessio Ferrari
ed1f51393a method name for computing NLI updated in sim_params.json 2019-08-01 14:54:33 +02:00
Alessio Ferrari
8bd43130ab modified Raman pumps in the example raman_edfa_example_network.json to make Raman effect more evident 2019-08-01 14:48:16 +02:00
Alessio Ferrari
6c975a53a1 minor fix to eqpt_with_raman_config.json 2019-08-01 14:45:17 +02:00
Alessio Ferrari
8a1001cd40 dynamic evaluation of threshold frequency between near XPM and far XPM 2019-08-01 14:42:47 +02:00
Alessio Ferrari
beb2b576aa changed output of propagate_raman_fiber to return a list 2019-08-01 14:41:40 +02:00
Alessio Ferrari
8f3923046b alpha0 computation moved from NLI solver to Fiber Params 2019-08-01 11:36:32 +02:00
Alessio Ferrari
88c68d2065 introduced classes for the parameters 2019-08-01 11:32:07 +02:00
Alessio Ferrari
8bcde72a10 changes on variable names to be more clear 2019-08-01 10:49:25 +02:00
Alessio Ferrari
4653dbcf4b introduced a faster computation for XPM 2019-08-01 10:48:02 +02:00
Alessio Ferrari
cde08b32a4 output fixes on transmission_with_raman_main_example.py 2019-08-01 10:45:20 +02:00
Alessio Ferrari
2eed891f8d new variable names for SPM and XPM 2019-07-31 11:29:25 +02:00
Alessio Ferrari
c0b84e84c8 double underscore replaced with single one for some attributes 2019-07-31 11:24:16 +02:00
Alessio Ferrari
2c20fd3f9f strings 'XPM' and 'SPM' removed while computing NLI, now it is implicit 2019-07-31 11:20:34 +02:00
Alessio Ferrari
f4db56ca29 minor fix to comments 2019-07-31 11:18:46 +02:00
Alessio Ferrari
5b6d58ac7d minor fix to printed text 2019-07-29 14:45:32 +02:00
Alessio Ferrari
ecb8bd9fbe new print of final propagation 2019-07-29 14:17:27 +02:00
Alessio Ferrari
2f9385451f removed old printing of spectral information 2019-07-29 14:16:49 +02:00
Alessio Ferrari
1a1346461b self.pch_out_db in RamanFiber now takes into account Raman gain 2019-07-29 12:33:05 +02:00
Jan Kundrát
27dcd29074 CI: Attempt to builds docs during regular CI, too
Since ReadTheDocs does not support [1] running as a CI check on GitHub
yet, let's make sure that we at least run sphinx. This would have caught
a recent docs breakage (see parent commit).

[1] https://github.com/readthedocs/readthedocs.org/issues/1340
2019-07-04 00:37:58 +02:00
Jan Kundrát
93986f36c3 docs: Fix docs building
Docs building started failing after our dependency update. The reason is
that the updated sphinx bibtex extension started being a bit stricter in
their interpretation of bibliography files:

  parsing bibtex file /home/jkt/work/TIP/oopt-gnpy/docs/biblio.bib...
  Exception occurred:
    File "/home/jkt/work/TIP/_py/lib64/python3.6/site-packages/pybtex/errors.py", line 78, in report_error
      raise exception
  pybtex.database.input.bibtex.DuplicatePersonField: entry with key bononi_modeling_2012 has a duplicate year field

Fix this by using `date` as field name when a full date is given.
2019-07-04 00:37:23 +02:00
Jan Kundrát
a6ab8055b1 CI: do not attempt to cd twice
I misunderstood how Travis CI works; it's not a subshell, it's something
which affects the other commands. Let's just run everything from the
top-level directory.
2019-07-04 00:28:33 +02:00
Jan Kundrát
31ea479d7f Merge pull request #268 from jktjkt/prune-dependencies
Update and rework dependency handling

fixes #269
2019-06-25 15:25:48 +02:00
Jan Kundrát
89fb2e047b Update dependencies
Pinning dependencies to a specific version is safe in terms of
preventing accidental breakage, but it has a cost of possibly using
outdated dependencies.

Let's see if we can rely on the semantic versioning of our dependencies
here.

I'm doing this instead of other approaches (such as splitting the
top-level deps from "the rest of the dep chain" [1][2]) because I require
additional tools in my venv, such as the python-lnaguage-server. There
would be little point in injecting these into requirements to be used by
other people.

[1] https://www.kennethreitz.org/essays/a-better-pip-workflow
[2] 59eb51c026
2019-06-21 12:13:38 +02:00
Jan Kundrát
8f705e6173 requirements: only list what we really need
Listing all transitive dependencies, including their respective versions
(as done by `pip freeze`, for example) is something which effectively
leads to installing outdated and possibly vulnerable software.

Let's get rid of the transitive dependencies.

Thanks to Esther for noticing.
2019-06-21 11:46:17 +02:00
Jan Kundrát
8f735316f5 Merge pull request #264 from Telecominfraproject/master
Merge master into develop

...so that the docs are updated and synced.
2019-06-19 14:28:45 +02:00
Jan Kundrát
0d7a1871a1 Merge pull request #259 from jktjkt/docs
docs: Show a pretty picture of GNPy in action

Thanks to Esther for suggesting this, and to Gert and Esther for their reviews.
2019-06-19 11:57:55 +02:00
Jan Kundrát
33832b3d25 docs: Animated invocation of transmission_main_example.py
Tools used:
- [asciinema](https://asciinema.org/) for recording the session
- `$EDITOR` for a few simple fix-ups
- [termtosvg](https://github.com/nbedos/termtosvg) for creating the
resulting SVG so that it's fully self-contained
- pushing the results to the `gh-pages` so that it's available and not
subject to [random filtering and
breakage](https://stackoverflow.com/questions/13808020/include-an-svg-hosted-on-github-in-markdown),
such as the animated SVG not actually animating

I'm still linking to asciinema.org because that site offers nice
cut-and-oaste capabilities, playback control, etc.

Co-Authored-By: Gert Grammel <ggrammel@juniper.net>
2019-06-19 11:54:44 +02:00
Alessio Ferrari
4da7f0cc38 added evaluation of the SNR at the end of the line 2019-06-18 13:31:50 +02:00
Alessio Ferrari
e29f8485ea integration of the GGN-model with spectral separation 2019-06-18 13:31:01 +02:00
Alessio Ferrari
2da344a563 sim_params now include GGN model 2019-06-18 13:27:02 +02:00
Alessio Ferrari
2a0cb8e14f raman pump powers reduced in raman_edfa_example_network.json 2019-06-18 13:21:05 +02:00
Jan Kundrát
e1dc3dc357 docs: no need to cd examples first
The code determines these paths relative to the actual example .py file
already. Given that `pytest` and other bits expect to run from the
top-levle directory, let's remove this misleading instruction from the
docs.
2019-06-17 20:01:36 +02:00
Jan Kundrát
8259124f73 docs: Show a pretty picture of GNPy in action
I do not have a vector image of this one, unfortunately. The data
apparently comes from "someone at TIP", perhaps a hired graphic
designer. It was passed to me by Diego Landa when I asked for the
original dataset.
2019-06-17 19:55:00 +02:00
Alessio Ferrari
0422956ac6 RamanFiber propagate method now call the propagate_raman_fiber in the science_utils module 2019-06-11 13:40:42 +02:00
Alessio Ferrari
ff82c5171b propagate_raman_fiber function introduced 2019-06-11 13:39:41 +02:00
Alessio Ferrari
f9bd6310f1 introcude NLI solver 2019-06-11 13:38:39 +02:00
Alessio Ferrari
471eab126e fix raman solver parameters 2019-06-11 13:38:05 +02:00
Alessio Ferrari
6ad011d12d raman_efficiency included in RamanFiber equipment 2019-06-11 13:28:39 +02:00
Alessio Ferrari
561c8aff85 modified sim_params for transmission_with_raman_main_example.py 2019-06-11 13:27:40 +02:00
Alessio Ferrari
5cf5dd2234 add temperature parameter in fiber 2019-06-11 13:24:12 +02:00
Alessio Ferrari
fb9915d301 add raman efficiency in SSMF equipment 2019-06-11 13:23:38 +02:00
Jan Kundrát
7ab67194d6 Merge a3778dfe8b into 603ac9d8c5 2019-06-11 08:47:09 +00:00
Jan Kundrát
603ac9d8c5 Merge pull request #257 from jktjkt/fixes
Python: do not use a mutable default value
2019-06-11 10:46:25 +02:00
Jan Kundrát
a3c7811e9d Merge pull request #256 from jktjkt/docs
Documentation fixes (plus an equipment.Fiber.beta2 change)
2019-06-11 10:45:23 +02:00
Jan Kundrát
a3778dfe8b Merge pull request #255 from jktjkt/weekly-calls
Encourage people to join us and to join the VCs
2019-06-11 10:45:00 +02:00
Jan Kundrát
2dff934612 CI: run the examples as well
We are not checking their results or anything, but it is nice to be able
to say "hey, these still work".
2019-06-07 00:02:53 +02:00
Jan Kundrát
89d666948e Fiber: beta2: use a default value directly
Rather than do the None-dance and document the default value within a
docstring, let's use the default value directly. This is safe because
numbers are immutable.
2019-06-06 23:55:16 +02:00
Jan Kundrát
c3499142b0 doc: hyperlinks++ 2019-06-06 23:47:52 +02:00
Jan Kundrát
d8feccc715 Python: do not use a mutable default value
Because default arguments are evaluated *once*, not every time they are
called, a mutable default value is not "reset", and this happens:

>>> from gnpy.core.node import Node
>>> x=Node('123')
>>> y=Node('456')
>>> print(x.metadata)
{'location': Location(latitude=0, longitude=0, city=None, region=None)}
>>> print(y.metadata)
{'location': Location(latitude=0, longitude=0, city=None, region=None)}
>>> y.metadata['foo']=123
>>> print(x.metadata)
{'location': Location(latitude=0, longitude=0, city=None, region=None), 'foo': 123}

This is easily fixable by using an immutable value as a placeholder
here.
2019-06-06 23:29:07 +02:00
Jan Kundrát
16173355f3 docs: random improvements and Sphinxiation 2019-06-06 23:26:12 +02:00
Jan Kundrát
f46134fda5 docs: document the _private methods
This is a reference documentation, so it makes sense for it to be
reasonably complete.
2019-06-06 23:05:42 +02:00
Jan Kundrát
bfecff0412 docs: use a default preset here to prevent extra repetitions 2019-06-06 23:04:34 +02:00
Jan Kundrát
168f1891cf docs: ensure that all modules are documented 2019-06-06 22:55:56 +02:00
Jan Kundrát
862845b4ac docs: minor grammar fixes 2019-06-06 21:51:48 +02:00
Jan Kundrát
b7a5dbff49 Encourage people to join us and to join the VCs
This was suggested by Esther on today's PSE group call.
2019-06-06 18:39:21 +02:00
Jan Kundrát
5be30d89a7 Merge pull request #230 from Orange-OpenSource/build_no_amp_in_roadm
ROADMs can now specify which amplifiers can be used as their preamps and boosters.
2019-06-06 12:47:06 +02:00
Esther Le Rouzic
d94dc51d88 Restrictions on auto-adding amplifiers into ROADMs
This feature is intended to support designs such as OpenROADM where the
line degree integrates a specific preamp/booster pair. In that case, it
does not make sense for our autodesign to "pick an amplifier". The
restrictions can be activated by:

- Listing them in `eqpt_config.json`, so that they are effective for all
ROADM instances.
- On a per-ROADM basis within the Excel sheet or the JSON definitions.

Restrictions apply to an entire ROADM as a whole, not to the individual
degrees.

If a per-degree exception is needed, the amplifier of this degree can be
defined in the equipment sheet or in the network definition.

If no booster amplifier should be placed on a degree, use the `Fused`
node in place of an amplifier.

Signed-off-by: Esther Le Rouzic <esther.lerouzic@orange.com>
Co-authored-by: Jan Kundrát <jan.kundrat@telecominfraproject.com>
2019-06-06 11:58:45 +02:00
Jan Kundrát
22acd88d44 Utility functions for pruning and merging
Co-authored-by: Esther Le Rouzic <esther.lerouzic@orange.com>
2019-06-06 11:42:05 +02:00
Alessio Ferrari
fd406c106b gn model introduced in the science utils module 2019-06-03 16:33:26 +02:00
Alessio Ferrari
16134b5caf +1 and -1 replaced with coporop and counterprop strings for Raman pumps description 2019-06-03 16:27:58 +02:00
Jan Kundrát
2c485efced Merge pull request #252 from Orange-OpenSource/bug_fixes_create_eqpt_sheet
Refactoring and bug fixes in an auxiliary transformation tool.
2019-05-31 16:26:10 +02:00
EstherLerouzic
279d08a0e8 Correct testTopology file
testTopology listed corlay twice in Eqpt although it is a ILA.
should appear only once in node A column

update expected parser results for this change in tests/data

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-05-31 11:31:46 +01:00
EstherLerouzic
1d4a8998e1 Bug fix on create_eqpt_sheet.py
program was not correctly listing nodes when links duplicate EDFA entries.
I refactored it to make it simpler to understand.

I added coordinates on ../tests/data/testTopology.xls for plot purpose

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-05-31 11:31:46 +01:00
Jan Kundrát
47a41e7980 Merge pull request #251 from jktjkt/json-parsing-exceptions
Use exceptions instead of `sys.exit()` when reporting configuration errors
2019-05-31 11:24:55 +02:00
Jan Kundrát
ecfc4a8cb2 One fewer exit() in a library scope 2019-05-30 13:58:33 +02:00
Jan Kundrát
2d66b6266b Add a missing FIXME for direct-printing of diagnostics 2019-05-30 12:39:42 +02:00
Jan Kundrát
b7afb5f9d2 Exceptions for errors in network topologies 2019-05-30 12:39:10 +02:00
Jan Kundrát
58c16a59ac Distinguish between "equipment library errors" and other "config errors"
And because no code for these "other config errors" has been merged yet,
this is just a placeholder for future work.
2019-05-30 12:28:47 +02:00
Jan Kundrát
f09789f5ef Refactor color temrinal escaping into a common module
I have no idea which one of the existing pypi modules is best for these,
and given that we're using just two escape codes, I think it makes sense
not to bother with a more capable third-party module just for two magic
strings.
2019-05-30 12:25:53 +02:00
Jan Kundrát
b2e12cd3e0 Use exceptions instead of direct-exit when parsing equipment JSON 2019-05-30 12:06:54 +02:00
Jan Kundrát
71b157a8ba Infrastructure for reporting configuration errors via exceptions
Once the actual config-parsing code start raising these exceptions
instead of directly calling sys.exit(), the user experience would
deteriorate due to raw exception traces. There's little value in the
trace itself, so just wrap the whole config loading with pretty error
formatting.

We still do not point to a specific place where that error is defined
(such as a line/column in a given JSON file) because that information is
already lost by the time we perform these checks.

Also, these checks are largely open-coded ad-hoc stuff. Some required
items are not covered, raising KeyError instead. We should get a formal
schema for these...
2019-05-30 12:06:54 +02:00
Alessio Ferrari
cb8affe9b2 transmission with raman integrated with sim_params configuration 2019-05-28 12:19:01 +02:00
Alessio Ferrari
3f7180c706 configure_network function created to configure the RamanFiber.sim_params in a network 2019-05-28 11:35:42 +02:00
Alessio Ferrari
f0bc2dc62f attribute sim_params added to RamanFiber 2019-05-28 11:11:40 +02:00
Jan Kundrát
9c95fd6b69 Do not mix THz and Hz
While the `itufl()` function uses THz by default, its values depend on
the frequency range that is passed via arguments. In this context, the
f_min and f_max come from the default SpectralInformation which is in
Hz.

Thanks to @ojnas for reporting this.

fixes #243
2019-05-27 18:35:14 +02:00
Alessio Ferrari
c0fda8c3a2 fix to Raman solver 2019-05-27 17:09:34 +02:00
Alessio Ferrari
bac20af381 eqpt_with_raman_config.json copied from eqpt_config.json and RamanFiber added into it 2019-05-27 17:08:36 +02:00
Alessio Ferrari
626211a320 Introduce RamanFiber in equipment 2019-05-27 17:04:33 +02:00
Jan Kundrát
783aaa8cb4 Merge remote-tracking branch 'origin/master' into develop 2019-05-27 12:27:23 +02:00
Jan Kundrát
768bd8af19 Merge pull request #247 from jktjkt/rst-fixes
docs: fix RST formatting and introduce CI coverage
2019-05-27 12:26:42 +02:00
Jan Kundrát
3894f52194 CI: test the RST files for validity
fixes #245
2019-05-27 12:08:02 +02:00
Jan Kundrát
dcfa9edb1c docs: Fix JSON syntax 2019-05-27 11:36:02 +02:00
Jan Kundrát
4ebdb5629c docs: specify code-block highlighter 2019-05-27 11:35:56 +02:00
Jan Kundrát
75b0668fc2 docs: Fix JSON syntax -- standard ASCII quotes 2019-05-27 11:35:52 +02:00
Jan Kundrát
5fe94ed463 docs: specify a correct markup language for JSON snippets
Some of these are "pseudo-JSON" with `...` etc, so one has to use `none`
for these.
2019-05-27 11:35:49 +02:00
Jan Kundrát
db21b97603 docs: fix table markup syntax
Oh boy, this is annoying.
2019-05-27 11:35:47 +02:00
Jan Kundrát
8074d0c548 docs: fix code markup
- use a correct highlighter
- ensure that the code is not parsed as RST code-block options
2019-05-27 11:07:53 +02:00
Jan Kundrát
2d611afbb0 Merge pull request #241 from Telecominfraproject/master
Merge master into develop
2019-05-24 11:36:46 +02:00
Jan Kundrát
bc42507724 Merge pull request #239 from jktjkt/spectrial-info-fixes
docs: Fix rendering of the SpectralInformation docs
2019-05-24 11:24:00 +02:00
Jan Kundrát
ff82ab5718 docs: Fix rendering of the SpectralInformation docs
Due to a misaligned |, the table was not rendered at all in the GitHub
overview.

Thanks to Gert for catching this in his brownfield doc.
2019-05-24 11:00:14 +02:00
Alessio Ferrari
62fe374e15 modified fiber type in raman_edfa_example_network.json to use new class RamanFiber 2019-05-24 10:38:24 +02:00
Alessio Ferrari
e519a3bc39 add class RamanFiber 2019-05-24 10:36:29 +02:00
Jan Kundrát
4f146d12ee Merge branch 'pytest-warnings' into develop 2019-05-24 01:45:53 +02:00
Jan Kundrát
46d6074ad5 Merge branch 'trivial-bugfixes' into develop 2019-05-24 01:40:07 +02:00
Jan Kundrát
cbb61f1240 tests: consult the doctests as well
There are no doctests right now, but they will (might?) be added (see
PR #230).
2019-05-24 01:38:12 +02:00
Jan Kundrát
0e9f3c3576 tests: re-enable warnings
For some reason, warnings were disabled in commit 07de78c, but it is not
clear to me what the purpose was back then. It passes without warnings
locally, and I think that warnings are useful in general, so let's give
the CI a try.
2019-05-24 01:37:49 +02:00
Alessio Ferrari
92f11dc075 add an example of json including Raman pumps in fiber 2019-05-23 15:05:08 +02:00
Jan Kundrát
3aa0a0999b Update networkx and xlrd
...in an attempt to kill some deprecation warnings under Python 3.7.
2019-05-22 17:46:41 +02:00
Jan Kundrát
e86fbcfa5b CI: Disable Codecov comments
The results are still perfectly visible within the *Checks* tab, so
let's cut the comment spam a little bit.

I've already done the same for lgtm.com; for that app, it's done in
their service's configuration within the web app.
2019-05-22 17:33:13 +02:00
Alessio Ferrari
0b2ee6fdaf add Raman solver 2019-05-22 12:05:51 +02:00
Alessio Ferrari
35f3866882 load_sim_params used to parse sim_params.json and new default filename 2019-05-22 11:06:58 +02:00
Alessio Ferrari
d86bea80d3 fix load_sim_params 2019-05-22 11:00:02 +02:00
Alessio Ferrari
13b4b5072f fix sim params 2019-05-22 10:59:00 +02:00
Alessio Ferrari
efae43f122 utility to load sim params from json 2019-05-22 10:26:24 +02:00
Alessio Ferrari
45e8c8692b add science utils module 2019-05-22 10:09:27 +02:00
Alessio Ferrari
f8fc2a5050 simparams enriched with Raman and NLI parameters 2019-05-22 10:02:45 +02:00
Jan Kundrát
3c20d57cc4 Be sure that both nf_min and nf_max are removed
If `nf_min` was not present, then `nf_max` would be left remaining
because an attempt to nuke `nf_min` raised an exception.

Thanks to codecov.io for making it easy to spot this on the coverage
page.
2019-05-21 16:29:09 +02:00
Jan Kundrát
2cb3858330 Exceptions must be raised, not just instantiated
Found by lgtm.com code scanner, thanks!
2019-05-21 16:20:26 +02:00
Jan Kundrát
925d36a561 CI: do not require sudo
...which might give us a faster, container-based build environment as
long as some legacy, several-years-old docs are still correct. Let's
give it a try.
2019-05-21 13:04:44 +02:00
Jan Kundrát
0ffaca91cc CI: Test on Python 3.7 as well
According to the Travis-CI docs, this requires switching to a newer base
OS image. It might not require an explicit pin like this in near future
as the default is, apparently, being migrated at this very time, but I
do not feel like working with a random rolling-update process of a
third-party service, thank you.
2019-05-21 12:27:41 +02:00
Jan Kundrát
d3a0f1d969 CI: remove unused section 2019-05-21 12:17:33 +02:00
Jan Kundrát
37704db583 CI: Report test code coverage to codecov.io 2019-05-21 12:09:24 +02:00
Alessio Ferrari
aadd038bbe transmission w Raman gets sim params 2019-05-20 17:17:34 +02:00
diegolaunch
6d601b4267 Merge pull request #232 from jktjkt/unify-authors-list
Add all missing authors and sync the two lists of contributors
2019-05-20 08:10:24 -07:00
Jan Kundrát
194798d881 Add all missing authors and sync the two lists of contributors
These two lists were not synced with each other, and some contributors
whose commits have been merged in git were missing. The resulting list
is a bit longer because not everybody who contributes does that strictly
via commits pushed to GitHub.
2019-05-20 12:31:07 +02:00
Jan Kundrát
1a10495645 Replace James with myself as a maintainer
The credits/attributions have not been touched of course.
2019-05-20 12:31:04 +02:00
Alessio Ferrari
0e2316513e add raman transmission 2019-05-18 15:41:59 +02:00
Alessio Ferrari
72ce4e2fad add json sim_params 2019-05-18 15:39:48 +02:00
Jan Kundrát
4ad7311e18 Merge branch 'develop' 2019-05-17 21:25:08 +02:00
Jan Kundrát
fa2b0e8fad docs: use github for release management
Teh changelog is available over GitHub (and also within the git tags,
now that I added the missing v1.2 tag), so let's cut the verbosity a bit
by just linking to a dedicated page for this trivia.
2019-05-17 20:57:38 +02:00
Jan Kundrát
78eb926693 docs: prune the list of branches
The README is way too long already. Welcoming potential users and
contributors with an overview of the project's history does not help,
IMHO.
2019-05-17 20:46:37 +02:00
Jan Kundrát
3613efbaab docs: there's the --power argument for launch power config 2019-05-17 20:31:50 +02:00
Jan Kundrát
2e732854b3 docs: Improve typography and fix typos in the README
It's quite common to use a ``monospace font`` for referring to
identifiers, so let's use that for GitHub's rendering of the main
project's README.
2019-05-17 20:29:14 +02:00
Jan Kundrát
f9560d6b1d Merge branch 'master' into develop
This is mainly to resolve one conflict in README and to have CI run its
tasks.
2019-05-16 13:09:42 +02:00
Jan Kundrát
51b0826398 Merge pull request #226 from Orange-OpenSource/bugs_correction
correction of 2 bugs
2019-05-16 13:06:09 +02:00
Jan Kundrát
af0adb454d Fix RST syntax in the ROADM table
The table was completely hidden from the HTML rendering at GitHub. The
space between the "left edge" of the table and the text content appears
to be substantial.
2019-05-16 12:41:01 +02:00
EstherLerouzic
cdd4c571b0 correction of bugs
exception KeyError type in service sheet not correctly catched
print Request.mode instead of Requestmode (not defined at this point)

selection of modes did not respect min spacing criterium:
constraint added

transmission_main did not give SNR in 0.1 nm: added in std out

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-05-07 12:28:28 +01:00
James
9c440764c7 Merge pull request #224 from Orange-OpenSource/auto_design3
Auto design3
2019-04-18 11:42:09 -04:00
James
6e94834033 Merge pull request #223 from Orange-OpenSource/addWeightEdge
Add weight edge
2019-04-18 11:41:46 -04:00
EstherLerouzic
1720ed23c9 Sort all simple paths using length instead of hop
when constraints such as include noe or disjuntion is applied
the candidates are sorted with shortest path in length first

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-04-18 11:16:07 +01:00
EstherLerouzic
137fab1d92 Adding weights on edges to have shortest path in length instead of hops
add weight = length of fiber nodes on connections wher from_node is a fiber
add weight = 0.01 km on other edges

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-04-18 11:16:07 +01:00
Jean-Luc Auge
0fee63fa81 manage empty edfa requirement list in auto_design
code exit if no edfa are available and raman do not fill min gain
requirement

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-18 12:02:57 +02:00
Jean-Luc Auge
d5f0d80eed clean up auto_design code
replace lambdas, filter and map functions with comprehension list

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-18 12:02:57 +02:00
Jean-Luc Auge
b7d4d43f56 improve auto_design selection of raman
-separate edfa and raman_list of acceptable amplifiers to better
customize criteria between edfa and raman amplifiers
for example min gian requirement: no extended min gain range for raman
amplifiers because there is always an edfa solution, which is better for
small gains.

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-18 12:02:57 +02:00
Jean-Luc Auge
c0379a1981 verbose possibility for paht_request_run
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-18 12:02:57 +02:00
Jean-Luc Auge
e5db8e42d1 Display warning when Raman is used above max fiber lineic loss
- in auto-design, Raman is already not used if lineic fiber loss >
eqpt_config.json [Span][max_fiber_lineic_loss_for_raman]
- this commit ensures that a warning is displayed even when auto-desing
is not used (when the network equipment configuration is imported from
json or xls topology)

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-18 12:02:57 +02:00
Jean-Luc Auge
27cf9806f0 bug fix in mixed auto/imported design with output voa
non zero voa values were transferd to the following node

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-18 12:02:57 +02:00
Jean-Luc Auge
5dbb5cd112 bug fix with FUSED spans and EOL
EOL span ageing was added for each connector "to be fused" span
=> now, EOL ageing is only added to the last span of the fused spans
section.

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-18 12:02:57 +02:00
Jean-Luc Auge
af75569eb8 clarify code on connector and EOL default loss
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-18 12:02:57 +02:00
James
aebf2ff270 Merge pull request #216 from Orange-OpenSource/automatic_design_test
Automatic design test
2019-04-16 12:21:29 -04:00
EstherLerouzic
3bcdeda3e9 Small fix
Coordinates in files changed during the autodesign compared
to previous autodesign version: autodesign_expeted test
files updated

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-04-09 17:55:19 +01:00
EstherLerouzic
7433667243 Update of README.rst wrt Roadm equalization feature and addresses issue #210
TODO update README with dual stage Edfa type_def
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-04-09 17:13:40 +01:00
EstherLerouzic
d7c009167f add units on the stdout answer given by transmission_main_example.py (issue 201)
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-04-09 17:13:40 +01:00
EstherLerouzic
79f198d6fe reduce the number of test files
- use the same file to test different configuration: with and without Eqpt sheet defined,
   add the abcdfgh topo for disjunction tests in the same file
 - change the name of files to avoid mixing with examples/meshTopologyExampleV2.xxx

TODO: test if examples/ files pass for both path_requests_run and trasmission_main_example

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-04-09 17:13:40 +01:00
EstherLerouzic
315a12b9df Update parser test files in order wrt novel road_equalization branch
- update eqpt_config.json to power-mode : false to take into account gain target
    of the eqpt sheet
  - correct eqpt-config.json with new parameters
  - add gain target in meshTopolgyExampleV2Eqpt.xls
  - correct test_parser.py test function names
  - suppress Exceltestfile
  - change power_mode in test_parser to True in eqpt_config.json for all tests
    except for parser: changed in the function to False to enable
    Eqpt files gain target reading

Next: reduce the number of test files and include examples/ files in the tests

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-04-09 17:13:40 +01:00
EstherLerouzic
e14d145f2c Adding a test on autodesign self consistency
tests it the autodesign applied on an autodesigned file gives exactly the same result

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-04-09 17:13:29 +01:00
EstherLerouzic
f839da39f0 adding a test on autodesign verfication
- tests if networ autodesign does not change some reference files
- changed the meshTopologyExampleV2 files to account for newest features:
  empty columns in mode, power, nb channels, disjuntion examples ...
- added a tsp type with more modes for diversification of tests

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-04-09 17:13:29 +01:00
James
45ca7a63ed Merge pull request #222 from Orange-OpenSource/edfa_model_doc
Edfa model doc
2019-04-09 11:01:42 -04:00
James
5d187255ae Merge pull request #215 from Orange-OpenSource/roadm_equalization
Roadm equalization
2019-04-09 11:00:48 -04:00
Jean-Luc Auge
7adf6aed59 doc file description of all amplifier models!!
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-08 15:03:30 +02:00
Jean-Luc Auge
b22a7a0234 refactor amplifier build build_OA_json.py
update the advanced json model generator

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-05 16:06:08 +02:00
Jean-Luc Auge
8805723114 clean edfa_model repo
remove unused obsolete files

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-05 10:59:03 +02:00
Jean-Luc Auge
88db4358f5 import Juniper amp model example
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-04 17:25:53 +02:00
Jean-Luc Auge
a5d9685caf modify std_medium_gain_advanced_config.json
reflect interpolation code changes

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-04 17:17:47 +02:00
Jean-Luc Auge
94ff8e6beb Advanced amplifier model improvement
-add f_min & f_max frequency definition in amplifier json
-improve interpolation algorithm to support length differences between the spectrum information and the amplifier ripple and dgt frequency definition

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-04 17:14:25 +02:00
Jean-Luc Auge
f3400d9bc1 update eqpt_config.json
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:51:12 +02:00
Jean-Luc Auge
6a6591e41d Add a warning message when attributes are missing in eqpt_config.json
new code provides default values when attribute is missing in the
eqpt_config.json, which can cause unexpected beahaviour
=> provide a warning message when a missing attribute is set to a
default value.

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:51:12 +02:00
Jean-Luc Auge
a3a53f3b06 info message in gain mode
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:51:12 +02:00
Jean-Luc Auge
2f39abfdb8 power reduction warning message in auto-design
warning message when extended gain range or power requirements cannot be met
and power is reduced

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:09:45 +02:00
Jean-Luc Auge
92239d66fc pass tests
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:09:45 +02:00
Jean-Luc Auge
178813806f update eqpt_config and example files
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:09:45 +02:00
Jean-Luc Auge
265dbffc53 Roadm channels power equalization
set individual channel powers wrto the target reference power
tilt and ripple is eliminated after the Roadm

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
a67a08a4d0 Power reference tracking to roadm target power
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
6d15f55304 bug fix : Roadms & Spans vs Roadm & Span
discrepancy between topology and eqpt_config json descriptions

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
0dbcd1f265 manage operational vs calculated values
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
a0f6380f90 improve power mode and gain mode
power mode no longer reads operational_gain:
	now reads operational.dp_db
	or calculate optimum dp_db from next span loss
gain_mode:
	reads operational.gain_target
	or use gain_from_dp

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>

squash to dp

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
ab69cf5bf4 improve Edfa class element
manage default values

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>

squash to improve edfa element

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
3e55a5526a improve power saturation handling
fix issue with low power level (after very long spans) where signal gian
was depleted

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
b6216bb701 operational amplifier delta P target power support
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
fa3ea3aaa7 convert headers restriction management
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
7b56e3a6c3 bug fix in dual stage NF calculation
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
00ad542a11 auto-design: relax min_gain restriction
tolerate 3dB below min gain in amplifier selection
this is to allow selection of high power amps even if they are below min
gain

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
c3e00eea2c manage inter-stage padding for dual stage typedef
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
5c8dd911e3 reduce power when the extended_gain_range limit is reached
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
407fd62da5 improve auto_design algorithm for amplifier selection
remove gain_max limit and use gain+power limit selection instead

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
c92f7ca0d8 parametrization of amplifier extended_gain_range
used for amplifier selection in auto-design
but it is possible to manually use and set an amplifier beyond its
extended_gain_range

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
676901e113 remove hybrid type_def implementation
replaced by dual_stage type_def

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
c099b53a03 parametrization of the max_fiber_lineic_loss_for_raman
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
fd065e4e7c raman tag in eqpt description
manage raman restrictions

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
fd97527561 raman restrictions
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
7b9647a063 dual stage
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
bf943f1347 insert a raman hybrid example in eqpt_config.json
auto_design over meshTopologyToy.xls will show raman use
run:
python transmission_main.py meshTopologyToy.xls

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:08:34 +02:00
Jean-Luc Auge
dbff610d77 Improve auto-design amplifier selection algorithm
support min gain for raman auto-design:
raman amplification is only considered for a span if its gain > specified
min gain of the raman hybrid in eqpt_config

auto-design was checked succesfully when enabling a mix of 6 possible different
amplifiers: low gain, medium gain, hybrid ramans, high power and high
gain amplifiers. They were picked as expected.

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:05:45 +02:00
Jean-Luc Auge
46aae9486e update raman model to work in auto-design
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:05:45 +02:00
Jean-Luc Auge
70066de390 remove namedtuple class structure for network elements
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:05:45 +02:00
Jean-Luc Auge
106af9d444 hybrid raman/edfa model
quick & dirty 1st implementation:
-fixed raman nf and gain,
-no tilt, no ripple,
-no nonlinear contribution,
-no pump power reduction from egress con loss

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-04-03 14:05:45 +02:00
James Powell
f0be267f9f Merge branch 'develop' 2019-03-05 19:31:28 -05:00
James Powell
edbaec7265 small fixes 2019-03-05 19:11:17 -05:00
James Powell
f4537a538b interpolate fiber positions 2019-03-05 01:07:05 -05:00
James Powell
d6eb6f33d2 fixed ordering, display of SI details 2019-03-04 17:05:56 -05:00
James Powell
7a139c261a removed redundant files 2019-03-04 17:01:34 -05:00
James Powell
2e7aa213ed add units to OSNR 2019-03-04 17:00:41 -05:00
James Powell
d9344287e4 tweaks for OFC 2019-03-04 13:58:09 -05:00
James Powell
b9768a81e9 update README for v1.2 2019-03-04 12:50:35 -05:00
James Powell
3418c07512 changes for OFC demo 2019-03-04 12:35:12 -05:00
James Powell
5f8621c224 changes for OFC demo 2019-03-04 12:32:26 -05:00
James
c05f3555a3 Merge pull request #213 from Orange-OpenSource/features-description
more precise description on README for v1.1
2019-03-04 11:18:08 -05:00
EstherLerouzic
f21827395b update of the descriptioon json structure
- adding the different types of amplifiers

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-02-27 10:26:15 +00:00
EstherLerouzic
894c7bb17a update Excel_userguide.rst wrt convert-service_sheet issue #214
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-02-26 14:33:39 +00:00
Esther LE ROUZIC
2028cfae4d more precise description on README for v1.
Signed-off-by: Esther LE ROUZIC <esther.lerouzic@orange.com>
2019-02-04 09:59:58 +01:00
James Powell
2064f65c04 Merge branch 'develop' 2019-01-30 16:00:38 -05:00
James Powell
1de4a4eaa2 README table fix 2019-01-30 15:59:27 -05:00
James
06ff45d0c2 Merge pull request #208 from Telecominfraproject/develop
Merge Develop to Master for v1.1 Release
2019-01-30 15:52:50 -05:00
James
81474e252e Merge branch 'master' into develop 2019-01-30 15:47:50 -05:00
James
0069265905 Merge pull request #207 from Telecominfraproject/dutc-patch-1
Update README.rst
2019-01-30 15:45:36 -05:00
James
dec15f6797 Update README.rst 2019-01-30 15:40:38 -05:00
Gert Grammel
f6041cd844 Update Contributors
Adding Contributors to the list
2019-01-30 19:35:42 +00:00
James
ec20d3981b Merge pull request #200 from ojnas/fix-readme-coronet
Correct README
2019-01-28 10:39:38 -05:00
James
08a867ef5a Merge pull request #199 from Orange-OpenSource/sys_margins
Sys margins
2019-01-28 10:39:19 -05:00
Jean-Luc Auge
76c8296a5d remove osnr_nli update when adding add_drop_osnr or tx_osnr
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-23 14:52:02 +01:00
jonas
f65059dd7f correct README 2019-01-23 14:16:43 +01:00
Jean-Luc Auge
e23fef3f64 update and pass tests
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-23 10:31:42 +01:00
Jean-Luc Auge
30234f913c system margins implementation
-read sys_margins in equipment['SI']
-add sys_margins to all Transceivers OSNR for path feasibility check
(path_request_run only)

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-23 10:31:42 +01:00
Jean-Luc Auge
2dd017bddc code speed improvement (-30% computation time)
remove path propagation for each mode
path propogation is only done for each baud rate

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-23 10:31:42 +01:00
Jean-Luc Auge
b25a298087 NEW add_drop OSNR in Roadms model
add_drop_osnr contribution is added at the end of the transmission
propagation, at the same time as Tx_osnr

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-23 10:31:31 +01:00
Jean-Luc Auge
8635a7c182 Code fix: non cumulative osnr penalties
add Tx_osnr or add_drop_osnr on raw snr values to avoid cumulated adding

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>

bug fix: forgot to add add_drop_osnr with tx_osnr

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-23 10:29:30 +01:00
Jean-Luc Auge
46d25df241 add Tx_osnr at the end of the transmission
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-22 18:05:19 +01:00
Jean-Luc Auge
0338ccb08f restore SI basic parameters f_min f_max & spacing
remove unnecessary SI parameters in eqpt config.json:
-rx osnr
-bit rate
-min spacing
-cost
remove nb_channel parameter when calling SI

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-22 18:05:19 +01:00
James
8899a575b8 Merge pull request #144 from ojnas/fix-crash-source-dest
Change source and destination logic in transmission_main_example.py
2019-01-22 10:12:58 -05:00
James
b4407b1ff3 Merge branch 'develop' into fix-crash-source-dest 2019-01-22 10:12:18 -05:00
James
75f0aebe8f Merge pull request #193 from Orange-OpenSource/carrier_probe
Carrier probe
2019-01-22 10:09:47 -05:00
James
5a2dd53636 Merge pull request #195 from Orange-OpenSource/name_matching
Name matching dictionary
2019-01-22 10:09:16 -05:00
James
3555154c3e Merge pull request #194 from Orange-OpenSource/xls_parser_enhance
Xls parser enhance
2019-01-22 10:08:51 -05:00
James
6c92c282e7 Merge pull request #192 from Orange-OpenSource/roadms_openroadm
openroadm noise models and ROADM improvement
2019-01-22 10:07:21 -05:00
James
bd1847e5ba Merge branch 'develop' into roadms_openroadm 2019-01-22 10:04:45 -05:00
James
38727d6203 Merge pull request #188 from Orange-OpenSource/tsp_min_spacing
Tsp min spacing
2019-01-22 10:04:03 -05:00
James
7e6d557d01 Merge pull request #187 from Orange-OpenSource/fourth_correction
Fourth correction
2019-01-22 10:03:25 -05:00
James
5d3ce91839 Merge branch 'develop' into fourth_correction 2019-01-22 10:00:54 -05:00
James
1f34e3005e Merge pull request #186 from Orange-OpenSource/tx_osnr_bug_correction
bug fix due to tx_osnr add
2019-01-22 09:59:57 -05:00
James
8e27437086 Merge branch 'develop' into tx_osnr_bug_correction 2019-01-22 09:56:19 -05:00
James
b4cbe8029e Merge pull request #191 from davidBoertjes/davidBoertjes-node-coord-fix
fix for issue #190
2019-01-15 11:33:35 -05:00
James
92f3fd2063 Update node.py 2019-01-15 11:33:17 -05:00
James
1112b331ef Merge pull request #183 from Orange-OpenSource/second_corrections
Fixes concerning service sheet reading
2019-01-15 11:32:32 -05:00
James
ec34e84a3a Merge pull request #182 from Orange-OpenSource/corrections
Corrections
2019-01-15 11:31:48 -05:00
Jean-Luc Auge
bc9eee326a parametrization of the name matching dictionary
-names argument

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 17:17:58 +01:00
Jean-Luc Auge
c9106c3a6f enhance name matching dictionary
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 17:17:45 +01:00
Jean-Luc Auge
fa949f977a add name find closest match in xls parser convert.py
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>

output node name gestalt matching to a json

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 17:17:33 +01:00
Jean-Luc Auge
4c2d61bb9b update test files
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 17:14:17 +01:00
Jean-Luc Auge
ec7b14da8c convert.py xls to json parser class enhancement
improve default values handling
update xls examples with east/west headers

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 17:11:07 +01:00
Jean-Luc Auge
771af4991c carrier probe point for Edfa and Fiber elements
save and be able to retrieve the carrier information processed by Edfa
and Fiber elements:
@ input & output of the element
- channel power: ase, nli, signal and total

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 12:20:45 +01:00
Jean-Luc Auge
a08ce9ecb7 gain mode fix
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 12:16:16 +01:00
Jean-Luc Auge
4d84a4f528 replace egress/ingress by east/west denomination
xls parser

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 12:16:16 +01:00
Jean-Luc Auge
5d92baf35e Add link duplication check for the xls parser
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>

Check and remove duplicate links

a warning is issued when a duplicate link is dicovered
the execution is paused for the user ot see the warning
the duplicate link is removed and the execution resumed

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 12:16:16 +01:00
Jean-Luc Auge
ac5171e95e Improve Excel Node parser
-read headers in any order
-possible hierarchical implementation

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 12:16:16 +01:00
Jean-Luc Auge
697ac311fe Improve excel link sheet parser
-implement hierarchical headers
-can read headers in any order

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-14 12:16:16 +01:00
Jean-Luc Auge
c22d1173af update test eqpt_config
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-11 12:28:39 +01:00
Jean-Luc Auge
c0cc5fa9fd Improve ROADM loss calculation in power mode
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-11 12:28:39 +01:00
Jean-Luc Auge
4bd9a9cdda final openroadm models implementation
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2019-01-11 12:26:48 +01:00
EstherLerouzic
63f8139dbc Adding min_spacing parameter in transponder mode library
with this feature, spacing is checked with respect to eqpt library
instead of hard coded values in equipment +
mode selection is based on the minspacing instead of a coef or margin on baudrate value
some sanity check are introduced:
 - request spacing must be greater than min spacing
 - tsp baudrate must be smaller than min spacing

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-01-11 11:11:56 +00:00
EstherLerouzic
be731a5977 cosmetic changes
add colors on messages to highlight them
add messages explaining program steps
update readme and excel user guide with latest fetures

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-01-11 11:09:19 +00:00
EstherLerouzic
dd4ce4cea4 small fixes on stdout
- indicate when a mode is selected if the selected path does not pass
  + some reformatting of columns

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-01-11 11:09:19 +00:00
EstherLerouzic
2548a2eee8 small fix on test eqpt_config file
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-01-11 11:09:19 +00:00
EstherLerouzic
03948d6785 Fixes concerning service sheet reading
- indicate which request has incorrect tsp type or mode
- correct wrong reading from excel : if string values contain only a number
  xlrd interprets as a float eg 1 -> 1.0  . A function is used to correct this
  when filling the Request class

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-01-11 10:41:16 +00:00
EstherLerouzic
4c1c17eea6 adding some exception handling to help user debugging
exception handling add some info on which element (uid) is causing a problem
this can help the user to identify where he must correct the input files

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-01-11 10:41:16 +00:00
EstherLerouzic
b258d22d25 add a limit cutoff for path search
all_simple_path search leads to a very long time process in case of large network
a cutoff parameters has been added to avoid this. Value needs to be parametrized:
right now correspond to my experience

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2019-01-11 10:41:16 +00:00
EstherLerouzic
aef43e6bca Corrections of some bugs
- output file of -o option in path_requests_run.py was not correctly
  handled in case of path indirection eg ../../bar.foo
- fiber constraint is not correctly handled in case of several parrallel directions
  of same fiber name. remove if from the set of constraint till problem is not solved
- loose_list was not correctly indexed in request.py. assume the first element attribute (except
  of the transceiver) applies for the whole list. This will need further corrections
- handle the case when path.snr is none in the optimization of mode process
2019-01-11 10:41:11 +00:00
David Boertjes
3d7362743d Update node.py
Fix return value error in *coords* property which was intended to build a tuple from two values for latitude and longitude.
2019-01-10 13:15:59 -05:00
EstherLerouzic
96f3d5a805 Adding min_spacing parameter in transponder mode library
with this feature, spacing is checked with respect to eqpt library
instead of hard coded values in equipment +
mode selection is based on the minspacing instead of a coef or margin on baudrate value
some sanity check are introduced:
 - request spacing must be greater than min spacing
 - tsp baudrate must be smaller than min spacing

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-12-21 16:57:08 +00:00
EstherLerouzic
2df500e027 cosmetic changes
add colors on messages to highlight them
add messages explaining program steps
update readme and excel user guide with latest fetures

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-12-19 18:24:22 +00:00
EstherLerouzic
346f24022a small fixes on stdout
- indicate when a mode is selected if the selected path does not pass
  + some reformatting of columns

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-12-19 18:21:51 +00:00
EstherLerouzic
cb45c7ef16 new test to check all combination of service specifications
add combination of [mode, pow, nb channel] filled or empty
leading to feasible path or not, and check the propagate and propagate_and_optimize_mode
functions work as expected on a reference toy example

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-12-19 18:17:29 +00:00
EstherLerouzic
9cfb57dc4b bug fix due to tx_osnr add
tx_osnr was impacting automatic mode selection feature : this commit solves the error
+ add a novel version of the excel example with an empty mode cases

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-12-19 16:32:54 +00:00
EstherLerouzic
020d852758 adding some exception handling to help user debugging
exception handling add some info on which element (uid) is causing a problem
this can help the user to identify where he must correct the input files

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-12-19 10:49:31 +00:00
James
8d97fcd735 Merge pull request #177 from ojnas/add-tx-osnr
Add Tx OSNR
2018-12-18 11:57:35 -05:00
James
097fe3114e Merge pull request #156 from ojnas/power-range
improve power range handling - revised version
2018-12-18 11:56:47 -05:00
James
5e0fd265ff Merge pull request #180 from ojnas/fix-osnr-calculation
Fix OSNR calculation when ASE or NLI is zero
2018-12-18 11:55:43 -05:00
EstherLerouzic
6af137a085 Fixes concerning service sheet reading
- indicate which request has incorrect tsp type or mode
- correct wrong reading from excel : if string values contain only a number
  xlrd interprets as a float eg 1 -> 1.0  . A function is used to correct this
  when filling the Request class

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-12-17 09:27:05 +00:00
EstherLerouzic
3cdc8511a8 add a limit cutoff for path search
all_simple_path search leads to a very long time process in case of large network
a cutoff parameters has been added to avoid this. Value needs to be parametrized:
right now correspond to my experience

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-12-13 11:37:26 +00:00
EstherLerouzic
2d515eea4c Corrections of some bugs
- output file of -o option in path_requests_run.py was not correctly
  handled in case of path indirection eg ../../bar.foo
- fiber constraint is not correctly handled in case of several parrallel directions
  of same fiber name. remove if from the set of constraint till problem is not solved
- loose_list was not correctly indexed in request.py. assume the first element attribute (except
  of the transceiver) applies for the whole list. This will need further corrections
- handle the case when path.snr is none in the optimization of mode process
2018-12-13 11:28:42 +00:00
Jonas Mårtensson
61289119cb fix OSNR calculation when ASE or NLI is zero 2018-12-13 11:07:16 +01:00
Jonas Mårtensson
b6bc995e40 fix OSNR test 2018-12-11 21:29:17 +01:00
Jonas Mårtensson
b0bb41bac6 fix eqpt_config test file 2018-12-11 20:48:53 +01:00
Jonas Mårtensson
0927a92652 fix amplifier test 2018-12-11 17:25:26 +01:00
Jonas Mårtensson
f51061d650 Add support for Tx OSNR. 2018-12-11 16:59:05 +01:00
James
74314f00ca Merge pull request #175 from MiquelGA/develop
Few minor details and typos in README
2018-12-11 10:07:09 -05:00
miquelgarr
81c5ef4a23 Adding "transmission_main_example.py -h" 2018-11-30 12:16:22 +01:00
miquelgarr
72e329b08e Now applying corrections to the README 2018-11-30 10:28:41 +01:00
miquelgarr
50f884663f Minor typo changes in README 2018-11-30 10:25:17 +01:00
miquelgarr
0d81eb4b29 Minor typos solved in README 2018-11-30 09:59:51 +01:00
James
978a9407fa Merge pull request #171 from szhu3210/patch-1
Fix a typo of log message in transmission_main_example.py
2018-11-27 12:22:01 -05:00
James
2c3b74cdc1 Merge pull request #168 from Orange-OpenSource/service_aggregation_clean_rebased
Service aggregation clean rebased
2018-11-27 11:31:41 -05:00
Shengxiang Zhu (Troy)
448e0f54be Update transmission_main_example.py
Seems a typo.
2018-11-23 17:32:58 -07:00
EstherLerouzic
17f638e991 New mesh example with correct inputs + small fix
small fix: correct wrong numbering of request in json and csv
           output when -o is used ; correct example files

signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-21 09:56:00 +00:00
EstherLerouzic
b78d3d8eda small fixes
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-21 09:20:25 +00:00
EstherLerouzic
02a7e467e2 Major correction on mode optimization behaviour + small fixes
- this version handles special cases: if no baudrate satisfies the spacing
  , if no mode satisfies the OSNR threshold from transponders
- template of service corrected wih the novel path_bandwidth
- some ideas TODO added for testing

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-21 09:20:25 +00:00
EstherLerouzic
15304890f5 Corrections related to tests and cost feature
- adding cost in every config files including tests files,
- correcting wrong usage reactions (if value is not correct)
- correction of orthograph

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-21 09:20:19 +00:00
EstherLerouzic
9ea96e431c Test disjunction file corrected to include novel path_bandwidth parameter
Correction of data file for test_parser.py + adding a test on path non looping

- service json files now include path_nadwidth value
- previous constrained routing did not ensure loop free path. This has been
  corrected in A test has been added to avoid loosing this feature in future
  versions.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-21 09:16:23 +00:00
EstherLerouzic
7b3bfea614 correction on aggregation to support disjunction feature
- correction of json to csv function
- small correction on disjunction format to avoid double (double crash the program)
- adding time stamp to measure perf
- demands with all the same disjunction can be aggregated
- a new set of disjunction list is created to avoid inconsistancy
  with the aggregated request-ids

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-21 09:16:17 +00:00
Jean-Luc Auge
d68637c2c8 clean eqpt_config.json, add low noise openroadm amp
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2018-11-21 09:14:17 +00:00
Jean-Luc Auge
f009306030 small fix
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2018-11-21 09:12:58 +00:00
Jean-Luc Auge
ca97cba18b display channel count information when running transmission_main
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2018-11-21 09:12:58 +00:00
Jean-Luc Auge
a46c8c5398 OpenRoadm amplifier model!
nf = f(Pin)

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
2018-11-21 09:12:58 +00:00
EstherLerouzic
88c2e2bd70 adding the example file used for disjunction testing
adding the counting feature on standard output
adding tsp nb in csv + small bug fixes
small fixes to improve stdout and csv printing
small bug fixes on service aggregation and automatic correction of names

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-21 09:12:52 +00:00
EstherLerouzic
1bbcee8715 First version of mode optimization
if mode is not given, the program automatically choses the first mode of the tsp
that has the highest bitrate and baudrate
- propagation for each loop on baudrate, no proppag in the loop on the mode
  (same propoagation applies for identical baudrate)
- starts with the highest baudrate
- Make power and nb-channel optional fields
  - power uses defaulf power in SI
  - nb channel computed based on min max frequency and spacing

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-21 09:06:38 +00:00
EstherLerouzic
39a8fa3335 Adding aggregation feature
- add bitrate in the json service file and enable to support both with and without bitrate
- change mode to optional
- add an aggregation function that groups identical demands (except for id and bitrate)
  TODO : check which mode can satisfy the request or how many transponders  -> going towards
  dimensionning
- Correcting some bug on loose parameter: hop-type attribute was forced to be always 'loose'
  -> now reads the excel entry
- service sheets checks on header if field names are corrects, but any order is
  now feasible.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-21 09:06:32 +00:00
James
fe067e5367 Merge pull request #167 from ojnas/verbosity
handling of --verbose
2018-11-20 09:57:26 -05:00
Jonas Mårtensson
5efbd17829 add help text 2018-11-20 15:27:54 +01:00
Jonas Mårtensson
fa3e54a747 change handling of verbosity argument 2018-11-20 14:57:28 +01:00
James
603beccb01 Merge pull request #151 from ojnas/gain-mode-fix
gain-mode fix
2018-11-19 18:18:35 -05:00
James
4b20afd599 Merge pull request #165 from ojnas/fix-broken-links
fix broken links in readme
2018-11-19 18:16:46 -05:00
Jonas Mårtensson
adbe283c83 fix broken links in readme 2018-11-19 13:03:32 +01:00
James
1908d7e29a Merge pull request #161 from Orange-OpenSource/correcting_example
correcting example file
2018-11-16 11:08:38 -05:00
James
7c6e16cfbc Merge pull request #164 from ojnas/fix-encoding
fix utf-8 encoding
2018-11-16 11:07:27 -05:00
Jonas Mårtensson
1480d23088 fix utf-8 encoding 2018-11-16 13:38:07 +01:00
EstherLerouzic
4be3522209 change for meshTopologyToy.xls
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-16 11:14:37 +00:00
EstherLerouzic
5381e0300f correcting example file
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-11-16 11:04:52 +00:00
James
cde822ebf8 Merge pull request #157 from ojnas/missing-imports
add a couple of missing imports
2018-11-15 12:39:31 -05:00
Jonas Mårtensson
72d3525da1 add a couple of missing imports 2018-11-14 15:24:25 +01:00
Jonas Mårtensson
dc867fa051 improve power range handling 2018-11-14 09:08:14 +01:00
James
eaf3fcade8 Merge pull request #153 from dutc/develop
fix changed spelling & misspellings in meshTopolgyExampleV2_Services.json for "synchronize/synchronization"
2018-11-13 11:49:01 -05:00
James Powell
3df270e4ac fix changed spelling & misspellings in meshTopolgyExampleV2_Services.json for "synchronize/synchronization" 2018-11-13 10:24:15 -05:00
Jonas Mårtensson
7937392dfc fix bug in gain mode 2018-11-13 13:33:21 +01:00
James
9c1c0f8d1f Merge pull request #137 from Orange-OpenSource/path_disjunction
Path disjunction
2018-11-12 10:11:08 -05:00
James
ad2ab0d164 Merge pull request #143 from ojnas/coronet-all-nodes-roadm
Explicitly set all nodes in CORONET example to ROADM
2018-11-12 10:10:45 -05:00
James
c4bed94eb0 Merge branch 'develop' into coronet-all-nodes-roadm 2018-11-12 10:10:23 -05:00
James
edc8eb55de Merge pull request #145 from ojnas/split-fiber
Some modifications in split_fiber function
2018-11-12 10:09:15 -05:00
Jonas Mårtensson
f4f9868381 Change span numbering in split_fiber. 2018-11-05 17:43:14 +01:00
Jonas Mårtensson
f103bebe05 Some modifications in split_fiber function. 2018-11-05 17:13:03 +01:00
Jonas Mårtensson
c168af46bc Update transmission_main_example.py 2018-10-30 17:37:50 +01:00
EstherLerouzic
7d82248903 Small fix
obsolete text not removed in the message made an error: I removed them

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-30 16:36:44 +00:00
James Powell
e6cb269754 Merge branch 'master' into develop 2018-10-30 12:05:44 -04:00
James
ac8a96398a Update issue templates 2018-10-30 12:03:36 -04:00
EstherLerouzic
86c79c7c60 Merge branch 'develop' into path_disjunction solving conflict
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-30 16:01:06 +00:00
James Powell
0c47b3f3ea small fix in case spacing_list is not sorted 2018-10-30 12:00:47 -04:00
James
b2b500c5dc Merge pull request #141 from ojnas/automatic-spacing
Simplify automatic spacing
2018-10-30 11:59:20 -04:00
ojnas
efc8468268 Set type of all nodes to ROADM in CORONET Glabal. 2018-10-30 09:48:59 +01:00
ojnas
205baebd48 Merge remote-tracking branch 'upstream/develop' into develop 2018-10-30 09:37:27 +01:00
James
af9ba2750d Update examples/path_requests_run.py
Co-Authored-By: EstherLerouzic <EstherLerouzic@users.noreply.github.com>
2018-10-30 09:27:52 +01:00
James
e04afdbe4c Update examples/path_requests_run.py
Co-Authored-By: EstherLerouzic <EstherLerouzic@users.noreply.github.com>
2018-10-30 09:27:12 +01:00
James
e94fd9590e Merge pull request #142 from dutc/develop
Fix encoding issues
2018-10-29 20:02:28 -04:00
James Powell
4f4f05abdf allow JSON to encode UTF-8 2018-10-29 11:17:13 -04:00
James Powell
bcf93e1d9f enforce utf-8 encoding for reading/writing JSON 2018-10-29 11:14:31 -04:00
ojnas
48198bdd89 Simplify automatic spacing. 2018-10-28 08:11:22 +01:00
EstherLerouzic
fbb4f3e5dd small fixes to comply with test files
- re-changing to to \u2192 in convert.py to comply with test files
- changing route constraint in data file : the constraint had a too ambiguous
  naming in the service file
- test on None was incorrect in convert_sheet

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-26 10:43:27 +01:00
James
cefd1cf030 Merge pull request #136 from ojnas/main-source-dest
source and destination handling i transmission main example
2018-10-25 10:47:00 -04:00
James
f8fa544e31 Merge pull request #135 from ojnas/add-fiber-types
Update eqpt_config.json
2018-10-25 10:46:02 -04:00
EstherLerouzic
27885a4cbc Correction of route constraint to support any type of node
correct loose or strict constraint in the new function correct_route_list
correct test to find the name with existing uids
remove the \u2192 char that was not well supported with excel reading

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-25 15:42:09 +01:00
EstherLerouzic
185a62958f adding safe check on baudrate and spacing
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-25 15:42:02 +01:00
EstherLerouzic
1ba748f2a4 Changing optical power and nb channels as optional input for requests
- normal way is usually to apply the design optical power for all channels.
  This change uses the default power (same power as used for design) but enables to
  force an arbitrary power if needed. TODO : introduce spectral power density
  to apply power depending on baudrate.
- definition of min max frequency and spacing define the nb of channels:
  uses min max frequencies and spacing to determine nb-channels. It is possible
  to force a different spacing for the request. TODO: check that the value is
  consistant with baudrate and min max values.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-25 15:36:53 +01:00
EstherLerouzic
90a75a9b3d Removing the duplicate convert_service_sheet.py in examples directory
service_sheet.py contains exactly the same function: I suggest to remove this file
to clean the examples directory

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-25 15:36:36 +01:00
ojnas
215295efb1 changed source and destination handling 2018-10-25 15:25:28 +02:00
Jonas Mårtensson
2413bd9e0d Update eqpt_config.json
Add fiber types.
2018-10-25 11:31:39 +02:00
James
356ae650fd Update issue templates 2018-10-23 17:09:56 -04:00
James
2444c24545 Update issue templates 2018-10-23 17:09:10 -04:00
James
7727708a3a Merge pull request #131 from ojnas/equipment-refactoring
Equipment refactoring
2018-10-23 14:15:39 -04:00
Jonas Mårtensson
0bfacd84f4 Update equipment.py 2018-10-23 17:16:51 +02:00
Jonas Mårtensson
b271c1ca3c Update equipment.py 2018-10-23 17:13:08 +02:00
EstherLerouzic
75660febc1 Create test_disjunction
- create some test to check that produced paths are really disjoint
- add some TODOs on optical power for requests computation

First check of disjunction feature added to tests

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-23 11:53:57 +01:00
EstherLerouzic
13aaa174e1 Adding this toy example network for disjunction testing
1     1
     a----b-------c
     |    |       |
     |1   |0.5    |1
     |    |       |
     e----f---h---g
       1   0.5 0.5

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-23 09:30:20 +01:00
EstherLerouzic
d112c728fc Moving the disjunction function into core functions
and minor corrections

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-23 09:25:07 +01:00
EstherLerouzic
826af4a9fd Minor corrections of tests due to orthograph change
- "synchronisation" replaced with synchronization
- "synchonzation" replaced with synchronization

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-22 16:41:56 +01:00
EstherLerouzic
c9693d355f some cleaning and renaming
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-22 16:26:58 +01:00
EstherLerouzic
9f37cb8ce6 Corrections, cleanup and debugging
Added (partly):
- relax one constraint when no more path are available

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-22 16:25:52 +01:00
EstherLerouzic
d99e8ca565 routing constraint added on top of disjunction feature : step 4
select disjoint routes that satisfy route constraint

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-22 16:25:52 +01:00
EstherLerouzic
44312125ab Disjunction feature step 3
- select the first path that is satisfying disjunction
not finished:
- constraints on nodes not taken into account
- modification on the build network not added
- not clean, need some refactoring

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-22 16:25:49 +01:00
EstherLerouzic
7558721642 Disjunction feature step 2
- selection of disjoint path set for each synchronization vector

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-22 16:23:11 +01:00
EstherLerouzic
fb49f7fb5d Implementing disjunction in path_requests_run.py
- Implementing a conflict table for path disjoint choices
 - adding disjunction in the parser and json (read and write)

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
2018-10-22 16:23:00 +01:00
James
ee7f2c2f47 Merge pull request #129 from ojnas/minor-readme-fixes
Update README.rst
2018-10-22 10:19:04 -04:00
Jonas Mårtensson
833fe006af Update README.rst 2018-10-22 14:19:52 +02:00
104 changed files with 231959 additions and 6950 deletions

1
.bandit Normal file
View File

@@ -0,0 +1 @@
skips: ['B101']

1
.codecov.yml Normal file
View File

@@ -0,0 +1 @@
comment: off

3
.docker-entry.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
cp -nr /oopt-gnpy/examples /shared
exec "$@"

52
.docker-travis.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/bin/bash
set -e
IMAGE_NAME=telecominfraproject/oopt-gnpy
IMAGE_TAG=$(git describe --tags)
if [[ "${TRAVIS_BRANCH}" == "experimental/2019-summit" ]]; then
IMAGE_NAME=telecominfraproject/oopt-gnpy-experimental
fi
ALREADY_FOUND=0
docker pull ${IMAGE_NAME}:${IMAGE_TAG} && ALREADY_FOUND=1
if [[ $ALREADY_FOUND == 0 ]]; then
docker build . -t ${IMAGE_NAME}
docker tag ${IMAGE_NAME} ${IMAGE_NAME}:${IMAGE_TAG}
else
echo "Image ${IMAGE_NAME}:${IMAGE_TAG} already available, will just update the other tags"
fi
docker images
do_docker_login() {
echo "${DOCKER_PASSWORD}" | docker login -u "${DOCKER_USERNAME}" --password-stdin
}
if [[ "${TRAVIS_PULL_REQUEST}" == "false" ]]; then
if [[ "${TRAVIS_BRANCH}" == "develop" || "${TRAVIS_BRANCH}" == "docker" ]]; then
echo "Publishing latest"
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest
do_docker_login
if [[ $ALREADY_FOUND == 0 ]]; then
docker push ${IMAGE_NAME}:${IMAGE_TAG}
fi
docker push ${IMAGE_NAME}:latest
elif [[ "${TRAVIS_BRANCH}" == "master" ]]; then
echo "Publishing stable"
docker tag ${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:stable
do_docker_login
if [[ $ALREADY_FOUND == 0 ]]; then
docker push ${IMAGE_NAME}:${IMAGE_TAG}
fi
docker push ${IMAGE_NAME}:stable
elif [[ "${TRAVIS_BRANCH}" == "experimental/2019-summit" ]]; then
echo "Publishing ad-hoc image for the TIP Summit demo"
do_docker_login
if [[ $ALREADY_FOUND == 0 ]]; then
docker push ${IMAGE_NAME}:${IMAGE_TAG}
fi
fi
fi

29
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Environment:**
- OS: [e.g. Windows]
- Python Version [e.g, 3.7]
- Anaconda Version [e.g. 3.7]
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,17 @@
---
name: Feature request
about: Suggest an idea for this project
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -1,10 +1,27 @@
dist: xenial
sudo: false
language: python
services: docker
python:
- "3.6"
# command to install dependencies
install:
- python setup.py install
# command to run tests
before_script:
- "3.7"
install: skip
script:
- pytest
- python setup.py install
- pip install pytest-cov rstcheck
- pytest --cov-report=xml --cov=gnpy
- rstcheck --ignore-roles cite --ignore-directives automodule --recursive --ignore-messages '(Duplicate explicit target name.*)' .
- ./examples/transmission_main_example.py
- ./examples/path_requests_run.py
- ./examples/transmission_main_example.py examples/raman_edfa_example_network.json --sim examples/sim_params.json --show-channels
- sphinx-build docs/ x-throwaway-location
after_success:
- bash <(curl -s https://codecov.io/bash)
jobs:
include:
- stage: test
name: Docker image
script:
- git fetch --unshallow
- ./.docker-travis.sh
- docker images

8
.zuul.yaml Normal file
View File

@@ -0,0 +1,8 @@
---
- project:
check:
jobs:
- noop
gate:
jobs:
- noop

View File

@@ -6,16 +6,24 @@ To learn how to contribute, please see CONTRIBUTING.md
(*in alphabetical order*)
- Alessio Ferrari (Politecnico di Torino) <alessio.ferrari@polito.it>
- Anders Lindgren (Telia Company) <Anders.X.Lindgren@teliacompany.com>
- Andrea d'Amico (Politecnico di Torino) <andrea.damico@polito.it>
- Brian Taylor (Facebook) <briantaylor@fb.com>
- David Boertjes (Ciena) <dboertje@ciena.com>
- Diego Landa (Facebook) <dlanda@fb.com>
- Esther Le Rouzic (Orange) <esther.lerouzic@orange.com>
- Gabriele Galimberti (Cisco) <ggalimbe@cisco.com>
- Gert Grammel (Juniper Networks) <ggrammel@juniper.net>
- Gilad Goldfarb (Facebook) <giladg@fb.com>
- James Powell (Telecom Infra Project) <james.powell@telecominfraproject.com>
- Jan Kundrát (Telecom Infra Project) <jan.kundrat@telecominfraproject.com>
- Jeanluc Augé (Orange) <jeanluc.auge@orange.com>
- Jonas Mårtensson (RISE) <jonas.martensson@ri.se>
- Mattia Cantono (Politecnico di Torino) <mattia.cantono@polito.it>
- Miguel Garrich (University Catalunya) <miquel.garrich@upct.es>
- Raj Nagarajan (Lumentum) <raj.nagarajan@lumentum.com>
- Roberts Miculens (Lattelecom) <roberts.miculens@lattelecom.lv>
- Shengxiang Zhu (University of Arizona) <szhu@email.arizona.edu>
- Stefan Melin (Telia Company) <Stefan.Melin@teliacompany.com>
- Vittorio Curri (Politecnico di Torino) <vittorio.curri@polito.it>
- Xufeng Liu (Jabil) <xufeng_liu@jabil.com>

8
Dockerfile Normal file
View File

@@ -0,0 +1,8 @@
FROM python:3.7-slim
COPY . /oopt-gnpy
WORKDIR /oopt-gnpy
RUN python setup.py install
WORKDIR /shared
ENTRYPOINT ["/oopt-gnpy/.docker-entry.sh"]
CMD ["python", "examples/path_requests_run.py", "examples/2019-demo-topology.json", "examples/2019-demo-services.json", "examples/2019-demo-equipment.json", "--rest"]
EXPOSE 5000

View File

@@ -19,8 +19,8 @@ In order to work the excel file MUST contain at least 2 sheets:
Nodes sheet
-----------
Nodes sheet contains seven columns.
Each line represents a 'node' (ROADM site or an in line amplifier site ILA)::
Nodes sheet contains nine columns.
Each line represents a 'node' (ROADM site or an in line amplifier site ILA or a Fused)::
City (Mandatory) ; State ; Country ; Region ; Latitude ; Longitude ; Type
@@ -38,6 +38,9 @@ Each line represents a 'node' (ROADM site or an in line amplifier site ILA)::
- *Longitude*, *Latitude* are not mandatory. If filled they should contain numbers.
- **Booster_restriction** and **Preamp_restriction** are not mandatory.
If used, they must contain one or several amplifier type_variety names separated by ' | '. This information is used to restrict types of amplifiers used in a ROADM node during autodesign. If a ROADM booster or preamp is already specified in the Eqpt sheet , the field is ignored. The field is also ignored if the node is not a ROADM node.
**There MUST NOT be empty line(s) between two nodes lines**
@@ -166,6 +169,7 @@ This generates a text file meshTopologyExampleV2_eqt_sheet.txt whose content ca
- **amp type** is not mandatory.
If filled it must contain types listed in `eqpt_config.json <examples/eqpt_config.json>`_ in "Edfa" list "type_variety".
If not filled it takes "std_medium_gain" as default value.
If filled with fused, a fused element with 0.0 dB loss will be placed instead of an amplifier. This might be used to avoid booster amplifier on a ROADM direction.
- **amp_gain** is not mandatory. It is the value to be set on the amplifier (in dB).
If not filled, it will be determined with design rules in the convert.py file.
@@ -189,47 +193,30 @@ Service sheet must contain 11 columns::
route id ; Source ; Destination ; TRX type ; Mode ; System: spacing ; System: input power (dBm) ; System: nb of channels ; routing: disjoint from ; routing: path ; routing: is loose?
- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash)
- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash or coma)
- **Source** is mandatory. It is the name of the source node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries)
- **Destination** is mandatory. It is the name of the destination node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries)
- **TRX type and mode** are mandatory. They are the variety type and selected mode of the transceiver to be used for the propagation simulation. These modes MUST be defined in the equipment library. The format of the mode is used as the name of the mode. (TODO: maybe add another mode id on Transceiver library ?). In particular the mode selection defines the channel baudrate to be used for the propagation simulation.
- **TRX type** is mandatory. They are the variety type and selected mode of the transceiver to be used for the propagation simulation. These modes MUST be defined in the equipment library. The format of the mode is used as the name of the mode. (TODO: maybe add another mode id on Transceiver library ?). In particular the mode selection defines the channel baudrate to be used for the propagation simulation.
- **System: spacing ; System: input power (dBm) ; System: nb of channels** are mandatory input defining the system parameters for the propagation simulation.
- **mode** is optional. If not specified, the program will search for the mode of the defined transponder with the highest baudrate fitting within the spacing value.
- **System: spacing** is mandatory. Spacing is the channel spacing defined in GHz difined for the feasibility propagation simulation, assuming system full load.
- **System: input power (dBm) ; System: nb of channels** are optional input defining the system parameters for the propagation simulation.
- spacing is the channel spacing defined in GHz
- input power is the channel optical input power in dBm
- nb of channels is the number of channels to be used for the simulation.
- **routing: disjoint from ; routing: path ; routing: is loose?** are optional.
- disjoint from: (work not started) identifies the requests from which this request must be disjoint. It is not used yet
- path: is the set of ROADM nodes that must be used by the path. It must contain the list of ROADM names that the path must cross. TODO : only ROADM nodes are accepted in this release. Relax this with any type of nodes.
- is loose? (in progress) 'no' value means that the list of nodes should be strictly followed, while any other value means that the constraint may be relaxed if the node is not reachable.
- disjoint from: identifies the requests from which this request must be disjoint. If filled it must contain request ids separated by ' | '
- path: is the set of ROADM nodes that must be used by the path. It must contain the list of ROADM names that the path must cross. TODO : only ROADM nodes are accepted in this release. Relax this with any type of nodes. If filled it must contain ROADM ids separated by ' | '. Exact names are required.
- is loose? 'no' value means that the list of nodes should be strictly followed, while any other value means that the constraint may be relaxed if the node is not reachable.
# to be completed #
convert_service_sheet.py
------------------------
`convert_service_sheet.py <examples/convert_service_sheet.py>`_ converts the service sheet to a json file following the Yang model for requesting Path Computation defined in `draft-ietf-teas-yang-path-computation-01.txt <https://www.ietf.org/id/draft-ietf-teas-yang-path-computation-01.pdf>`_. TODO: verify that this implementation is correct + give feedback to ietf on what is missing for our specific application.
For PSE use, additional fields with trx type and mode have been added to the te-bandwidth field.
**Usage**: convert_service_sheet.py [-h] [-v] [-o OUTPUT] [workbook_name.xls]
.. code-block:: shell
$ cd examples
$ python convert_service_sheet.py meshTopologyExampleV2.xls -o service_file.json
-o output_file.json is an optional parameter:
- if not used, the program output the json data on standard output and on a json file with name 'workbook_name_services.json'.
A template for the json file can be found here: `service_template.json <service_template.json>`_
- ** path bandwidth** is optional. It is the amount of capacity required between source and destination in Gbit/s. Default value is 0.0 Gbit/s.
path_requests_run.py
------------------------
@@ -244,6 +231,11 @@ path_requests_run.py
A function that computes performances for a list of services provided in the service file (accepts json or excel format.
if the service <file.xls> is in xls format, path_requests_run.py converts it to a json file <file_services.json> following the Yang model for requesting Path Computation defined in `draft-ietf-teas-yang-path-computation-01.txt <https://www.ietf.org/id/draft-ietf-teas-yang-path-computation-01.pdf>`_. For PSE use, additional fields with trx type and mode have been added to the te-bandwidth field.
A template for the json file can be found here: `service_template.json <service_template.json>`_
If no output file is given, the computation is shown on standard output for demo.
If a file is specified with the optional -o argument, the result of the computation is converted into a json format following the Yang model for requesting Path Computation defined in `draft-ietf-teas-yang-path-computation-01.txt <https://www.ietf.org/id/draft-ietf-teas-yang-path-computation-01.pdf>`_. TODO: verify that this implementation is correct + give feedback to ietf on what is missing for our specific application.

View File

@@ -1,15 +1,21 @@
.. image:: docs/images/GNPy-banner.png
:width: 100%
:align: left
:alt: GNPy with an OLS system
====================================================================
`gnpy`: mesh optical network route planning and optimization library
====================================================================
|docs| |build|
|docs| |build| |doi|
**`gnpy` is an open-source, community-developed library for building route
planning and optimization tools in real-world mesh optical networks.**
`gnpy <http://github.com/telecominfraproject/oopt-gnpy>`__ is:
--------------------------------------------------------------
- a sponsored project of the `OOPT/PSE <https://telecominfraproject.com/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_.
- a sponsored project of the `OOPT/PSE <https://telecominfraproject.com/open-optical-packet-transport/>`_ working group of the `Telecom Infra Project <http://telecominfraproject.com>`_
- fully community-driven, fully open source library
- driven by a consortium of operators, vendors, and academic researchers
- intended for rapid development of production-grade route planning tools
@@ -18,27 +24,50 @@ planning and optimization tools in real-world mesh optical networks.**
Documentation: https://gnpy.readthedocs.io
Get In Touch
~~~~~~~~~~~~
There are `weekly calls <https://telecominfraproject.workplace.com/events/702894886867547/>`__ about our progress.
Newcomers, users and telecom operators are especially welcome there.
We encourage all interested people outside the TIP to `join the project <https://telecominfraproject.com/apply-for-membership/>`__.
Branches and Tagged Releases
----------------------------
- the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, validated code. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group. For more information about the validation process, see: https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality
- all releases are `available via GitHub <https://github.com/Telecominfraproject/oopt-gnpy/releases>`_
- the `master <https://github.com/Telecominfraproject/oopt-gnpy/tree/master>`_ branch contains stable, `validated code <https://github.com/Telecominfraproject/oopt-gnpy/wiki/Testing-for-Quality>`_. It is updated from develop on a release schedule determined by the OOPT-PSE Working Group.
- the `develop <https://github.com/Telecominfraproject/oopt-gnpy/tree/develop>`_ branch contains the latest code under active development, which may not be fully validated and tested.
- the `phase-1 <https://github.com/Telecominfraproject/oopt-gnpy/tree/phase-1>`_ branch contains code for Phase I of the OOPT-PSE efforts and is kept only for reference. This branch is unmaintained.
A brief outline of major (tagged) `gnpy` releases:
+---------------+-------------+-----------------------------------------------+
| release date | version tag | notes |
+===============+=============+===============================================+
| Oct 16, 2018 | v1.0 | - first "production"-ready release |
| | | - open network element model (EDFA, GN-model) |
| | | - auto-design functionality |
| | | - path request functionality |
+---------------+-------------+-----------------------------------------------+
How to Install
--------------
Using prebuilt Docker images
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Our `Docker images <https://hub.docker.com/r/telecominfraproject/oopt-gnpy>`_ contain everything needed to run all examples from this guide.
Docker transparently fetches the image over the network upon first use.
On Linux and Mac, run:
.. code-block:: shell-session
$ docker run -it --rm --volume $(pwd):/shared telecominfraproject/oopt-gnpy
root@bea050f186f7:/shared/examples#
On Windows, launch from Powershell as:
.. code-block:: powershell
PS C:\> docker run -it --rm --volume ${PWD}:/shared telecominfraproject/oopt-gnpy
root@89784e577d44:/shared/examples#
In both cases, a directory named ``examples/`` will appear in your current working directory.
GNPy automaticallly populates it with example files from the current release.
Remove that directory if you want to start from scratch.
Using Python on your computer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Note**: `gnpy` supports Python 3 only. Python 2 is not supported.
`gnpy` requires Python ≥3.6
@@ -48,10 +77,9 @@ How to Install
It is recommended that you use a "virtual environment" when installing `gnpy`.
Do not install `gnpy` on your system Python.
We recommend the use of the Anaconda Python distribution
(https://www.anaconda.com/download) which comes with many scientific computing
We recommend the use of the `Anaconda Python distribution <https://www.anaconda.com/download>`_ which comes with many scientific computing
dependencies pre-installed. Anaconda creates a base "virtual environment" for
you automatically. You can also create and manage your conda "virtual
you automatically. You can also create and manage your ``conda`` "virtual
environments" yourself (see:
https://conda.io/docs/user-guide/tasks/manage-environments.html)
@@ -97,14 +125,13 @@ of the `gnpy` repo and install it with:
$ python setup.py install # install
To test that `gnpy` was successfully installed, you can run this command. If it
executes without a `ModuleNotFoundError`, you have successfully installed
executes without a ``ModuleNotFoundError``, you have successfully installed
`gnpy`.
.. code-block:: shell
$ python -c 'import gnpy' # attempt to import gnpy
$ cd oopt-gnpy
$ pytest # run tests
Instructions for First Use
@@ -116,32 +143,29 @@ It ships with a number of example programs. Release versions will ship with
fully-functional programs.
**Note**: *If you are a network operator or involved in route planning and
optimization for your organization, please contact project maintainer James
Powell <james.powell@telecominfraproject>. gnpy is looking for users with
optimization for your organization, please contact project maintainer Jan
Kundrát <jan.kundrat@telecominfraproject.com>. gnpy is looking for users with
specific, delineated use cases to drive requirements for future
development.*
**To get started, run the main transmission example:**
This example demonstrates how GNPy can be used to check the expected SNR at the end of the line by varying the channel input power:
**Note**: *Examples should be run from the examples/ folder.*
.. code-block:: shell
$ pwd
/path/to/oopt-gnpy
$ cd examples
$ python transmission_main_example.py
.. image:: https://telecominfraproject.github.io/oopt-gnpy/docs/images/transmission_main_example.svg
:width: 100%
:align: left
:alt: Running a simple simulation example
:target: https://asciinema.org/a/252295
By default, this script operates on a single span network defined in
`examples/edfa_example_network.json <examples/edfa_example_network.json>`_
You can specify a different network at the command line as follows. For
example, to use the CORONET Continental US (CONUS) network defined in
`examples/coronet_conus_example.json <examples/coronet_conus_example.json>`_:
example, to use the CORONET Global network defined in
`examples/CORONET_Global_Topology.json <examples/CORONET_Global_Topology.json>`_:
.. code-block:: shell
.. code-block:: shell-session
$ cd examples
$ python transmission_main_example.py CORONET_Global_Topology.json
$ ./examples/transmission_main_example.py examples/CORONET_Global_Topology.json
It is also possible to use an Excel file input (for example
`examples/CORONET_Global_Topology.xls <examples/CORONET_Global_Topology.xls>`_).
@@ -150,10 +174,9 @@ further instructions on how to prepare the Excel input file, see
`Excel_userguide.rst <Excel_userguide.rst>`_.
The main transmission example will calculate the average signal OSNR and SNR
across 93 network elements (transceiver, ROADMs, fibers, and amplifiers)
between two transceivers selected by the user. (By default, for the CORONET US
network, it will show the transmission of spectral information between Abilene,
Texas and Albany, New York.)
across network elements (transceiver, ROADMs, fibers, and amplifiers)
between two transceivers selected by the user. Additional details are provided by doing ``transmission_main_example.py -h``. (By default, for the CORONET Global
network, it will show the transmission of spectral information between Abilene and Albany)
This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|.
@@ -171,8 +194,8 @@ Further Instructions for Use (`transmission_main_example.py`, `path_requests_run
Design and transmission parameters are defined in a dedicated json file. By
default, this information is read from `examples/eqpt_config.json
<examples/eqpt_config.json>`_. This file defines the equipement librairies that
can be customized (EDFAs, fibers, and transcievers).
<examples/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.)
@@ -180,59 +203,59 @@ information to transmit.)
The EDFA equipment library is a list of supported amplifiers. New amplifiers
can be added and existing ones removed. Three different noise models are available:
1. `'type_def': 'variable_gain'` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: `nf_min`, `nf_max`, and `gain_flatmax`. It is not a simple interpolation but a 2-stage NF calculation.
2. `'type_def': 'fixed_gain'` is a fixed gain model. `NF == Cte == nf0` if `gain_min < gain < gain_flatmax`
3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/advanced_config_from.json <examples/advanced_config_from.json>`_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled.
1. ``'type_def': 'variable_gain'`` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: ``nf_min``, ``nf_max``, and ``gain_flatmax``. It is not a simple interpolation but a 2-stage NF calculation.
2. ``'type_def': 'fixed_gain'`` is a fixed gain model. `NF == Cte == nf0` if `gain_min < gain < gain_flatmax`
3. ``'type_def': None`` is an advanced model. A detailed JSON configuration file is required (by default `examples/std_medium_gain_advanced_config.json <examples/std_medium_gain_advanced_config.json>`_). It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled.
For all amplifier models:
+----------------------+-----------+-----------------------------------------+
| field | type | description |
+======================+===========+=========================================+
| `type_variety` | (string) | a unique name to ID the amplifier in the|
| | | JSON/Excel template topology input file |
+----------------------+-----------+-----------------------------------------+
| `out_voa_auto` | (boolean) | auto_design feature to optimize the |
| | | amplifier output VOA. If true, output |
| | | VOA is present and will be used to push |
| | | amplifier gain to its maximum, within |
| | | EOL power margins. |
+----------------------+-----------+-----------------------------------------+
| `allowed_for_design` | (boolean) | If false, the amplifier will not be |
| | | picked by auto-design but it can still |
| | | be used as a manual input (from JSON or |
| | | Excel template topology files.) |
+----------------------+-----------+-----------------------------------------+
+------------------------+-----------+-----------------------------------------+
| field | type | description |
+========================+===========+=========================================+
| ``type_variety`` | (string) | a unique name to ID the amplifier in the|
| | | JSON/Excel template topology input file |
+------------------------+-----------+-----------------------------------------+
| ``out_voa_auto`` | (boolean) | auto_design feature to optimize the |
| | | amplifier output VOA. If true, output |
| | | VOA is present and will be used to push |
| | | amplifier gain to its maximum, within |
| | | EOL power margins. |
+------------------------+-----------+-----------------------------------------+
| ``allowed_for_design`` | (boolean) | If false, the amplifier will not be |
| | | picked by auto-design but it can still |
| | | be used as a manual input (from JSON or |
| | | Excel template topology files.) |
+------------------------+-----------+-----------------------------------------+
The fiber library currently describes SSMF but additional fiber types can be entered by the user following the same model:
The fiber library currently describes SSMF and NZDF but additional fiber types can be entered by the user following the same model:
+----------------------+-----------+-----------------------------------------+
| field | type | description |
+======================+===========+=========================================+
| `type_variety` | (string) | a unique name to ID the amplifier in the|
| ``type_variety`` | (string) | a unique name to ID the fiber in the |
| | | JSON or Excel template topology input |
| | | file |
+----------------------+-----------+-----------------------------------------+
| `dispersion` | (number) | (s.m-1.m-1) |
| ``dispersion`` | (number) | (s.m-1.m-1) |
+----------------------+-----------+-----------------------------------------+
| `gamma` | (number) | 2pi.n2/(lambda*Aeff) (w-2.m-1) |
| ``gamma`` | (number) | 2pi.n2/(lambda*Aeff) (w-2.m-1) |
+----------------------+-----------+-----------------------------------------+
The transceiver equipment library is a list of supported transceivers. New
transceivers can be added and existing ones removed at will by the user. It is
used to determine the service list path feasibility when running the
path_request_run.py routine.
`path_request_run.py routine <examples/path_request_run.py>`_.
+----------------------+-----------+-----------------------------------------+
| field | type | description |
+======================+===========+=========================================+
| `type_variety` | (string) | a unique name to ID the amplifier in |
| ``type_variety`` | (string) | A unique name to ID the transceiver in |
| | | the JSON or Excel template topology |
| | | input file |
+----------------------+-----------+-----------------------------------------+
| `frequency` | (number) | Min/max as below. |
| ``frequency`` | (number) | Min/max as below. |
+----------------------+-----------+-----------------------------------------+
| `mode` | (number) | a list of modes supported by the |
| ``mode`` | (number) | A list of modes supported by the |
| | | transponder. New modes can be added at |
| | | will by the user. The modes are specific|
| | | to each transponder type_variety. |
@@ -244,138 +267,142 @@ The modes are defined as follows:
+----------------------+-----------+-----------------------------------------+
| field | type | description |
+======================+===========+=========================================+
| `format` | (string) | a unique name to ID the mode. |
| ``format`` | (string) | a unique name to ID the mode |
+----------------------+-----------+-----------------------------------------+
| `baud_rate` | (number) | in Hz |
| ``baud_rate`` | (number) | in Hz |
+----------------------+-----------+-----------------------------------------+
| `OSNR` | (number) | min required OSNR in 0.1nm (dB) |
| ``OSNR`` | (number) | min required OSNR in 0.1nm (dB) |
+----------------------+-----------+-----------------------------------------+
| `bit_rate` | (number) | in bit/s |
| ``bit_rate`` | (number) | in bit/s |
+----------------------+-----------+-----------------------------------------+
| `roll_off` | (number) | |
| ``roll_off`` | (number) | Not used. |
+----------------------+-----------+-----------------------------------------+
| ``tx_osnr`` | (number) | In dB. OSNR out from transponder. |
+----------------------+-----------+-----------------------------------------+
| ``cost`` | (number) | Arbitrary unit |
+----------------------+-----------+-----------------------------------------+
Simulation parameters are defined as follows.
Auto-design automatically creates EDFA amplifier network elements when they are
missing, after a fiber, or between a ROADM and a fiber. This auto-design
functionality can be manually and locally deactivated by introducing a `Fused`
network element after a `Fiber` or a `Roadm` that doesn't need amplification.
functionality can be manually and locally deactivated by introducing a ``Fused``
network element after a ``Fiber`` or a ``Roadm`` that doesn't need amplification.
The amplifier is chosen in the EDFA list of the equipment library based on
gain, power, and NF criteria. Only the EDFA that are marked
`'allowed_for_design': true` are considered.
``'allowed_for_design': true`` are considered.
For amplifiers defined in the topology JSON input but whose gain = 0
(placeholder), auto-design will set its gain automatically: see `power_mode` in
the `Spans` library to find out how the gain is calculated.
For amplifiers defined in the topology JSON input but whose ``gain = 0``
(placeholder), auto-design will set its gain automatically: see ``power_mode`` in
the ``Spans`` library to find out how the gain is calculated.
Span configuration is performed as followws. It is not a list (which may change
in later releases,) and the user can only modify the value of existing
Span configuration is performed as follows. It is not a list (which may change
in later releases) and the user can only modify the value of existing
parameters:
+------------------------+-----------+---------------------------------------------+
| field | type | description |
+========================+===========+=============================================+
| `power_mode` | (boolean) | If false, gain mode. Auto-design sets |
| | | amplifier gain = preceeding span loss, |
| | | unless the amplifier exists and its |
| | | gain > 0 in the topology input json. |
| | | If true, power mode (recommended for |
| | | auto-design and power sweep.) |
| | | Auto-design sets amplifier power |
| | | according to delta_power_range. If the |
| | | amplifier exists with gain > 0 in the |
| | | topology json input, then its gain is |
| | | translated into a power target/channel. |
| | | Moreover, when performing a power sweep |
| | | (see power_range_db in the SI |
| | | configuration library) the power sweep |
| | | is performed w/r/t this power target, |
| | | regardless of preceeding amplifiers |
| | | power saturation/limitations. |
+------------------------+-----------+---------------------------------------------+
| `delta_power_range_db` | (number) | Auto-design only, power-mode |
| | | only. Specifies the [min, max, step] |
| | | power excursion/span. It is a relative |
| | | power excursion w/r/t the |
| | | power_dbm + power_range_db |
| | | (power sweep if applicable) defined in |
| | | the SI configuration library. This |
| | | relative power excursion is = 1/3 of |
| | | the span loss difference with the |
| | | reference 20 dB span. The 1/3 slope is |
| | | derived from the GN model equations. |
| | | For example, a 23 dB span loss will be |
| | | set to 1 dB more power than a 20 dB |
| | | span loss. The 20 dB reference spans |
| | | will *always* be set to |
| | | power = power_dbm + power_range_db. |
| | | To configure the same power in all |
| | | spans, use `[0, 0, 0]`. All spans will |
| | | be set to |
| | | power = power_dbm + power_range_db. |
| | | To configure the same power in all spans |
| | | and 3 dB more power just for the longest |
| | | spans: `[0, 3, 3]`. The longest spans are |
| | | set to |
| | | power = power_dbm + power_range_db + 3. |
| | | To configure a 4 dB power range across |
| | | all spans in 0.5 dB steps: `[-2, 2, 0.5]`. |
| | | A 17 dB span is set to |
| | | power = power_dbm + power_range_db - 1, |
| | | a 20 dB span to |
| | | power = power_dbm + power_range_db and |
| | | a 23 dB span to |
| | | power = power_dbm + power_range_db + 1 |
+------------------------+-----------+---------------------------------------------+
| `max_length` | (number) | Split fiber lengths > max_length. |
| | | Interest to support high level |
| | | topologies that do not specify in line |
| | | amplification sites. For example the |
| | | CORONET_Global_Topology.xls defines |
| | | links > 1000km between 2 sites: it |
| | | couldn't be simulated if these links |
| | | were not splitted in shorter span |
| | | lengths. |
+------------------------+-----------+---------------------------------------------+
| `length_unit` | "m"/"km" | Unit for max_length. |
+------------------------+-----------+---------------------------------------------+
| `max_loss` | (number) | Not used in the current code |
| | | implementation. |
+------------------------+-----------+---------------------------------------------+
| `padding` | (number) | In dB. Min span loss before putting an |
| | | attenuator before fiber. Attenuator |
| | | value |
| | | Fiber.att_in = max(0, padding - span_loss). |
| | | Padding can be set manually to reach a |
| | | higher padding value for a given fiber |
| | | by filling in the Fiber/params/att_in |
| | | field in the topology json input [1] |
| | | but if span_loss = length * loss_coef |
| | | + att_in + con_in + con_out < padding, |
| | | the specified att_in value will be |
| | | completed to have span_loss = padding. |
| | | Therefore it is not possible to set |
| | | span_loss < padding. |
+------------------------+-----------+---------------------------------------------+
| `EOL` | (number) | All fiber span loss ageing. The value |
| | | is added to the con_out (fiber output |
| | | connector). So the design and the path |
| | | feasibility are performed with |
| | | span_loss + EOL. EOL cannot be set |
| | | manually for a given fiber span |
| | | (workaround is to specify higher con_out |
| | | loss for this fiber). |
+------------------------+-----------+---------------------------------------------+
| `con_in`, `con_out` | (number) | Default values if Fiber/params/con_in/out |
| | | is None in the topology input |
| | | description. This default value is |
| | | ignored if a Fiber/params/con_in/out |
| | | value is input in the topology for a |
| | | given Fiber. |
+------------------------+-----------+---------------------------------------------+
**[1]**
+-------------------------------------+-----------+---------------------------------------------+
| field | type | description |
+=====================================+===========+=============================================+
| ``power_mode`` | (boolean) | If false, gain mode. Auto-design sets |
| | | amplifier gain = preceding span loss, |
| | | unless the amplifier exists and its |
| | | gain > 0 in the topology input JSON. |
| | | If true, power mode (recommended for |
| | | auto-design and power sweep.) |
| | | Auto-design sets amplifier power |
| | | according to delta_power_range. If the |
| | | amplifier exists with gain > 0 in the |
| | | topology JSON input, then its gain is |
| | | translated into a power target/channel. |
| | | Moreover, when performing a power sweep |
| | | (see ``power_range_db`` in the SI |
| | | configuration library) the power sweep |
| | | is performed w/r/t this power target, |
| | | regardless of preceding amplifiers |
| | | power saturation/limitations. |
+-------------------------------------+-----------+---------------------------------------------+
| ``delta_power_range_db`` | (number) | Auto-design only, power-mode |
| | | only. Specifies the [min, max, step] |
| | | power excursion/span. It is a relative |
| | | power excursion w/r/t the |
| | | power_dbm + power_range_db |
| | | (power sweep if applicable) defined in |
| | | the SI configuration library. This |
| | | relative power excursion is = 1/3 of |
| | | the span loss difference with the |
| | | reference 20 dB span. The 1/3 slope is |
| | | derived from the GN model equations. |
| | | For example, a 23 dB span loss will be |
| | | set to 1 dB more power than a 20 dB |
| | | span loss. The 20 dB reference spans |
| | | will *always* be set to |
| | | power = power_dbm + power_range_db. |
| | | To configure the same power in all |
| | | spans, use `[0, 0, 0]`. All spans will |
| | | be set to |
| | | power = power_dbm + power_range_db. |
| | | To configure the same power in all spans |
| | | and 3 dB more power just for the longest |
| | | spans: `[0, 3, 3]`. The longest spans are |
| | | set to |
| | | power = power_dbm + power_range_db + 3. |
| | | To configure a 4 dB power range across |
| | | all spans in 0.5 dB steps: `[-2, 2, 0.5]`. |
| | | A 17 dB span is set to |
| | | power = power_dbm + power_range_db - 1, |
| | | a 20 dB span to |
| | | power = power_dbm + power_range_db and |
| | | a 23 dB span to |
| | | power = power_dbm + power_range_db + 1 |
+-------------------------------------+-----------+---------------------------------------------+
| ``max_fiber_lineic_loss_for_raman`` | (number) | Maximum linear fiber loss for Raman |
| | | amplification use. |
+-------------------------------------+-----------+---------------------------------------------+
| ``max_length`` | (number) | Split fiber lengths > max_length. |
| | | Interest to support high level |
| | | topologies that do not specify in line |
| | | amplification sites. For example the |
| | | CORONET_Global_Topology.xls defines |
| | | links > 1000km between 2 sites: it |
| | | couldn't be simulated if these links |
| | | were not split in shorter span lengths. |
+-------------------------------------+-----------+---------------------------------------------+
| ``length_unit`` | "m"/"km" | Unit for ``max_length``. |
+-------------------------------------+-----------+---------------------------------------------+
| ``max_loss`` | (number) | Not used in the current code |
| | | implementation. |
+-------------------------------------+-----------+---------------------------------------------+
| ``padding`` | (number) | In dB. Min span loss before putting an |
| | | attenuator before fiber. Attenuator |
| | | value |
| | | Fiber.att_in = max(0, padding - span_loss). |
| | | Padding can be set manually to reach a |
| | | higher padding value for a given fiber |
| | | by filling in the Fiber/params/att_in |
| | | field in the topology json input [1] |
| | | but if span_loss = length * loss_coef |
| | | + att_in + con_in + con_out < padding, |
| | | the specified att_in value will be |
| | | completed to have span_loss = padding. |
| | | Therefore it is not possible to set |
| | | span_loss < padding. |
+-------------------------------------+-----------+---------------------------------------------+
| ``EOL`` | (number) | All fiber span loss ageing. The value |
| | | is added to the con_out (fiber output |
| | | connector). So the design and the path |
| | | feasibility are performed with |
| | | span_loss + EOL. EOL cannot be set |
| | | manually for a given fiber span |
| | | (workaround is to specify higher |
| | | ``con_out`` loss for this fiber). |
+-------------------------------------+-----------+---------------------------------------------+
| ``con_in``, | (number) | Default values if Fiber/params/con_in/out |
| ``con_out`` | | is None in the topology input |
| | | description. This default value is |
| | | ignored if a Fiber/params/con_in/out |
| | | value is input in the topology for a |
| | | given Fiber. |
+-------------------------------------+-----------+---------------------------------------------+
.. code-block:: json
@@ -396,35 +423,38 @@ parameters:
}
ROADMs can be configured as follows. The user can only modify the value of
existing parmeters:
existing parameters:
+-------------------------+-----------+---------------------------------------------+
| field | type | description |
+=========================+===========+=============================================+
|`gain_mode_default_loss` | (number) | Default value if Roadm/params/loss is |
| | | None in the topology input description. |
| | | This default value is ignored if a |
| | | params/loss value is input in the |
| | | topology for a given ROADM. |
+-------------------------+-----------+---------------------------------------------+
|`power_mode_pref` | (number) | Power mode only. Auto-design sets the |
| | | power of ROADM ingress amplifiers to |
| | | power_dbm + power_range_db, |
| | | regardless of existing gain settings |
| | | from the topology JSON input. |
| | | Auto-design sets the Roadm loss so that |
| | | its egress channel power = power_mode_pref, |
| | | regardless of existing loss settings |
| | | from the topology JSON input. It means |
| | | that the ouput power from a ROADM (and |
| | | therefore its OSNR contribution) is Cte |
| | | and not depending from power_dbm and |
| | | power_range_db sweep settings. This |
| | | choice is meant to reflect some typical |
| | | control loop algorithms. |
+-------------------------+-----------+---------------------------------------------+
+--------------------------+-----------+---------------------------------------------+
| field | type | description |
+==========================+===========+=============================================+
| ``target_pch_out_db`` | (number) | Auto-design sets the ROADM egress channel |
| | | power. This reflects typical control loop |
| | | algorithms that adjust ROADM losses to |
| | | equalize channels (eg coming from different |
| | | ingress direction or add ports) |
| | | This is the default value |
| | | Roadm/params/target_pch_out_db if no value |
| | | is given in the ``Roadm`` element in the |
| | | topology input description. |
| | | This default value is ignored if a |
| | | params/target_pch_out_db value is input in |
| | | the topology for a given ROADM. |
+--------------------------+-----------+---------------------------------------------+
| ``add_drop_osnr`` | (number) | OSNR contribution from the add/drop ports |
+--------------------------+-----------+---------------------------------------------+
| ``restrictions`` | (dict of | If non-empty, keys ``preamp_variety_list`` |
| | strings) | and ``booster_variety_list`` represent |
| | | list of ``type_variety`` amplifiers which |
| | | are allowed for auto-design within ROADM's |
| | | line degrees. |
| | | |
| | | If no booster should be placed on a degree, |
| | | insert a ``Fused`` node on the degree |
| | | output. |
+--------------------------+-----------+---------------------------------------------+
The `SpectralInformation` object can be configured as follows. The user can
The ``SpectralInformation`` object can be configured as follows. The user can
only modify the value of existing parameters. It defines a spectrum of N
identical carriers. While the code libraries allow for different carriers and
power levels, the current user parametrization only allows one carrier type and
@@ -433,19 +463,18 @@ one power/channel definition.
+----------------------+-----------+-------------------------------------------+
| field | type | description |
+======================+===========+===========================================+
| `f_min/max` | (number) | In Hz. Carrier min max excursion |
| ``f_min``, | (number) | In Hz. Carrier min max excursion. |
| ``f_max`` | | |
+----------------------+-----------+-------------------------------------------+
| `baud_rate` | (number) | In Hz. Simulated baud rate. |
| ``baud_rate`` | (number) | In Hz. Simulated baud rate. |
+----------------------+-----------+-------------------------------------------+
| `spacing` | (number) | In Hz. Carrier spacing. |
| ``spacing`` | (number) | In Hz. Carrier spacing. |
+----------------------+-----------+-------------------------------------------+
| `roll_off` | (number) | Not used. |
| ``roll_off`` | (number) | Not used. |
+----------------------+-----------+-------------------------------------------+
| `OSNR` | (number) | Not used. |
| ``tx_osnr`` | (number) | In dB. OSNR out from transponder. |
+----------------------+-----------+-------------------------------------------+
| `bit_rate` | (number) | Not used. |
+----------------------+-----------+-------------------------------------------+
| `power_dbm` | (number) | Reference channel power. In gain mode |
| ``power_dbm`` | (number) | Reference channel power. In gain mode |
| | | (see spans/power_mode = false), all gain |
| | | settings are offset w/r/t this reference |
| | | power. In power mode, it is the |
@@ -458,22 +487,30 @@ one power/channel definition.
| | | power sweep is defined (see after) the |
| | | design is not repeated. |
+----------------------+-----------+-------------------------------------------+
| `power_range_db` | (number) | Power sweep excursion around power_dbm. |
| ``power_range_db`` | (number) | Power sweep excursion around power_dbm. |
| | | It is not the min and max channel power |
| | | values! The reference power becomes: |
| | | power_range_db + power_dbm. |
+----------------------+-----------+-------------------------------------------+
| ``sys_margins`` | (number) | In dB. Added margin on min required |
| | | transceiver OSNR. |
+----------------------+-----------+-------------------------------------------+
The `transmission_main_example.py <examples/transmission_main_example.py>`_
script propagates a specrum of channels at 32 Gbaud, 50 GHz spacing and 0
dBm/channel. These are not yet parametrized but can be modified directly in the
script (via the SpectralInformation structure) to accomodate any baud rate,
spacing, power or channel count demand.
The `transmission_main_example.py <examples/transmission_main_example.py>`_ script propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing and 0 dBm/channel.
Launch power can be overridden by using the ``--power`` argument.
Spectrum information is not yet parametrized but can be modified directly in the ``eqpt_config.json`` (via the ``SpectralInformation`` -SI- structure) to accommodate any baud rate or spacing.
The number of channel is computed based on ``spacing`` and ``f_min``, ``f_max`` values.
The amplifier's gain is set to exactly compensate for the loss in each network
element. The amplifier is currently defined with gain range of 15 dB to 25 dB
and 21 dBm max output power. Ripple and NF models are defined in
`examples/std_medium_gain_advanced_config.json <examples/std_medium_gain_advanced_config.json>`_
An experimental support for Raman amplification is available:
.. code-block:: shell
$ ./examples/transmission_main_example.py \
examples/raman_edfa_example_network.json \
--sim examples/sim_params.json --show-channels
Configuration of Raman pumps (their frequencies, power and pumping direction) is done via the `RamanFiber element in the network topology <examples/raman_edfa_example_network.json>`_.
General numeric parameters for simulaiton control are provided in the `examples/sim_params.json <examples/sim_params.json>`_.
Use `examples/path_requests_run.py <examples/path_requests_run.py>`_ to run multiple optimizations as follows:
@@ -482,7 +519,7 @@ Use `examples/path_requests_run.py <examples/path_requests_run.py>`_ to run mult
$ python path_requests_run.py -h
Usage: path_requests_run.py [-h] [-v] [-o OUTPUT] [network_filename] [service_filename] [eqpt_filename]
The `network_filename` and `service_filename` can be an XLS or JSON file. The `eqpt_filename` must be a JSON file.
The ``network_filename`` and ``service_filename`` can be an XLS or JSON file. The ``eqpt_filename`` must be a JSON file.
To see an example of it, run:
@@ -493,11 +530,11 @@ To see an example of it, run:
This program requires a list of connections to be estimated and the equipment
library. The program computes performances for the list of services (accepts
json or excel format) using the same spectrum propagation modules as
transmission_main_example.py. Explanation on the Excel template is provided in
JSON or Excel format) using the same spectrum propagation modules as
``transmission_main_example.py``. Explanation on the Excel template is provided in
the `Excel_userguide.rst <Excel_userguide.rst#service-sheet>`_. Template for
the json format can be found here: `service_template.json
<service_template.json>`_.
the JSON format can be found here: `service-template.json
<service-template.json>`_.
Contributing
------------
@@ -505,15 +542,15 @@ Contributing
``gnpy`` is looking for additional contributors, especially those with experience
planning and maintaining large-scale, real-world mesh optical networks.
To get involved, please contact James Powell
<james.powell@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>.
To get involved, please contact Jan Kundrát
<jan.kundrat@telecominfraproject.com> or Gert Grammel <ggrammel@juniper.net>.
``gnpy`` contributions are currently limited to members of `TIP
<http://telecominfraproject.com>`_. Membership is free and open to all.
See the `Onboarding Guide
<https://github.com/Telecominfraproject/gnpy/wiki/Onboarding-Guide>`_ for
specific details on code contribtions.
specific details on code contributions.
See `AUTHORS.rst <AUTHORS.rst>`_ for past and present contributors.
@@ -523,7 +560,7 @@ Project Background
Data Centers are built upon interchangeable, highly standardized node and
network architectures rather than a sum of isolated solutions. This also
translates to optical networking. It leads to a push in enabling multi-vendor
optical network by disaggregating HW and SW functions and focussing on
optical network by disaggregating HW and SW functions and focusing on
interoperability. In this paradigm, the burden of responsibility for ensuring
the performance of such disaggregated open optical systems falls on the
operators. Consequently, operators and vendors are collaborating in defining
@@ -564,6 +601,11 @@ implementations.
:alt: Build Status
:scale: 100%
.. |doi| image:: https://zenodo.org/badge/96894149.svg
:target: https://zenodo.org/badge/latestdoi/96894149
:alt: DOI
:scale: 100%
TIP OOPT/PSE & PSE WG Charter
-----------------------------

View File

@@ -874,7 +874,7 @@ month={Sept},}
number = {7},
journal = {Optics Express},
urlyear = {2017-11-14},
year = {2012-03-26},
date = {2012-03-26},
year = {2012},
pages = {7777},
author = {Bononi, A. and Serena, P. and Rossi, N. and Grellier, E. and Vacondio, F.}
@@ -1114,7 +1114,7 @@ month={Sept},}
number = {26},
journal = {Optics Express},
urlyear = {2017-11-16},
year = {2013-12-30},
date = {2013-12-30},
year = {2013},
pages = {32254},
author = {Bononi, Alberto and Beucher, Ottmar and Serena, Paolo}

View File

@@ -173,5 +173,4 @@ texinfo_documents = [
'Miscellaneous'),
]
autodoc_default_flags = ['members', 'undoc-members', 'private-members', 'show-inheritance']

BIN
docs/images/GNPy-banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

View File

@@ -43,10 +43,16 @@ Contributors in alphabetical order
+==========+============+=======================+======================================+
| Alessio | Ferrari | Politecnico di Torino | alessio.ferrari@polito.it |
+----------+------------+-----------------------+--------------------------------------+
| Anders | Lindgren | Telia Company | Anders.X.Lindgren@teliacompany.com |
+----------+------------+-----------------------+--------------------------------------+
| Andrea | d'Amico | Politecnico di Torino | andrea.damico@polito.it |
+----------+------------+-----------------------+--------------------------------------+
| Brian | Taylor | Facebook | briantaylor@fb.com |
+----------+------------+-----------------------+--------------------------------------+
| David | Boertjes | Ciena | dboertje@ciena.com |
+----------+------------+-----------------------+--------------------------------------+
| Diego | Landa | Facebook | dlanda@fb.com |
+----------+------------+-----------------------+--------------------------------------+
| Esther | Le Rouzic | Orange | esther.lerouzic@orange.com |
+----------+------------+-----------------------+--------------------------------------+
| Gabriele | Galimberti | Cisco | ggalimbe@cisco.com |
@@ -57,14 +63,29 @@ Contributors in alphabetical order
+----------+------------+-----------------------+--------------------------------------+
| James | Powell | Telecom Infra Project | james.powell@telecominfraproject.com |
+----------+------------+-----------------------+--------------------------------------+
| Jeanluc | Auge | Orange | jeanluc.auge@orange.com |
| Jan | Kundrát | Telecom Infra Project | jan.kundrat@telecominfraproject.com |
+----------+------------+-----------------------+--------------------------------------+
| Jeanluc | Augé | Orange | jeanluc.auge@orange.com |
+----------+------------+-----------------------+--------------------------------------+
| Jonas | Mårtensson | RISE Research Sweden | jonas.martensson@ri.se |
+----------+------------+-----------------------+--------------------------------------+
| Mattia | Cantono | Politecnico di Torino | mattia.cantono@polito.it |
+----------+------------+-----------------------+--------------------------------------+
| Miguel | Garrich | University Catalunya | miquel.garrich@upct.es |
+----------+------------+-----------------------+--------------------------------------+
| Raj | Nagarajan | Lumentum | raj.nagarajan@lumentum.com |
+----------+------------+-----------------------+--------------------------------------+
| Roberts | Miculens | Lattelecom | roberts.miculens@lattelecom.lv |
+----------+------------+-----------------------+--------------------------------------+
| Shengxiang | Zhu | University of Arizona | szhu@email.arizona.edu |
+----------+------------+-----------------------+--------------------------------------+
| Stefan | Melin | Telia Company | Stefan.Melin@teliacompany.com |
+----------+------------+-----------------------+--------------------------------------+
| Vittorio | Curri | Politecnico di Torino | vittorio.curri@polito.it |
+----------+------------+-----------------------+--------------------------------------+
| Xufeng | Liu | Jabil | xufeng_liu@jabil.com |
+----------+------------+-----------------------+--------------------------------------+
PSE WG Charter
--------------
- Goal is to build an end-to-end simulation environment which defines the

View File

@@ -4,10 +4,39 @@ gnpy\.core package
Submodules
----------
gnpy\.core\.ansi_escapes module
-------------------------------
.. automodule:: gnpy.core.ansi_escapes
:members:
:undoc-members:
:show-inheritance:
gnpy\.core\.convert module
--------------------------
.. automodule:: gnpy.core.convert
:members:
:undoc-members:
:show-inheritance:
gnpy\.core\.elements module
---------------------------
.. automodule:: gnpy.core.elements
gnpy\.core\.equipment module
----------------------------
.. automodule:: gnpy.core.equipment
:members:
:undoc-members:
:show-inheritance:
gnpy\.core\.exceptions module
-----------------------------
.. automodule:: gnpy.core.exceptions
:members:
:undoc-members:
:show-inheritance:
@@ -16,30 +45,34 @@ gnpy\.core\.execute module
--------------------------
.. automodule:: gnpy.core.execute
:members:
:undoc-members:
:show-inheritance:
gnpy\.core\.info module
-----------------------
.. automodule:: gnpy.core.info
:members:
:undoc-members:
:show-inheritance:
gnpy\.core\.network module
--------------------------
.. automodule:: gnpy.core.network
:members:
:undoc-members:
:show-inheritance:
gnpy\.core\.node module
-----------------------
.. automodule:: gnpy.core.node
gnpy\.core\.request module
--------------------------
.. automodule:: gnpy.core.request
:members:
:undoc-members:
:show-inheritance:
gnpy\.core\.service_sheet module
--------------------------------
.. automodule:: gnpy.core.service_sheet
:members:
:undoc-members:
:show-inheritance:
@@ -48,23 +81,14 @@ gnpy\.core\.units module
------------------------
.. automodule:: gnpy.core.units
:members:
:undoc-members:
:show-inheritance:
gnpy\.core\.utils module
------------------------
.. automodule:: gnpy.core.utils
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: gnpy.core
:members:
:undoc-members:
:show-inheritance:

View File

@@ -12,6 +12,3 @@ Module contents
---------------
.. automodule:: gnpy
:members:
:undoc-members:
:show-inheritance:

View File

@@ -0,0 +1,124 @@
{ "Edfa":[
{
"type_variety": "fixed27",
"type_def": "fixed_gain",
"gain_flatmax": 27,
"gain_min": 27,
"p_max": 21,
"nf0": 5.5,
"allowed_for_design": false
},
{
"type_variety": "fixed22",
"type_def": "fixed_gain",
"gain_flatmax": 22,
"gain_min": 22,
"p_max": 21,
"nf0": 5.5,
"allowed_for_design": false
}
],
"Fiber":[{
"type_variety": "SSMF",
"dispersion": 1.67e-05,
"gamma": 0.00127
},
{
"type_variety": "NZDF",
"dispersion": 0.5e-05,
"gamma": 0.00146
},
{
"type_variety": "LOF",
"dispersion": 2.2e-05,
"gamma": 0.000843
}
],
"Span":[{
"power_mode": false,
"delta_power_range_db": [-2,3,0.5],
"max_fiber_lineic_loss_for_raman": 0.25,
"target_extended_gain": 2.5,
"max_length": 150,
"length_units": "km",
"max_loss": 28,
"padding": 10,
"EOL": 0,
"con_in": 0,
"con_out": 0
}
],
"Roadm":[{
"target_pch_out_db": -25,
"add_drop_osnr": 30.00,
"restrictions": {
"preamp_variety_list":[],
"booster_variety_list":[]
}
}],
"SI":[{
"f_min": 191.6e12,
"baud_rate": 32e9,
"f_max":195.1e12,
"spacing": 50e9,
"power_dbm": 0,
"power_range_db": [0,0,1],
"roll_off": 0.15,
"tx_osnr": 40,
"sys_margins": 2
}],
"Transceiver":[
{
"type_variety": "Cassini",
"frequency":{
"min": 191.35e12,
"max": 196.1e12
},
"mode":[
{
"format": "dp-qpsk",
"baud_rate": 32e9,
"OSNR": 11,
"bit_rate": 100e9,
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 37.5e9,
"cost":1
},
{
"format": "16-qam",
"baud_rate": 66e9,
"OSNR": 15,
"bit_rate": 200e9,
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 75e9,
"cost":1
}
]
},
{
"type_variety": "Voyager",
"frequency":{
"min": 191.35e12,
"max": 196.1e12
},
"mode":[
{
"format": "mode 1",
"baud_rate": 32e9,
"OSNR": 12,
"bit_rate": 100e9,
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 37.5e9,
"cost":1
}
]
}
]
}

View File

@@ -0,0 +1,67 @@
{
"path-request": [
{
"request-id": "first",
"source": "netconf:10.0.254.93:830",
"destination": "netconf:10.0.254.94:830",
"src-tp-id": "trx-Amsterdam",
"dst-tp-id": "trx-Bremen",
"bidirectional": true,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Cassini",
"trx_mode": null,
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 100000000000.0
}
}
},
{
"request-id": "second",
"source": "netconf:10.0.254.93:830",
"destination": "netconf:10.0.254.94:830",
"src-tp-id": "trx-Amsterdam",
"dst-tp-id": "trx-Bremen",
"bidirectional": true,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Cassini",
"trx_mode": null,
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 100000000000.0
}
}
}
],
"synchronization": [
{
"synchronization-id": "some redundancy please",
"svec": {
"relaxable": "false",
"disjointness": "node link",
"request-id-number": [
"first",
"second"
]
}
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,179 @@
# How many nodes in the ring topology? Up to eight is supported, then I ran out of cities..
HOW_MANY = 3
# city names
ALL_CITIES = [
'Amsterdam',
'Bremen',
'Cologne',
'Dueseldorf',
'Eindhoven',
'Frankfurt',
'Ghent',
'Hague',
]
# end of configurable parameters
J = {
"elements": [],
"connections": [],
}
def unidir_join(a, b):
global J
J["connections"].append(
{"from_node": a, "to_node": b}
)
def mk_edfa(name, gain, voa=0.0):
global J
J["elements"].append(
{"uid": name, "type": "Edfa", "type_variety": f"fixed{gain}", "operational": {"gain_target": gain, "out_voa": voa}}
)
def add_att(a, b, att):
global J
if att > 0:
uid = f"att-({a})-({b})"
else:
uid = f"splice-({a})-({b})"
J["elements"].append(
{"uid": uid, "type": "Fused", "params": {"loss": att}},
)
unidir_join(a, uid)
unidir_join(uid, b)
return uid
def build_fiber(city1, city2):
global J
J["elements"].append(
{
"uid": f"fiber-{city1}-{city2}",
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 50,
"length_units": "km",
"loss_coef": 0.2,
"con_in": 1.5,
"con_out": 1.5,
}
}
)
def unidir_patch(a, b):
global J
uid = f"patch-({a})-({b})"
J["elements"].append(
{
"uid": uid,
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": 0.5,
"con_out": 0.5,
}
}
)
add_att(a, uid, 0.0)
add_att(uid, b, 0.0)
for CITY in (ALL_CITIES[x] for x in range(0, HOW_MANY)):
J["elements"].append(
{"uid": f"trx-{CITY}", "type": "Transceiver"}
)
target_pwr = [
{"to_node": f"trx-{CITY}", "target_pch_out_db": -25},
{"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L1))", "target_pch_out_db": -12},
{"to_node": f"splice-(roadm-{CITY}-AD)-(patch-(roadm-{CITY}-AD)-(roadm-{CITY}-L2))", "target_pch_out_db": -12},
]
J["elements"].append(
{"uid": f"roadm-{CITY}-AD", "type": "Roadm", "params": {"target_pch_out_db": -2.0, "per_degree_target_pch_out_db": target_pwr}}
)
unidir_join(f"trx-{CITY}", f"roadm-{CITY}-AD")
unidir_join(f"roadm-{CITY}-AD", f"trx-{CITY}")
for n in (1,2):
target_pwr = [
{"to_node": f"roadm-{CITY}-L{n}-booster", "target_pch_out_db": -23},
{"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-AD))", "target_pch_out_db": -12},
]
for m in (1,2):
if m == n:
continue
target_pwr.append(
{"to_node": f"splice-(roadm-{CITY}-L{n})-(patch-(roadm-{CITY}-L{n})-(roadm-{CITY}-L{m}))", "target_pch_out_db": -12},
)
J["elements"].append(
{"uid": f"roadm-{CITY}-L{n}", "type": "Roadm", "params": {"target_pch_out_db": -23.0, "per_degree_target_pch_out_db": target_pwr}}
)
mk_edfa(f"roadm-{CITY}-L{n}-booster", 22)
mk_edfa(f"roadm-{CITY}-L{n}-preamp", 27)
unidir_join(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{n}-booster")
unidir_join(f"roadm-{CITY}-L{n}-preamp", f"roadm-{CITY}-L{n}")
unidir_patch(f"roadm-{CITY}-AD", f"roadm-{CITY}-L{n}")
unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-AD")
for m in (1,2):
if m == n:
continue
#add_att(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}", 22)
unidir_patch(f"roadm-{CITY}-L{n}", f"roadm-{CITY}-L{m}")
for city1, city2 in ((ALL_CITIES[i], ALL_CITIES[i + 1] if i < HOW_MANY - 1 else ALL_CITIES[0]) for i in range(0, HOW_MANY)):
build_fiber(city1, city2)
unidir_join(f"roadm-{city1}-L1-booster", f"fiber-{city1}-{city2}")
unidir_join(f"fiber-{city1}-{city2}", f"roadm-{city2}-L2-preamp")
build_fiber(city2, city1)
unidir_join(f"roadm-{city2}-L2-booster", f"fiber-{city2}-{city1}")
unidir_join(f"fiber-{city2}-{city1}", f"roadm-{city1}-L1-preamp")
for _, E in enumerate(J["elements"]):
uid = E["uid"]
if uid.startswith("roadm-") and (uid.endswith("-L1-booster") or uid.endswith("-L2-booster")):
E["operational"]["out_voa"] = 12.0
#if uid.endswith("-AD-add"):
# E["operational"]["out_voa"] = 21
translate = {
#"trx-Amsterdam": "10.0.254.93",
#"trx-Bremen": "10.0.254.94",
"trx-Amsterdam": "10.0.254.76",
"trx-Bremen": "10.0.254.77",
# Amsterdam A/D: coherent-v9u
"roadm-Amsterdam-AD": "10.0.254.107",
# Bremen A/D: -spi
"roadm-Bremen-AD": "10.0.254.225",
# Amsterdam -> Bremen ...QR79
"roadm-Amsterdam-L1": "10.0.254.78",
# Bremen -> Amsterdam ...QCP9
"roadm-Bremen-L2": "10.0.254.102",
# Bremen -> Cologne ...WKP
"roadm-Bremen-L1": "10.0.254.100",
# Cologne -> Bremen ...QLK6
"roadm-Cologne-L2": "10.0.254.104",
# Cologne -> Amsterdam ...TQQ
"roadm-Cologne-L1": "10.0.254.99",
# Amsterdam -> Cologne ...Q7JS
"roadm-Amsterdam-L2": "10.0.254.79",
# spare Line/Degree ...QC8B
"spare-line-degree": "10.0.254.101",
# spare Add/Drop: ...NNN
"spare-add-drop": "10.0.254.228",
}
import json
s = json.dumps(J, indent=2)
for (old, new) in translate.items():
s = s.replace(f'"{old}"', f'"netconf:{new}:830"')
print(s)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -0,0 +1,160 @@
{
"nf_fit_coeff": [
0.0008,
0.0272,
-0.2249,
6.4902
],
"f_min": 191.35e12,
"f_max": 196.1e12,
"nf_ripple": [
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0,
0.0
],
"gain_ripple": [
0.15017064489112,
0.14157768006701,
0.00223094639866,
-0.06701528475711,
-0.05982935510889,
-0.01028161641541,
0.02740682579566,
0.02795958961474,
0.00107516750419,
-0.02199015912898,
-0.00877407872698,
0.0453465242881,
0.1204721524288,
0.18936662479061,
0.23826109715241,
0.26956762981574,
0.27836159966498,
0.26941687604691,
0.23579878559464,
0.18147717755444,
0.1191656197655,
0.05921587102177,
0.01509526800668,
-0.01053287269681,
-0.02475397822447,
-0.01847257118928,
-0.00420121440538,
0.01584903685091,
0.0399193886097,
0.04494451423784,
0.04961788107202,
0.03378873534338,
0.01027114740367,
-0.01319618927973,
-0.04962835008375,
-0.0765630234506,
-0.10606051088777,
-0.13550774706866,
-0.15460322445561,
-0.17113588777219,
-0.18053287269681,
-0.18324644053602,
-0.19440221943049,
-0.20897508375209,
-0.23575900335007,
-0.25188965661642,
-0.22244242043552,
-0.15656302345061
],
"dgt": [
2.4553191172498,
2.44342862248888,
2.41879254989742,
2.38192717604575,
2.33147727493671,
2.26678136721453,
2.19013043016015,
2.10336369905543,
2.01414465424155,
1.92915262384742,
1.85543800978691,
1.79748596476494,
1.75428006928365,
1.72461030013125,
1.70379790088896,
1.68845480656382,
1.6761448370895,
1.66286684904577,
1.64799163036252,
1.63068023161292,
1.61073904908309,
1.58973304612691,
1.56750088631614,
1.54578500307573,
1.5242627235492,
1.50335352244996,
1.48420288841848,
1.46637521309853,
1.44977369463316,
1.43476940680732,
1.42089447397912,
1.40864903907609,
1.3966294751726,
1.38430337205545,
1.3710092503689,
1.35690844654118,
1.3405812000038,
1.32210817897091,
1.30069883494415,
1.27657903892303,
1.24931318255134,
1.21911100318577,
1.18632744096844,
1.15209185089701,
1.11575888725852,
1.07773189112355,
1.03941448941778,
1.0
]
}

View File

@@ -1,31 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
convert_service_sheet.py
========================
XLS parser that can be called to create a JSON request file in accordance with
Yang model for requesting path computation.
See: draft-ietf-teas-yang-path-computation-01.txt
"""
from argparse import ArgumentParser
from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO
from json import dumps
from gnpy.core.service_sheet import Request, Element, Request_element
from gnpy.core.service_sheet import parse_row, parse_excel, convert_service_sheet
logger = getLogger(__name__)
if __name__ == '__main__':
args = parser.parse_args()
basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL))
logger.info(f'Converting Service sheet {args.workbook!r} into gnpy JSON format')
if args.output is None:
data = convert_service_sheet(args.workbook,'eqpt_config.json')
print(dumps(data, indent=2))
else:
data = convert_service_sheet(args.workbook,'eqpt_config.json',args.output)

View File

@@ -11,93 +11,91 @@ If not present in the "Nodes" sheet, the "Type" column will be implicitly
determined based on the topology.
"""
from sys import exit
try:
from xlrd import open_workbook
except ModuleNotFoundError:
exit('Required: `pip install xlrd`')
from argparse import ArgumentParser
from collections import namedtuple, defaultdict
PARSER = ArgumentParser()
PARSER.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls',
help='create the mandatory columns in Eqpt sheet')
ALL_ROWS = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
Shortlink = namedtuple('Link', 'src dest')
class Node:
""" Node element contains uid, list of connected nodes and eqpt type
"""
def __init__(self, uid, to_node):
self.uid = uid
self.to_node = to_node
self.eqpt = None
Shortnode = namedtuple('Node', 'nodename eqt')
def __repr__(self):
return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n'
parser = ArgumentParser()
parser.add_argument('workbook', nargs='?', default='meshTopologyExampleV2.xls',
help = 'create the mandatory columns in Eqpt sheet ')
all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
def __str__(self):
return f'uid {self.uid} \nto_node {[node for node in self.to_node]}\neqpt {self.eqpt}\n'
def read_excel(input_filename):
with open_workbook(input_filename) as wb:
""" read excel Nodes and Links sheets and create a dict of nodes with
their to_nodes and type of eqpt
"""
with open_workbook(input_filename) as wobo:
# reading Links sheet
links_sheet = wb.sheet_by_name('Links')
links = []
nodeoccuranceinlinks = []
links_by_src = defaultdict(list)
links_by_dest = defaultdict(list)
for row in all_rows(links_sheet, start=5):
links.append(Shortlink(row[0].value,row[1].value))
links_by_src[row[0].value].append(Shortnode(row[1].value,''))
links_by_dest[row[1].value].append(Shortnode(row[0].value,''))
#print(f'source {links[len(links)-1].src} dest {links[len(links)-1].dest}')
nodeoccuranceinlinks.append(row[0].value)
nodeoccuranceinlinks.append(row[1].value)
links_sheet = wobo.sheet_by_name('Links')
nodes = {}
for row in ALL_ROWS(links_sheet, start=5):
try:
nodes[row[0].value].to_node.append(row[1].value)
except KeyError:
nodes[row[0].value] = Node(row[0].value, [row[1].value])
try:
nodes[row[1].value].to_node.append(row[0].value)
except KeyError:
nodes[row[1].value] = Node(row[1].value, [row[0].value])
# reading Nodes sheet
nodes_sheet = wb.sheet_by_name('Nodes')
nodes = []
node_degree = []
for row in all_rows(nodes_sheet, start=5) :
nodes_sheet = wobo.sheet_by_name('Nodes')
for row in ALL_ROWS(nodes_sheet, start=5):
node = row[0].value
eqpt = row[6].value
try:
if eqpt == 'ILA' and len(nodes[node].to_node) != 2:
print(f'Inconsistancy ILA node with degree > 2: {node} ')
exit()
if eqpt == '' and len(nodes[node].to_node) == 2:
nodes[node].eqpt = 'ILA'
elif eqpt == '' and len(nodes[node].to_node) != 2:
nodes[node].eqpt = 'ROADM'
else:
nodes[node].eqpt = eqpt
except KeyError:
print(f'inconsistancy between nodes and links sheet: {node} is not listed in links')
exit()
return nodes
temp_eqt = row[6].value
# verify node degree to confirm eqt type
node_degree.append(nodeoccuranceinlinks.count(row[0].value))
if temp_eqt.lower() == 'ila' and nodeoccuranceinlinks.count(row[0].value) !=2 :
print(f'Inconsistancy: node {nodes[len(nodes)-1]} has degree \
{node_degree[len(nodes)-1]} and can not be an ILA ... replaced by ROADM')
temp_eqt = 'ROADM'
if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) == 2 :
temp_eqt = 'ILA'
if temp_eqt == '' and nodeoccuranceinlinks.count(row[0].value) != 2 :
temp_eqt = 'ROADM'
# print(f'node {nodes[len(nodes)-1]} eqt {temp_eqt}')
nodes.append(Shortnode(row[0].value,temp_eqt))
# print(len(nodes)-1)
print(f'reading: node {nodes[len(nodes)-1].nodename} eqpt {temp_eqt}')
return links,nodes, links_by_src , links_by_dest
def create_eqt_template(links,nodes, links_by_src , links_by_dest, input_filename):
def create_eqt_template(nodes, input_filename):
""" writes list of node A node Z corresponding to Nodes and Links sheets in order
to help user populating Eqpt
"""
output_filename = f'{input_filename[:-4]}_eqpt_sheet.txt'
with open(output_filename,'w') as my_file :
with open(output_filename, 'w', encoding='utf-8') as my_file:
# print header similar to excel
my_file.write('OPTIONAL\n\n\n\
\t\tNode a egress amp (from a to z)\t\t\t\t\tNode a ingress amp (from z to a) \
\nNode A \tNode Z \tamp type \tatt_in \tamp gain \ttilt \tatt_out\
amp type \tatt_in \tamp gain \ttilt \tatt_out\n')
tab = []
temp = []
i = 0
for lk in links:
if [e for n,e in nodes if n==lk.src][0] != 'FUSED' :
temp = [lk.src , lk.dest]
tab.append(temp)
my_file.write(f'{temp[0]}\t{temp[1]}\n')
for n in nodes :
if n.eqt.lower() == 'roadm' :
for src in links_by_dest[n.nodename] :
temp = [n.nodename , src.nodename]
tab.append(temp)
# print(temp)
my_file.write(f'{temp[0]}\t{temp[1]}\n')
i = i + 1
for node in nodes.values():
if node.eqpt == 'ILA':
my_file.write(f'{node.uid}\t{node.to_node[0]}\n')
if node.eqpt == 'ROADM':
for to_node in node.to_node:
my_file.write(f'{node.uid}\t{to_node}\n')
print(f'File {output_filename} successfully created with Node A - Node Z ' +
' entries for Eqpt sheet in excel file.')
' entries for Eqpt sheet in excel file.')
if __name__ == '__main__':
args = parser.parse_args()
input_filename = args.workbook
links,nodes,links_by_src, links_by_dest = read_excel(input_filename)
create_eqt_template(links,nodes, links_by_src , links_by_dest , input_filename)
ARGS = PARSER.parse_args()
create_eqt_template(read_excel(ARGS.workbook), ARGS.workbook)

1033
examples/demo.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
{
"nf_ripple": "NFR0_96.txt",
"gain_ripple": "DFG0_96.txt",
"dgt": "DGT_96.txt"
"nf_ripple": "NFR_96.txt",
"gain_ripple": "DFG_96.txt",
"dgt": "DGT_96.txt",
"nf_fit_coeff": "pNFfit3.txt"
}

View File

@@ -1,8 +0,0 @@
-1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01
-2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02
-1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01 -2.0000000000000000e+01
-2.0500000000000000e+01 -2.0489473680000000e+01 -2.0478947370000000e+01 -2.0468421050000000e+01 -2.0457894740000000e+01 -2.0447368420000000e+01 -2.0436842110000001e+01 -2.0426315790000000e+01 -2.0415789470000000e+01 -2.0405263160000001e+01 -2.0394736840000000e+01 -2.0384210530000001e+01 -2.0373684210000000e+01 -2.0363157890000000e+01 -2.0352631580000001e+01 -2.0342105260000000e+01 -2.0331578950000001e+01 -2.0321052630000001e+01 -2.0310526320000001e+01 -2.0300000000000001e+01 -2.0289473680000000e+01 -2.0278947370000001e+01 -2.0268421050000001e+01 -2.0257894740000001e+01 -2.0247368420000001e+01 -2.0236842110000001e+01 -2.0226315790000001e+01 -2.0215789470000001e+01 -2.0205263160000001e+01 -2.0194736840000001e+01 -2.0184210530000001e+01 -2.0173684210000001e+01 -2.0163157890000001e+01 -2.0152631580000001e+01 -2.0142105260000001e+01 -2.0131578950000002e+01 -2.0121052630000001e+01 -2.0110526320000002e+01 -2.0100000000000001e+01 -2.0089473680000001e+01 -2.0078947370000002e+01 -2.0068421050000001e+01 -2.0057894739999998e+01 -2.0047368420000002e+01 -2.0036842109999998e+01 -2.0026315790000002e+01 -2.0015789470000001e+01 -2.0005263159999998e+01 -1.9994736840000002e+01 -1.9984210529999999e+01 -1.9973684209999998e+01 -1.9963157890000002e+01 -1.9952631579999998e+01 -1.9942105260000002e+01 -1.9931578949999999e+01 -1.9921052629999998e+01 -1.9910526319999999e+01 -1.9899999999999999e+01 -1.9889473679999998e+01 -1.9878947369999999e+01 -1.9868421049999998e+01 -1.9857894739999999e+01 -1.9847368419999999e+01 -1.9836842109999999e+01 -1.9826315789999999e+01 -1.9815789469999999e+01 -1.9805263159999999e+01 -1.9794736839999999e+01 -1.9784210529999999e+01 -1.9773684209999999e+01 -1.9763157889999999e+01 -1.9752631579999999e+01 -1.9742105259999999e+01 -1.9731578949999999e+01 -1.9721052629999999e+01 -1.9710526320000000e+01 -1.9699999999999999e+01 -1.9689473679999999e+01 -1.9678947369999999e+01 -1.9668421049999999e+01 -1.9657894740000000e+01 -1.9647368419999999e+01 -1.9636842110000000e+01 -1.9626315790000000e+01 -1.9615789469999999e+01 -1.9605263160000000e+01 -1.9594736839999999e+01 -1.9584210530000000e+01 -1.9573684210000000e+01 -1.9563157889999999e+01 -1.9552631580000000e+01 -1.9542105260000000e+01 -1.9531578950000000e+01 -1.9521052630000000e+01 -1.9510526320000000e+01 -1.9500000000000000e+01
-2.0500000000000000e+01 -2.0489473680000000e+01 -2.0478947370000000e+01 -2.0468421050000000e+01 -2.0457894740000000e+01 -2.0447368420000000e+01 -2.0436842110000001e+01 -2.0426315790000000e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.9573684210000000e+01 -1.9563157889999999e+01 -1.9552631580000000e+01 -1.9542105260000000e+01 -1.9531578950000000e+01 -1.9521052630000000e+01 -1.9510526320000000e+01 -1.9500000000000000e+01
-1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.4460000000000001e+01
-1.4460000000000001e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01 -1.4460000000000001e+01
-1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.4460000000000001e+01 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02 -1.0000000000000000e+02

View File

@@ -1,8 +0,0 @@
7.0000000000000000e+01 1.1700000000000000e+02 1.0800000000000000e+02 1.0800000000000000e+02 3.2000000000000000e+01 7.0000000000000000e+01 1.0800000000000000e+02 9.7000000000000000e+01 1.1600000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
7.9000000000000000e+01 1.1000000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 7.1000000000000000e+01 1.1400000000000000e+02 1.1100000000000000e+02 1.1700000000000000e+02 1.1200000000000000e+02 3.2000000000000000e+01 6.6000000000000000e+01 1.0800000000000000e+02 1.1700000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01
7.9000000000000000e+01 1.1000000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 7.1000000000000000e+01 1.1400000000000000e+02 1.1100000000000000e+02 1.1700000000000000e+02 1.1200000000000000e+02 3.2000000000000000e+01 8.2000000000000000e+01 1.0100000000000000e+02 1.0000000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01
7.0000000000000000e+01 1.1700000000000000e+02 1.0800000000000000e+02 1.0800000000000000e+02 3.2000000000000000e+01 1.1900000000000000e+02 3.2000000000000000e+01 8.3000000000000000e+01 8.2000000000000000e+01 8.3000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
6.6000000000000000e+01 1.1100000000000000e+02 1.1600000000000000e+02 1.0400000000000000e+02 3.2000000000000000e+01 6.9000000000000000e+01 1.1000000000000000e+02 1.0000000000000000e+02 1.1500000000000000e+02 3.2000000000000000e+01 1.1900000000000000e+02 3.2000000000000000e+01 8.3000000000000000e+01 8.2000000000000000e+01 8.3000000000000000e+01
1.0400000000000000e+02 1.0100000000000000e+02 9.7000000000000000e+01 1.1800000000000000e+02 1.2100000000000000e+02 3.2000000000000000e+01 9.8000000000000000e+01 1.0800000000000000e+02 1.1700000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
1.0400000000000000e+02 1.0100000000000000e+02 9.7000000000000000e+01 1.1800000000000000e+02 1.2100000000000000e+02 3.2000000000000000e+01 1.1400000000000000e+02 1.0100000000000000e+02 1.0000000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01
1.1900000000000000e+02 1.1100000000000000e+02 1.1400000000000000e+02 1.1500000000000000e+02 1.1600000000000000e+02 3.2000000000000000e+01 9.9000000000000000e+01 9.7000000000000000e+01 1.1500000000000000e+02 1.0100000000000000e+02 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01 3.2000000000000000e+01

View File

@@ -1,301 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Nov 27 12:32:04 2017
@author: briantaylor
"""
import numpy as np
from numpy import polyfit, polyval, mean
from utilities import lin2db, db2lin, itufs, freq2wavelength
import matplotlib.pyplot as plt
from scipy.constants import h
def noise_profile(nf, gain, ffs, df):
""" noise_profile(nf, gain, ffs, df) computes amplifier ase
:param nf: Noise figure in dB
:param gain: Actual gain calculated for the EDFA in dB units
:param ffs: A numpy array of frequencies
:param df: the reference bw in THz
:type nf: numpy.ndarray
:type gain: numpy.ndarray
:type ffs: numpy.ndarray
:type df: float
:return: the asepower in dBm
:rtype: numpy.ndarray
ASE POWER USING PER CHANNEL GAIN PROFILE
INPUTS:
NF_dB - Noise figure in dB, vector of length number of channels or
spectral slices
G_dB - Actual gain calculated for the EDFA, vector of length number of
channels or spectral slices
ffs - Center frequency grid of the channels or spectral slices in THz,
vector of length number of channels or spectral slices
dF - width of each channel or spectral slice in THz,
vector of length number of channels or spectral slices
OUTPUT:
ase_dBm - ase in dBm per channel or spectral slice
NOTE: the output is the total ASE in the channel or spectral slice. For
50GHz channels the ASE BW is effectively 0.4nm. To get to noise power in
0.1nm, subtract 6dB.
ONSR is usually quoted as channel power divided by
the ASE power in 0.1nm RBW, regardless of the width of the actual
channel. This is a historical convention from the days when optical
signals were much smaller (155Mbps, 2.5Gbps, ... 10Gbps) than the
resolution of the OSAs that were used to measure spectral power which
were set to 0.1nm resolution for convenience. Moving forward into
flexible grid and high baud rate signals, it may be convenient to begin
quoting power spectral density in the same BW for both signal and ASE,
e.g. 12.5GHz."""
h_mWThz = 1e-3 * h * (1e14)**2
nf_lin = db2lin(nf)
g_lin = db2lin(gain)
ase = h_mWThz * df * ffs * (nf_lin * g_lin - 1)
asedb = lin2db(ase)
return asedb
def gain_profile(dfg, dgt, Pin, gp, gtp):
"""
:param dfg: design flat gain
:param dgt: design gain tilt
:param Pin: channing input power profile
:param gp: Average gain setpoint in dB units
:param gtp: gain tilt setting
:type dfg: numpy.ndarray
:type dgt: numpy.ndarray
:type Pin: numpy.ndarray
:type gp: float
:type gtp: float
:return: gain profile in dBm
:rtype: numpy.ndarray
AMPLIFICATION USING INPUT PROFILE
INPUTS:
DFG - vector of length number of channels or spectral slices
DGT - vector of length number of channels or spectral slices
Pin - input powers vector of length number of channels or
spectral slices
Gp - provisioned gain length 1
GTp - provisioned tilt length 1
OUTPUT:
amp gain per channel or spectral slice
NOTE: there is no checking done for violations of the total output power
capability of the amp.
Ported from Matlab version written by David Boerges at Ciena.
Based on:
R. di Muro, "The Er3+ fiber gain coefficient derived from a dynamic
gain
tilt technique", Journal of Lightwave Technology, Vol. 18, Iss. 3,
Pp. 343-347, 2000.
"""
err_tolerance = 1.0e-11
simple_opt = True
# TODO make all values linear unit and convert to dB units as needed within
# this function.
nchan = list(range(len(Pin)))
# TODO find a way to use these or lose them. Primarily we should have a
# way to determine if exceeding the gain or output power of the amp
tot_in_power_db = lin2db(np.sum(db2lin(Pin)))
avg_gain_db = lin2db(mean(db2lin(dfg)))
# Linear fit to get the
p = polyfit(nchan, dgt, 1)
dgt_slope = p[0]
# Calculate the target slope- Currently assumes equal spaced channels
# TODO make it so that supports arbitrary channel spacing.
targ_slope = gtp / (len(nchan) - 1)
# 1st estimate of DGT scaling
dgts1 = targ_slope / dgt_slope
# when simple_opt is true code makes 2 attempts to compute gain and
# the internal voa value. This is currently here to provide direct
# comparison with original Matlab code. Will be removed.
# TODO replace with loop
if simple_opt:
# 1st estimate of Er gain & voa loss
g1st = dfg + dgt * dgts1
voa = lin2db(mean(db2lin(g1st))) - gp
# 2nd estimate of Amp ch gain using the channel input profile
g2nd = g1st - voa
pout_db = lin2db(np.sum(db2lin(Pin + g2nd)))
dgts2 = gp - (pout_db - tot_in_power_db)
# Center estimate of amp ch gain
xcent = dgts2
gcent = g1st - voa + dgt * xcent
pout_db = lin2db(np.sum(db2lin(Pin + gcent)))
gavg_cent = pout_db - tot_in_power_db
# Lower estimate of Amp ch gain
deltax = np.max(g1st) - np.min(g1st)
xlow = dgts2 - deltax
glow = g1st - voa + xlow * dgt
pout_db = lin2db(np.sum(db2lin(Pin + glow)))
gavg_low = pout_db - tot_in_power_db
# Upper gain estimate
xhigh = dgts2 + deltax
ghigh = g1st - voa + xhigh * dgt
pout_db = lin2db(np.sum(db2lin(Pin + ghigh)))
gavg_high = pout_db - tot_in_power_db
# compute slope
slope1 = (gavg_low - gavg_cent) / (xlow - xcent)
slope2 = (gavg_cent - gavg_high) / (xcent - xhigh)
if np.abs(gp - gavg_cent) <= err_tolerance:
dgts3 = xcent
elif gp < gavg_cent:
dgts3 = xcent - (gavg_cent - gp) / slope1
else:
dgts3 = xcent + (-gavg_cent + gp) / slope2
gprofile = g1st - voa + dgt * dgts3
else:
gprofile = None
return gprofile
if __name__ == '__main__':
plt.close('all')
fc = itufs(0.05)
lc = freq2wavelength(fc) / 1000
nchan = list(range(len(lc)))
df = np.array([0.05] * (nchan[-1] + 1))
# TODO remove path dependence
path = ''
"""
DFG_96: Design flat gain at each wavelength in the 96 channel 50GHz ITU
grid in dB. This can be experimentally determined by measuring the gain
at each wavelength using a full, flat channel (or ASE) load at the input.
The amplifier should be set to its maximum flat gain (tilt = 0dB). This
measurement captures the ripple of the amplifier. If the amplifier was
designed to be mimimum ripple at some other tilt value, then the ripple
reflected in this measurement will not be that minimum. However, when
the DGT gets applied through the provisioning of tilt, the model should
accurately reproduce the expected ripple at that tilt value. One could
also do the measurement at some expected tilt value and back-calculate
this vector using the DGT method. Alternatively, one could re-write the
algorithm to accept a nominal tilt and a tiled version of this vector.
"""
dfg_96 = np.loadtxt(path + 'DFG_96.txt')
"""maximum gain for flat operation - the amp in the data file was designed
for 25dB gain and has an internal VOA for setting the external gain
"""
avg_dfg = dfg_96.mean()
"""
DGT_96: This is the so-called Dynamic Gain Tilt of the EDFA in dB/dB. It
is the change in gain at each wavelength corresponding to a 1dB change at
the longest wavelength supported. The value can be obtained
experimentally or through analysis of the cross sections or Giles
parameters of the Er fibre. This is experimentally measured by changing
the gain of the amplifier above the maximum flat gain while not changing
the internal VOA (i.e. the mid-stage VOA is set to minimum and does not
change during the measurement). Note that the measurement can change the
gain by an arbitrary amount and divide by the gain change (in dB) which
is measured at the reference wavelength (the red end of the band).
"""
dgt_96 = np.loadtxt(path + 'DGT_96.txt')
"""
pNFfit3: Cubic polynomial fit coefficients to noise figure in dB
averaged across wavelength as a function of gain change from design flat:
NFavg = pNFfit3(1)*dG^3 + pNFfit3(2)*dG^2 pNFfit3(3)*dG + pNFfit3(4)
where
dG = GainTarget - average(DFG_96)
note that dG will normally be a negative value.
"""
nf_fitco = np.loadtxt(path + 'pNFfit3.txt')
"""NFR_96: Noise figure ripple in dB away from the average noise figure
across the band. This captures the wavelength dependence of the NF. To
calculate the NF across channels, one uses the cubic fit coefficients
with the external gain target to get the average nosie figure, NFavg and
then adds this to NFR_96:
NF_96 = NFR_96 + NFavg
"""
nf_ripple = np.loadtxt(path + 'NFR_96.txt')
# This is an example to set the provisionable gain and gain-tilt values
# Tilt is in units of dB/THz
gain_target = 20.0
tilt_target = -0.7
# calculate the NF for the EDFA at this gain setting
dg = gain_target - avg_dfg
nf_avg = polyval(nf_fitco, dg)
nf_96 = nf_ripple + nf_avg
# get the input power profiles to show
pch2d = np.loadtxt(path + 'Pchan2D.txt')
# Load legend and assemble legend text
pch2d_legend_data = np.loadtxt(path + 'Pchan2DLegend.txt')
pch2d_legend = []
for ea in pch2d_legend_data:
s = ''.join([chr(xx) for xx in ea.astype(dtype=int)]).strip()
pch2d_legend.append(s)
# assemble plot
axis_font = {'fontname': 'Arial', 'size': '16', 'fontweight': 'bold'}
title_font = {'fontname': 'Arial', 'size': '17', 'fontweight': 'bold'}
tic_font = {'fontname': 'Arial', 'size': '12'}
plt.rcParams["font.family"] = "Arial"
plt.figure()
plt.plot(nchan, pch2d.T, '.-', lw=2)
plt.xlabel('Channel Number', **axis_font)
plt.ylabel('Channel Power [dBm]', **axis_font)
plt.title('Input Power Profiles for Different Channel Loading',
**title_font)
plt.legend(pch2d_legend, loc=5)
plt.grid()
plt.ylim((-100, -10))
plt.xlim((0, 110))
plt.xticks(np.arange(0, 100, 10), **tic_font)
plt.yticks(np.arange(-110, -10, 10), **tic_font)
plt.figure()
ea = pch2d[1, :]
for ea in pch2d:
chgain = gain_profile(dfg_96, dgt_96, ea, gain_target, tilt_target)
pase = noise_profile(nf_96, chgain, fc, df)
pout = lin2db(db2lin(ea + chgain) + db2lin(pase))
plt.plot(nchan, pout, '.-', lw=2)
plt.title('Output Power with ASE for Different Channel Loading',
**title_font)
plt.xlabel('Channel Number', **axis_font)
plt.ylabel('Channel Power [dBm]', **axis_font)
plt.grid()
plt.ylim((-50, 10))
plt.xlim((0, 100))
plt.xticks(np.arange(0, 100, 10), **tic_font)
plt.yticks(np.arange(-50, 10, 10), **tic_font)
plt.legend(pch2d_legend, loc=5)
plt.show()

View File

@@ -0,0 +1,300 @@
*********************************************
Amplifier models and configuration
*********************************************
1. Equipment configuration description
#######################################
Equipment description defines equipment types and parameters.
It takes place in the default **eqpt_config.json** file.
By default **transmission_main_example.py** uses **eqpt_config.json** file and that
can be changed with **-e** or **--equipment** command line parameter.
2. Amplifier parameters and subtypes
#######################################
Several amplifiers can be used by GNpy, so they are defined as an array of equipment parameters in **eqpt_config.json** file.
- *"type_variety"*:
Each amplifier is identified by its unique *"type_variety"*, which is used in the topology files input to reference a specific amplifier. It is a user free defined id.
For each amplifier *type_variety*, specific parameters are describing its attributes and performance:
- *"type_def"*:
Sets the amplifier model that the simulation will use to calculate the ase noise contribution. 5 models are defined with reserved words:
- *"advanced_model"*
- *"variable_gain"*
- *"fixed_gain"*
- *"dual_stage"*
- *"openroadm"*
*see next section for a full description of these models*
- *"advanced_config_from_json"*:
**This parameter is only applicable to the _"advanced_model"_ model**
json file name describing:
- nf_fit_coeff
- f_min/max
- gain_ripple
- nf_ripple
- dgt
*see next section for a full description*
- *"gain_flatmax"*:
amplifier maximum gain in dB before its extended gain range: flat or nominal tilt output.
If gain > gain_flatmax, the amplifier will tilt, based on its dgt function
If gain > gain_flatmax + target_extended_gain, the amplifier output power is reduced to not exceed the extended gain range.
- *"gain_min"*:
amplifier minimum gain in dB.
If gain < gain_min, the amplifier input is automatically padded, which results in
NF += gain_min - gain
- *"p_max"*:
amplifier max output power, full load
Total signal output power will not be allowed beyond this value
- *"nf_min/max"*:
**These parameters are only applicable to the _"variable_gain"_ model**
min & max NF values in dB
NF_min is the amplifier NF @ gain_max
NF_max is the amplifier NF @ gain_min
- *"nf_coef"*:
**This parameter is only applicable to the *"openroadm"* model**
[a, b, c, d] 3rd order polynomial coefficients list to define the incremental OSNR vs Pin
Incremental OSNR is the amplifier OSNR contribution
Pin is the amplifier channel input power defined in a 50GHz bandwidth
Incremental OSNR = a*Pin³ + b*Pin² + c*Pin + d
- *"preamp_variety"*:
**This parameter is only applicable to the _"dual_stage"_ model**
1st stage type_variety
- *"booster_variety"*:
**This parameter is only applicable to the *"dual_stage"* model**
2nd stage type_variety
- *"out_voa_auto"*: true/false
**power_mode only**
**This parameter is only applicable to the *"advanced_model"* and *"variable_gain"* models**
If "out_voa_auto": true, auto_design will chose the output_VOA value that maximizes the amplifier gain within its power capability and therefore minimizes its NF.
- *"allowed_for_design"*: true/false
**auto_design only**
Tells auto_design if this amplifier can be picked for the design (deactivates unwanted amplifiers)
It does not prevent the use of an amplifier if it is placed in the topology input.
.. code-block:: json
{"Edfa": [{
"type_variety": "std_medium_gain",
"type_def": "variable_gain",
"gain_flatmax": 26,
"gain_min": 15,
"p_max": 23,
"nf_min": 6,
"nf_max": 10,
"out_voa_auto": false,
"allowed_for_design": true
},
{
"type_variety": "std_low_gain",
"type_def": "variable_gain",
"gain_flatmax": 16,
"gain_min": 8,
"p_max": 23,
"nf_min": 6.5,
"nf_max": 11,
"out_voa_auto": false,
"allowed_for_design": true
}
]}
3. Amplifier models
#######################################
In an opensource and multi-vendor environnement, it is needed to support different use cases and context. Therefore several models are supported for amplifiers.
5 types of EDFA definition are possible and referenced by the *"type_def"* parameter with the following reserved words:
- *"advanced_model"*
This model is refered as a whitebox model because of the detailed level of knowledge that is required. The amplifier NF model and ripple definition are described by a json file referenced with *"advanced_config_from_json"*: json filename. This json file contains:
- nf_fit_coeff: [a,b,c,d]
3rd order polynomial NF = f(-dg) coeficients list
dg = gain - gain_max
- f_min/max: amplifier frequency range in Hz
- gain_ripple : [...]
amplifier gain ripple excursion comb list in dB across the frequency range.
- nf_ripple : [...]
amplifier nf ripple excursion comb list in dB across the frequency range.
- dgt : [...]
amplifier dynamic gain tilt comb list across the frequency range.
*See next section for the generation of this json file*
.. code-block:: json-object
"Edfa":[{
"type_variety": "high_detail_model_example",
"type_def": "advanced_model",
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
"out_voa_auto": false,
"allowed_for_design": false
}
]
- *"variable_gain"*
This model is refered as an operator model because a lower level of knowledge is required. A full polynomial description of the NF cross the gain range is not required. Instead, NF_min and NF_max values are required and used by the code to model a dual stage amplifier with an internal mid stage VOA. NF_min and NF_max values are typically available from equipment suppliers data-sheet.
There is a default JSON file ”default_edfa_config.json”* to enforce 0 tilt and ripple values because GNpy core algorithm is a multi-carrier propogation.
- gain_ripple =[0,...,0]
- nf_ripple = [0,...,0]
- dgt = [...] generic dgt comb
.. code-block:: json-object
"Edfa":[{
"type_variety": "std_medium_gain",
"type_def": "variable_gain",
"gain_flatmax": 26,
"gain_min": 15,
"p_max": 23,
"nf_min": 6,
"nf_max": 10,
"out_voa_auto": false,
"allowed_for_design": true
}
]
- *"fixed_gain"*
This model is also an operator model with a single NF value that emulates basic single coil amplifiers without internal VOA.
if gain_min < gain < gain_max, NF == nf0
if gain < gain_min, the amplifier input is automatically padded, which results in
NF += gain_min - gain
.. code-block:: json-object
"Edfa":[{
"type_variety": "std_fixed_gain",
"type_def": "fixed_gain",
"gain_flatmax": 21,
"gain_min": 20,
"p_max": 21,
"nf0": 5.5,
"allowed_for_design": false
}
]
- *"openroadm"*
This model is a black box model replicating OpenRoadm MSA spec for ILA.
.. code-block:: json-object
"Edfa":[{
"type_variety": "low_noise",
"type_def": "openroadm",
"gain_flatmax": 27,
"gain_min": 12,
"p_max": 22,
"nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62],
"allowed_for_design": false
}
]
- *"dual_stage"*
This model allows the cascade (pre-defined combination) of any 2 amplifiers already described in the eqpt_config.json library.
- preamp_variety defines the 1st stge type variety
- booster variety defines the 2nd stage type variety
Both preamp and booster variety must exist in the eqpt libray
The resulting NF is the sum of the 2 amplifiers
The preamp is operated to its maximum gain
- gain_min indicates to auto_design when this dual_stage should be used
But unlike other models the 1st stage input will not be padded: it is always operated to its maximu gain and min NF. Therefore if gain adaptation and padding is needed it will be performed by the 2nd stage.
.. code-block:: json
{
"type_variety": "medium+low_gain",
"type_def": "dual_stage",
"gain_min": 25,
"preamp_variety": "std_medium_gain",
"booster_variety": "std_low_gain",
"allowed_for_design": true
}
4. advanced_config_from_json
#######################################
The build_oa_json.py library in gnpy/examples/edfa_model can be used to build the json file required for the amplifier advanced_model type_def:
Update an existing json file with all the 96ch txt files for a given amplifier type
amplifier type 'OA_type1' is hard coded but can be modified and other types added
returns an updated amplifier json file: output_json_file_name = 'edfa_config.json'
amplifier file names
Convert a set of amplifier files + input json definiton file into a valid edfa_json_file:
nf_fit_coeff: NF 3rd order polynomial coefficients txt file
nf = f(dg) with dg = gain_operational - gain_max
nf_ripple: NF ripple excursion txt file
gain_ripple: gain ripple txt file
dgt: dynamic gain txt file
input json file in argument (defult = 'OA.json')
the json input file should have the following fields:
.. code-block:: json
{
"nf_fit_coeff": "nf_filename.txt",
"nf_ripple": "nf_ripple_filename.txt",
"gain_ripple": "DFG_filename.txt",
"dgt": "DGT_filename.txt"
}

View File

@@ -4,7 +4,6 @@
Created on Tue Jan 30 12:32:00 2018
@author: jeanluc-auge
@comments about amplifier input files from Brian Taylor & Dave Boertjes
update an existing json file with all the 96ch txt files for a given amplifier type
amplifier type 'OA_type1' is hard coded but can be modified and other types added
@@ -18,46 +17,29 @@ from gnpy.core.utils import lin2db, db2lin
"""amplifier file names
convert a set of amplifier files + input json definiton file into a valid edfa_json_file:
nf_fit_coeff: NF polynomial coefficients txt file (optional)
nf_fit_coeff: NF 3rd order polynomial coefficients txt file
nf = f(dg)
with dg = gain_operational - gain_max
nf_ripple: NF ripple excursion txt file
dfg: gain txt file
gain_ripple: gain ripple txt file
dgt: dynamic gain txt file
input json file in argument (defult = 'OA.json')
the json input file should have the following fields:
{
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"nf_fit_coeff": "pNFfit3.txt",
"nf_ripple": "NFR_96.txt",
"dfg": "DFG_96.txt",
"dgt": "DGT_96.txt",
"nf_model":
{
"enabled": true,
"nf_min": 5.8,
"nf_max": 10
}
"nf_fit_coeff": "nf_filename.txt",
"nf_ripple": "nf_ripple_filename.txt",
"gain_ripple": "DFG_filename.txt",
"dgt": "DGT_filename.txt",
}
gain_flat = max flat gain (dB)
gain_min = min gain (dB) : will consider an input VOA if below (TBD vs throwing an exception)
p_max = max power (dBm)
nf_fit = boolean (True, False) :
if False nf_fit_coeff are ignored and nf_model fields are used
"""
input_json_file_name = "OA.json" #default path
output_json_file_name = "default_edfa_config.json"
param_field ="params"
gain_min_field = "gain_min"
gain_max_field = "gain_flatmax"
gain_ripple_field = "dfg"
gain_ripple_field = "gain_ripple"
nf_ripple_field = "nf_ripple"
nf_fit_coeff = "nf_fit_coeff"
nf_model_field = "nf_model"
nf_model_enabled_field = "enabled"
nf_min_field ="nf_min"
nf_max_field = "nf_max"
def read_file(field, file_name):
"""read and format the 96 channels txt files describing the amplifier NF and ripple
@@ -76,6 +58,7 @@ def read_file(field, file_name):
#consider ripple excursion only to avoid redundant information
#because the max flat_gain is already given by the 'gain_flat' field in json
#remove the mean component
print(file_name, ', mean value =', data.mean(), ' is substracted')
data = data - data.mean()
data = data.tolist()
return data

View File

@@ -1,313 +0,0 @@
{
"params": {
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"nf_fit_coeff": [
0.000168241,
0.0469961,
0.0359549,
5.82851
],
"nf_ripple": [
-0.3110761646066259,
-0.3110761646066259,
-0.31110274831665313,
-0.31419329378173544,
-0.3172854168606314,
-0.32037911876162584,
-0.3233255190215882,
-0.31624321721895354,
-0.30915729645781326,
-0.30206775396360075,
-0.2949045115165272,
-0.26632156113294336,
-0.23772399031437283,
-0.20911178784023846,
-0.18048410390821285,
-0.14379944379052215,
-0.10709599992470213,
-0.07037375788020579,
-0.03372858157230583,
-0.015660302006048,
0.0024172385953583004,
0.020504047353947653,
0.03860013139908377,
0.05670549786742816,
0.07482015390297145,
0.0838762040768461,
0.09284481475528361,
0.1018180306253394,
0.11079585523492333,
0.1020395478432815,
0.09310160456603413,
0.08415906712621996,
0.07521193198077789,
0.0676340601339394,
0.06005437964543287,
0.052470799141237305,
0.044883315610536455,
0.037679759069084225,
0.03047647598902483,
0.02326948274513522,
0.01605877647020772,
0.021248462316134083,
0.02657315875107553,
0.03190060058247842,
0.03723078993416436,
0.04256372893215024,
0.047899419704645264,
0.03915515813685565,
0.030289222542492025,
0.021418708618354456,
0.012573926129294415,
0.006240488799898697,
-9.622162373026585e-05,
-0.006436207679519103,
-0.012779471908040341,
-0.02038153550619876,
-0.027999803010447587,
-0.035622012697103154,
-0.043236398934156144,
-0.04493583574805963,
-0.04663615264317309,
-0.048337350303318156,
-0.050039429413028365,
-0.051742390657545205,
-0.05342028484370278,
-0.05254242298580185,
-0.05166410580536087,
-0.05078533294804249,
-0.04990610405914272,
-0.05409792133358102,
-0.05832916277634124,
-0.06256260169582961,
-0.06660356886269536,
-0.04779792991567815,
-0.028982516728038848,
-0.010157321677553965,
0.00861320615127981,
0.01913736978785662,
0.029667009055877668,
0.04020212822983975,
0.050742731588695494,
0.061288823415841555,
0.07184040799914815,
0.1043252636301016,
0.13687829834471027,
0.1694483010211072,
0.202035284929368,
0.23624619427167134,
0.27048596623174515,
0.30474360397422756,
0.3390191214858807,
0.36358851509924695,
0.38814205928193013,
0.41270842850729195,
0.4372876328262819,
0.4372876328262819
],
"dgt": [
2.714526681131686,
2.705443819238505,
2.6947834587664494,
2.6841217449620203,
2.6681935771243177,
2.6521732021128046,
2.630396440815385,
2.602860350286428,
2.5696460593920065,
2.5364027376452056,
2.499446286796604,
2.4587748041127506,
2.414398437185221,
2.3699990328716107,
2.322373696229342,
2.271520771371253,
2.2174389328192197,
2.16337565384239,
2.1183028432496016,
2.082225099873648,
2.055100772005235,
2.0279625371819305,
2.0008103857988204,
1.9736443063300082,
1.9482128147680253,
1.9245345552113182,
1.9026104247588487,
1.8806927939516411,
1.862235672444246,
1.847275503201129,
1.835814081380705,
1.824381436842932,
1.8139629377087627,
1.8045606557581335,
1.7961751115773796,
1.7877868031023945,
1.7793941781790852,
1.7709972329654864,
1.7625959636196327,
1.7541903672600494,
1.7459181197626403,
1.737780757913635,
1.7297783508684146,
1.7217732861435076,
1.7137640932265894,
1.7057507692361864,
1.6918150918099673,
1.6719047669939942,
1.6460167077689267,
1.6201194134191075,
1.5986915141218316,
1.5817353179379183,
1.569199764184379,
1.5566577309558969,
1.545374152761467,
1.5353620432989845,
1.5266220576235803,
1.5178910621476225,
1.5097346239790443,
1.502153039909686,
1.495145456062699,
1.488134243479226,
1.48111939735681,
1.474100442252211,
1.4670307626366115,
1.4599103316162523,
1.45273959485914,
1.445565137158368,
1.4340878115214444,
1.418273806730323,
1.3981208704326855,
1.3779439775587023,
1.3598972673004606,
1.3439818461440451,
1.3301807335621048,
1.316383926863083,
1.3040618749785347,
1.2932153453410835,
1.2838336236692311,
1.2744470198196236,
1.2650555289898042,
1.2556591482982988,
1.2428104897182262,
1.2264996957264114,
1.2067249615595257,
1.1869318618366975,
1.1672278304018044,
1.1476135933863398,
1.1280891949729075,
1.108555289615659,
1.0895983485572227,
1.0712204022764056,
1.0534217504465226,
1.0356155337864215,
1.017807767853702,
1.0
],
"nf_model": {
"enabled": true,
"nf1": 5.727887800964238,
"nf2": 7.727887800964238,
"delta_p": 5.238350271545567
},
"gain_ripple": [
0.1359703369791596,
0.11822862697916037,
0.09542181697916163,
0.06245819697916133,
0.02602813697916062,
-0.0036199830208403228,
-0.018326963020840026,
-0.0246928330208398,
-0.016792253020838643,
-0.0028138630208403015,
0.017572956979162058,
0.038328296979159404,
0.054956336979159914,
0.0670723869791594,
0.07091459697916136,
0.07094413697916124,
0.07114372697916238,
0.07533675697916209,
0.08731066697916035,
0.10313984697916112,
0.12276252697916235,
0.14239527697916188,
0.15945681697916214,
0.1739275269791598,
0.1767381569791624,
0.17037189697916233,
0.15216302697916007,
0.13114358697916018,
0.10802383697916085,
0.08548825697916129,
0.06916723697916183,
0.05848224697916038,
0.05447361697916264,
0.05154489697916276,
0.04946107697915991,
0.04717897697916129,
0.04551704697916037,
0.04467697697916151,
0.04072968697916224,
0.03285456697916089,
0.023488786979161347,
0.01659282697915998,
0.013321846979160057,
0.011234826979162449,
0.01030063697916006,
0.00936596697916059,
0.00874012697916271,
0.00842583697916055,
0.006965146979162284,
0.0040435869791615175,
0.0007104669791608842,
-0.0015763130208377163,
-0.006936193020838033,
-0.016475303020840215,
-0.028748483020837767,
-0.039618433020837784,
-0.051112303020840244,
-0.06468462302083822,
-0.07868024302083754,
-0.09101254302083817,
-0.10103437302083762,
-0.11041488302083735,
-0.11916081302083725,
-0.12789859302083784,
-0.1353792530208402,
-0.14160178302083892,
-0.1455411330208385,
-0.1484450830208388,
-0.14823350302084037,
-0.14591937302083835,
-0.1409032730208395,
-0.13525493302083902,
-0.1279646530208396,
-0.11963431302083904,
-0.11089282302084058,
-0.1027863830208382,
-0.09717347302083823,
-0.09343261302083761,
-0.0913487130208388,
-0.08906007302083907,
-0.0865687230208394,
-0.08407607302083875,
-0.07844600302084004,
-0.06968090302083851,
-0.05947139302083926,
-0.05095282302083959,
-0.042428283020839785,
-0.03218106302083967,
-0.01819858302084043,
-0.0021726530208390216,
0.01393231697916164,
0.028098946979159933,
0.040326236979161934,
0.05257029697916238,
0.06479749697916048,
0.07704745697916238
]
}
}

View File

@@ -1,12 +1,22 @@
{ "Edfa":[{
"type_variety": "high_detail_model_example",
"type_def": "advanced_model",
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
"out_voa_auto": false,
"allowed_for_design": false
},
}, {
"type_variety": "Juniper_BoosterHG",
"type_def": "advanced_model",
"gain_flatmax": 25,
"gain_min": 10,
"p_max": 21,
"advanced_config_from_json": "Juniper-BoosterHG.json",
"out_voa_auto": false,
"allowed_for_design": false
},
{
"type_variety": "operator_model_example",
"type_def": "variable_gain",
@@ -19,6 +29,35 @@
"allowed_for_design": false
},
{
"type_variety": "low_noise",
"type_def": "openroadm",
"gain_flatmax": 27,
"gain_min": 12,
"p_max": 22,
"nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62],
"allowed_for_design": false
},
{
"type_variety": "standard",
"type_def": "openroadm",
"gain_flatmax": 27,
"gain_min": 12,
"p_max": 22,
"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,
"gain_min": 25,
"p_max": 21,
"nf_min": 5.5,
"nf_max": 7,
"out_voa_auto": false,
"allowed_for_design": true
},
{
"type_variety": "std_medium_gain",
"type_def": "variable_gain",
"gain_flatmax": 26,
@@ -41,6 +80,17 @@
"allowed_for_design": true
},
{
"type_variety": "high_power",
"type_def": "variable_gain",
"gain_flatmax": 16,
"gain_min": 8,
"p_max": 25,
"nf_min": 9,
"nf_max": 15,
"out_voa_auto": false,
"allowed_for_design": false
},
{
"type_variety": "std_fixed_gain",
"type_def": "fixed_gain",
"gain_flatmax": 21,
@@ -50,25 +100,100 @@
"allowed_for_design": false
},
{
"type_variety": "test",
"type_def": "variable_gain",
"gain_flatmax": 25,
"gain_min": 15,
"type_variety": "4pumps_raman",
"type_def": "fixed_gain",
"gain_flatmax": 12,
"gain_min": 12,
"p_max": 21,
"nf_min": 7,
"nf_max": 11,
"nf0": -1,
"allowed_for_design": false
}
},
{
"type_variety": "hybrid_4pumps_lowgain",
"type_def": "dual_stage",
"raman": true,
"gain_min": 25,
"preamp_variety": "4pumps_raman",
"booster_variety": "std_low_gain",
"allowed_for_design": true
},
{
"type_variety": "hybrid_4pumps_mediumgain",
"type_def": "dual_stage",
"raman": true,
"gain_min": 25,
"preamp_variety": "4pumps_raman",
"booster_variety": "std_medium_gain",
"allowed_for_design": true
},
{
"type_variety": "medium+low_gain",
"type_def": "dual_stage",
"gain_min": 25,
"preamp_variety": "std_medium_gain",
"booster_variety": "std_low_gain",
"allowed_for_design": true
},
{
"type_variety": "medium+high_power",
"type_def": "dual_stage",
"gain_min": 25,
"preamp_variety": "std_medium_gain",
"booster_variety": "high_power",
"allowed_for_design": false
}
],
"Fiber":[{
"type_variety": "SSMF",
"dispersion": 1.67e-05,
"gamma": 0.00127
},
{
"type_variety": "NZDF",
"dispersion": 0.5e-05,
"gamma": 0.00146
},
{
"type_variety": "LOF",
"dispersion": 2.2e-05,
"gamma": 0.000843
}
],
"Spans":[{
"RamanFiber":[{
"type_variety": "SSMF",
"dispersion": 1.67e-05,
"gamma": 0.00127,
"raman_efficiency": {
"cr":[
0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119,
0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469,
0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826,
0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05,
7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05,
2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05,
2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05,
1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05,
1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07,
2E-07, 1E-07
],
"frequency_offset":[
0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12,
7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12,
13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12,
17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12,
22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12,
28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12,
35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12,
41.5e12, 42e12
]
}
}
],
"Span":[{
"power_mode":true,
"delta_power_range_db": [0,0,0.5],
"delta_power_range_db": [-2,3,0.5],
"max_fiber_lineic_loss_for_raman": 0.25,
"target_extended_gain": 2.5,
"max_length": 150,
"length_units": "km",
"max_loss": 28,
@@ -78,9 +203,13 @@
"con_out": 0
}
],
"Roadms":[{
"gain_mode_default_loss": 20,
"power_mode_pref": -20
"Roadm":[{
"target_pch_out_db": -20,
"add_drop_osnr": 38,
"restrictions": {
"preamp_variety_list":[],
"booster_variety_list":[]
}
}],
"SI":[{
"f_min": 191.3e12,
@@ -88,10 +217,10 @@
"f_max":195.1e12,
"spacing": 50e9,
"power_dbm": 0,
"power_range_db": [0,0,0.5],
"power_range_db": [0,0,1],
"roll_off": 0.15,
"OSNR": 11,
"bit_rate":100e9
"tx_osnr": 40,
"sys_margins": 2
}],
"Transceiver":[
{
@@ -107,28 +236,20 @@
"baud_rate": 32e9,
"OSNR": 11,
"bit_rate": 100e9,
"roll_off": 0.15
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 37.5e9,
"cost":1
},
{
"format": "mode 2",
"baud_rate": 66e9,
"OSNR": 15,
"bit_rate": 200e9,
"roll_off": 0.15
},
{
"format": "PS_SP64_1",
"baud_rate": 32e9,
"OSNR": 11,
"bit_rate": 100e9,
"roll_off": 0.15
},
{
"format": "mode_2",
"baud_rate": 66e9,
"OSNR": 15,
"bit_rate": 200e9,
"roll_off": 0.15
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 75e9,
"cost":1
}
]
},
@@ -140,44 +261,45 @@
},
"mode":[
{
"format": "16QAM",
"baud_rate": 32e9,
"OSNR": 19,
"bit_rate": 200e9,
"roll_off": 0.15
},
{
"format": "QPSK",
"format": "mode 1",
"baud_rate": 32e9,
"OSNR": 12,
"bit_rate": 100e9,
"roll_off": 0.15
}
]
},
{
"type_variety": "Voyager_16QAM",
"frequency":{
"min": 191.35e12,
"max": 196.1e12
},
"mode":[
{
"format": "16QAM",
"baud_rate": 32e9,
"OSNR": 19,
"bit_rate": 200e9,
"roll_off": 0.15
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 37.5e9,
"cost":1
},
{
"format": "QPSK",
"baud_rate": 32e9,
"OSNR": 12,
"bit_rate": 100e9,
"roll_off": 0.15
"format": "mode 3",
"baud_rate": 44e9,
"OSNR": 18,
"bit_rate": 300e9,
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 62.5e9,
"cost":1
},
{
"format": "mode 2",
"baud_rate": 66e9,
"OSNR": 21,
"bit_rate": 400e9,
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 75e9,
"cost":1
},
{
"format": "mode 4",
"baud_rate": 66e9,
"OSNR": 16,
"bit_rate": 200e9,
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 75e9,
"cost":1
}
]
}
]

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -1,165 +1,268 @@
{
"path-request": [
{
"request-id": 0,
"source": "Lorient_KMA",
"destination": "Vannes_KBE",
"request-id": "0",
"source": "trx Lorient_KMA",
"destination": "trx Vannes_KBE",
"src-tp-id": "trx Lorient_KMA",
"dst-tp-id": "trx Vannes_KBE",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"trx_type": "Voyager",
"trx_mode": null,
"effective-freq-slot": [
{
"n": "null",
"m": "null"
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
"output-power": 0.0012589254117941673,
"path_bandwidth": 100000000000.0
}
},
"optimizations": {
"explicit-route-include-objects": []
}
},
{
"request-id": 1,
"source": "Brest_KLA",
"destination": "Vannes_KBE",
"request-id": "1",
"source": "trx Brest_KLA",
"destination": "trx Vannes_KBE",
"src-tp-id": "trx Brest_KLA",
"dst-tp-id": "trx Vannes_KBE",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
"max-nb-of-channel": null,
"output-power": 0.0012589254117941673,
"path_bandwidth": 200000000000.0
}
},
"optimizations": {
"explicit-route-include-objects": [
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": "route-include-ero",
"index": 0,
"unnumbered-hop": {
"node-id": "Lannion_CAS",
"num-unnum-hop": {
"node-id": "roadm Brest_KLA",
"link-tp-id": "link-tp-id is not used",
"hop-type": "loose",
"direction": "direction is not used"
},
"label-hop": {
"te-label": {
"generic": "generic is not used",
"direction": "direction is not used"
}
"hop-type": "LOOSE"
}
},
{
"explicit-route-usage": "route-include-ero",
"index": 1,
"unnumbered-hop": {
"node-id": "Lorient_KMA",
"num-unnum-hop": {
"node-id": "roadm Lannion_CAS",
"link-tp-id": "link-tp-id is not used",
"hop-type": "loose",
"direction": "direction is not used"
},
"label-hop": {
"te-label": {
"generic": "generic is not used",
"direction": "direction is not used"
}
"hop-type": "LOOSE"
}
},
{
"explicit-route-usage": "route-include-ero",
"index": 2,
"num-unnum-hop": {
"node-id": "roadm Lorient_KMA",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
},
{
"explicit-route-usage": "route-include-ero",
"index": 3,
"num-unnum-hop": {
"node-id": "roadm Vannes_KBE",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
}
]
}
},
{
"request-id": 3,
"source": "Lannion_CAS",
"destination": "Rennes_STA",
"request-id": "3",
"source": "trx Lannion_CAS",
"destination": "trx Rennes_STA",
"src-tp-id": "trx Lannion_CAS",
"dst-tp-id": "trx Rennes_STA",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 60000000000.0
}
},
"optimizations": {
"explicit-route-include-objects": []
}
},
{
"request-id": 4,
"source": "Rennes_STA",
"destination": "Lannion_CAS",
"request-id": "4",
"source": "trx Rennes_STA",
"destination": "trx Lannion_CAS",
"src-tp-id": "trx Rennes_STA",
"dst-tp-id": "trx Lannion_CAS",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"trx_mode": null,
"effective-freq-slot": [
{
"n": "null",
"m": "null"
"N": "null",
"M": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": 64,
"output-power": 0.0019952623149688794
"max-nb-of-channel": null,
"output-power": 0.0019952623149688794,
"path_bandwidth": 150000000000.0
}
}
},
{
"request-id": "5",
"source": "trx Rennes_STA",
"destination": "trx Lannion_CAS",
"src-tp-id": "trx Rennes_STA",
"dst-tp-id": "trx Lannion_CAS",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "mode 2",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": 63,
"output-power": 0.0019952623149688794,
"path_bandwidth": 20000000000.0
}
}
},
{
"request-id": "6",
"source": "trx Lannion_CAS",
"destination": "trx Lorient_KMA",
"src-tp-id": "trx Lannion_CAS",
"dst-tp-id": "trx Lorient_KMA",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 76,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "7",
"source": "trx Lannion_CAS",
"destination": "trx Lorient_KMA",
"src-tp-id": "trx Lannion_CAS",
"dst-tp-id": "trx Lorient_KMA",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 76,
"output-power": 0.001,
"path_bandwidth": 400000000000.0
}
}
},
{
"request-id": "7b",
"source": "trx Lannion_CAS",
"destination": "trx Lorient_KMA",
"src-tp-id": "trx Lannion_CAS",
"dst-tp-id": "trx Lorient_KMA",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": 50,
"output-power": 0.001,
"path_bandwidth": 400000000000.0
}
},
"optimizations": {
"explicit-route-include-objects": []
}
}
],
"synchronisation": [
"synchronization": [
{
"synchonization-id": 0,
"synchronization-id": "3",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"relaxable": "false",
"disjointness": "node link",
"request-id-number": [
0,
0
"3",
"1"
]
}
},
{
"synchonization-id": 3,
"synchronization-id": "4",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"relaxable": "false",
"disjointness": "node link",
"request-id-number": [
3,
4
"4",
"5"
]
}
}
]
}
}

Binary file not shown.

View File

@@ -18,148 +18,552 @@ from pathlib import Path
from collections import namedtuple
from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO
from json import dumps, loads
from networkx import (draw_networkx_nodes, draw_networkx_edges,
draw_networkx_labels, dijkstra_path, NetworkXNoPath)
from numpy import mean
from examples.convert_service_sheet import convert_service_sheet, Request_element, Element
from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element
from gnpy.core.utils import load_json
from gnpy.core.network import load_network, build_network, set_roadm_loss
from gnpy.core.equipment import load_equipment, trx_mode_params
from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused
from gnpy.core.network import load_network, build_network, save_network, network_from_json
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch
from gnpy.core.elements import Transceiver, Roadm
from gnpy.core.utils import db2lin, lin2db
from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate, jsontocsv
from gnpy.core.request import (Path_request, Result_element,
propagate, jsontocsv, Disjunction, compute_path_dsjctn,
requests_aggregation, propagate_and_optimize_mode,
BLOCKING_NOPATH, BLOCKING_NOMODE,
find_reversed_path)
from gnpy.core.exceptions import (ConfigurationError, EquipmentConfigError, NetworkTopologyError,
ServiceError, DisjunctionError)
import gnpy.core.ansi_escapes as ansi_escapes
from gnpy.core.spectrum_assignment import (build_oms_list, pth_assign_spectrum)
from copy import copy, deepcopy
from textwrap import dedent
from math import ceil
from flask import Flask, jsonify, make_response, request
from flask_restful import Api, Resource, reqparse, fields
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
logger = getLogger(__name__)
LOGGER = getLogger(__name__)
parser = ArgumentParser(description = 'A function that computes performances for a list of services provided in a json file or an excel sheet.')
parser.add_argument('network_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls')
parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls')
parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json')
parser.add_argument('-v', '--verbose', action='count')
parser.add_argument('-o', '--output', default=None)
PARSER = ArgumentParser(description='A function that computes performances for a list of ' +
'services provided in a json file or an excel sheet.')
PARSER.add_argument('network_filename', nargs='?', type=Path,\
default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\
help='input topology file in xls or json')
PARSER.add_argument('service_filename', nargs='?', type=Path,\
default=Path(__file__).parent / 'meshTopologyExampleV2.xls',\
help='input service file in xls or json')
PARSER.add_argument('eqpt_filename', nargs='?', type=Path,\
default=Path(__file__).parent / 'eqpt_config.json',\
help='input equipment library in json. Default is eqpt_config.json')
PARSER.add_argument('-bi', '--bidir', action='store_true',\
help='considers that all demands are bidir')
PARSER.add_argument('-v', '--verbose', action='count', default=0,\
help='increases verbosity for each occurence')
PARSER.add_argument('-o', '--output', type=Path)
PARSER.add_argument('-r', '--rest', action='count', default=0, help='use the REST API')
NETWORK_FILENAME = 'topoDemov1.json' #'disagregatedTopoDemov1.json' #
def requests_from_json(json_data,equipment):
APP = Flask(__name__, static_url_path="")
API = Api(APP)
def requests_from_json(json_data, equipment):
""" converts the json data into a list of requests elements
"""
requests_list = []
for req in json_data['path-request']:
#print(f'{req}')
# init all params from request
params = {}
params['request_id'] = req['request-id']
params['source'] = req['src-tp-id']
params['destination'] = req['dst-tp-id']
params['source'] = req['source']
params['bidir'] = req['bidirectional']
params['destination'] = req['destination']
params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type']
params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode']
params['format'] = params['trx_mode']
nd_list = req['optimizations']['explicit-route-include-objects']
params['nodes_list'] = [n['unnumbered-hop']['node-id'] for n in nd_list]
params['loose_list'] = [n['unnumbered-hop']['hop-type'] for n in nd_list]
params['spacing'] = req['path-constraints']['te-bandwidth']['spacing']
trx_params = trx_mode_params(equipment,params['trx_type'],params['trx_mode'],True)
try:
nd_list = req['explicit-route-objects']['route-object-include-exclude']
except KeyError:
nd_list = []
params['nodes_list'] = [n['num-unnum-hop']['node-id'] for n in nd_list]
params['loose_list'] = [n['num-unnum-hop']['hop-type'] for n in nd_list]
# recover trx physical param (baudrate, ...) from type and mode
# in trx_mode_params optical power is read from equipment['SI']['default'] and
# nb_channel is computed based on min max frequency and spacing
trx_params = trx_mode_params(equipment, params['trx_type'], params['trx_mode'], True)
params.update(trx_params)
params['power'] = req['path-constraints']['te-bandwidth']['output-power']
params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel']
# print(trx_params['min_spacing'])
# optical power might be set differently in the request. if it is indicated then the
# params['power'] is updated
try:
if req['path-constraints']['te-bandwidth']['output-power']:
params['power'] = req['path-constraints']['te-bandwidth']['output-power']
except KeyError:
pass
# same process for nb-channel
f_min = params['f_min']
f_max_from_si = params['f_max']
try:
if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None:
nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel']
params['nb_channel'] = nch
spacing = params['spacing']
params['f_max'] = f_min + nch*spacing
else:
params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
except KeyError:
params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
consistency_check(params, f_max_from_si)
try:
params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth']
except KeyError:
pass
requests_list.append(Path_request(**params))
return requests_list
def consistency_check(params, f_max_from_si):
""" checks that the requested parameters are consistant (spacing vs nb channel,
vs transponder mode...)
"""
f_min = params['f_min']
f_max = params['f_max']
max_recommanded_nb_channels = automatic_nch(f_min, f_max, params['spacing'])
if params['baud_rate'] is not None:
#implicitely means that a mode is defined with min_spacing
if params['min_spacing'] > params['spacing']:
msg = f'Request {params["request_id"]} has spacing below transponder ' +\
f'{params["trx_type"]} {params["trx_mode"]} min spacing value ' +\
f'{params["min_spacing"]*1e-9}GHz.\nComputation stopped'
print(msg)
LOGGER.critical(msg)
raise ServiceError(msg)
if f_max > f_max_from_si:
msg = dedent(f'''
Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz
is not consistent with frequency range {f_min*1e-12} THz, {f_max*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz.
max recommanded nb of channels is {max_recommanded_nb_channels}
Computation stopped.''')
LOGGER.critical(msg)
raise ServiceError(msg)
def load_requests(filename,eqpt_filename):
def disjunctions_from_json(json_data):
""" reads the disjunction requests from the json dict and create the list
of requested disjunctions for this set of requests
"""
disjunctions_list = []
try:
temp_test = json_data['synchronization']
except KeyError:
temp_test = []
if temp_test:
for snc in json_data['synchronization']:
params = {}
params['disjunction_id'] = snc['synchronization-id']
params['relaxable'] = snc['svec']['relaxable']
params['link_diverse'] = 'link' in snc['svec']['disjointness']
params['node_diverse'] = 'node' in snc['svec']['disjointness']
params['disjunctions_req'] = snc['svec']['request-id-number']
disjunctions_list.append(Disjunction(**params))
return disjunctions_list
def load_requests(filename, eqpt_filename, bidir):
""" loads the requests from a json or an excel file into a data string
"""
if filename.suffix.lower() == '.xls':
logger.info('Automatically converting requests from XLS to JSON')
json_data = convert_service_sheet(filename,eqpt_filename)
LOGGER.info('Automatically converting requests from XLS to JSON')
try:
json_data = convert_service_sheet(filename, eqpt_filename, bidir=bidir)
except ServiceError as this_e:
print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
exit(1)
else:
with open(filename) as f:
json_data = loads(f.read())
with open(filename, encoding='utf-8') as my_f:
json_data = loads(my_f.read())
return json_data
def compute_path(network, equipment, pathreqlist):
def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist):
""" use a list but a dictionnary might be helpful to find path based on request_id
TODO change all these req, dsjct, res lists into dict !
"""
path_res_list = []
reversed_path_res_list = []
propagated_reversed_path_res_list = []
for pathreq in pathreqlist:
#need to rebuid the network for each path because the total power
#can be different and the choice of amplifiers in autodesign is power dependant
#but the design is the same if the total power is the same
#TODO parametrize the total spectrum power so the same design can be shared
for i, pathreq in enumerate(pathreqlist):
# use the power specified in requests but might be different from the one
# specified for design the power is an optional parameter for requests
# definition if optional, use the one defines in eqt_config.json
p_db = lin2db(pathreq.power*1e3)
p_total_db = p_db + lin2db(pathreq.nb_channel)
build_network(network, equipment, p_db, p_total_db)
pathreq.nodes_list.append(pathreq.destination)
#we assume that the destination is a strict constraint
pathreq.loose_list.append('strict')
print(f'request {pathreq.request_id}')
print(f'Computing path from {pathreq.source} to {pathreq.destination}')
print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output
total_path = compute_constrained_path(network, pathreq)
print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}\n')
# adding first node to be clearer on the output
print(f'with path constraint: {[pathreq.source] + pathreq.nodes_list}')
# pathlist[i] contains the whole path information for request i
# last element is a transciver and where the result of the propagation is
# recorded.
# Important Note: since transceivers attached to roadms are actually logical
# elements to simulate performance, several demands having the same destination
# may use the same transponder for the performance simulation. This is why
# we use deepcopy: to ensure that each propagation is recorded and not overwritten
total_path = deepcopy(pathlist[i])
print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}')
# for debug
# print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}')
if total_path :
total_path = propagate(total_path,pathreq,equipment, show=False)
else:
total_path = []
# we record the last tranceiver object in order to have th whole
# information about spectrum. Important Note: since transceivers
# attached to roadms are actually logical elements to simulate
# performance, several demands having the same destination may use
# the same transponder for the performance simaulation. This is why
# we use deepcopy: to ensure each propagation is recorded and not
# overwritten
if total_path:
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)
temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))), 2)
if temp_snr01nm < pathreq.OSNR:
msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
f' {pathreq.source} to {pathreq.destination} does not pass with' +\
f' {pathreq.tsp_mode}\n\tcomputedSNR in 0.1nm = {temp_snr01nm} ' +\
f'- required osnr {pathreq.OSNR}'
print(msg)
LOGGER.warning(msg)
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
else:
total_path, mode = propagate_and_optimize_mode(total_path, pathreq, equipment)
# if no baudrate satisfies spacing, no mode is returned and the last explored mode
# a warning is shown in the propagate_and_optimize_mode
# propagate_and_optimize_mode function returns the mode with the highest bitrate
# that passes. if no mode passes, then a attribute blocking_reason is added on
# pathreq that contains the reason for blocking: 'NO_PATH', 'NO_FEASIBLE_MODE', ...
try:
if pathreq.blocking_reason in BLOCKING_NOPATH:
total_path = []
elif pathreq.blocking_reason in BLOCKING_NOMODE:
pathreq.baud_rate = mode['baud_rate']
pathreq.tsp_mode = mode['format']
pathreq.format = mode['format']
pathreq.OSNR = mode['OSNR']
pathreq.tx_osnr = mode['tx_osnr']
pathreq.bit_rate = mode['bit_rate']
# other blocking reason should not appear at this point
except AttributeError:
pathreq.baud_rate = mode['baud_rate']
pathreq.tsp_mode = mode['format']
pathreq.format = mode['format']
pathreq.OSNR = mode['OSNR']
pathreq.tx_osnr = mode['tx_osnr']
pathreq.bit_rate = mode['bit_rate']
# reversed path is needed for correct spectrum assignment
reversed_path = find_reversed_path(pathlist[i])
if pathreq.bidir:
# only propagate if bidir is true, but needs the reversed path anyway for
# correct spectrum assignment
rev_p = deepcopy(reversed_path)
print(f'\n\tPropagating Z to A direction {pathreq.destination} to {pathreq.source}')
print(f'\tPath (roadsm) {[r.uid for r in rev_p if isinstance(r,Roadm)]}\n')
propagated_reversed_path = propagate(rev_p, pathreq, equipment)
temp_snr01nm = round(mean(propagated_reversed_path[-1].snr +\
lin2db(pathreq.baud_rate/(12.5e9))), 2)
if temp_snr01nm < pathreq.OSNR:
msg = f'\tWarning! Request {pathreq.request_id} computed path from' +\
f' {pathreq.source} to {pathreq.destination} does not pass with' +\
f' {pathreq.tsp_mode}\n' +\
f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}'
print(msg)
LOGGER.warning(msg)
# TODO selection of mode should also be on reversed direction !!
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
else:
propagated_reversed_path = []
else:
msg = 'Total path is empty. No propagation'
print(msg)
LOGGER.info(msg)
reversed_path = []
propagated_reversed_path = []
path_res_list.append(total_path)
reversed_path_res_list.append(reversed_path)
propagated_reversed_path_res_list.append(propagated_reversed_path)
# print to have a nice output
print('')
return path_res_list, reversed_path_res_list, propagated_reversed_path_res_list
def correct_route_list(network, pathreqlist):
""" prepares the format of route list of nodes to be consistant
remove wrong names, remove endpoints
also correct source and destination
"""
anytype = [n.uid for n in network.nodes()]
# TODO there is a problem of identification of fibers in case of parallel fibers
# between two adjacent roadms so fiber constraint is not supported
transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)]
for pathreq in pathreqlist:
for i, n_id in enumerate(pathreq.nodes_list):
# replace possibly wrong name with a formated roadm name
# print(n_id)
if n_id not in anytype:
# find nodes name that include constraint among all possible names except
# transponders (not yet supported as constraints).
nodes_suggestion = [uid for uid in anytype \
if n_id.lower() in uid.lower() and uid not in transponders]
if pathreq.loose_list[i] == 'LOOSE':
if len(nodes_suggestion) > 0:
new_n = nodes_suggestion[0]
print(f'invalid route node specified:\
\n\'{n_id}\', replaced with \'{new_n}\'')
pathreq.nodes_list[i] = new_n
else:
print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\',' +\
f' could not use it as constraint, skipped!'+'\x1b[0m')
pathreq.nodes_list.remove(n_id)
pathreq.loose_list.pop(i)
else:
msg = f'\x1b[1;33;40m'+f'could not find node: {n_id} in network topology.' +\
f' Strict constraint can not be applied.' + '\x1b[0m'
LOGGER.critical(msg)
raise ValueError(msg)
if pathreq.source not in transponders:
msg = f'\x1b[1;31;40m' + f'Request: {pathreq.request_id}: could not find' +\
f' transponder source: {pathreq.source}.'+'\x1b[0m'
LOGGER.critical(msg)
print(f'{msg}\nComputation stopped.')
raise ServiceError(msg)
if pathreq.destination not in transponders:
msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find' +\
f' transponder destination: {pathreq.destination}.'+'\x1b[0m'
LOGGER.critical(msg)
print(f'{msg}\nComputation stopped.')
raise ServiceError(msg)
# TODO remove endpoints from this list in case they were added by the user
# in the xls or json files
return pathreqlist
def correct_disjn(disjn):
""" clean disjunctions to remove possible repetition
"""
local_disjn = disjn.copy()
for elem in local_disjn:
for dis_elem in local_disjn:
if set(elem.disjunctions_req) == set(dis_elem.disjunctions_req) and\
elem.disjunction_id != dis_elem.disjunction_id:
local_disjn.remove(dis_elem)
return local_disjn
path_res_list.append(deepcopy(total_path))
return path_res_list
def path_result_json(pathresult):
""" create the response dictionnary
"""
data = {
'path': [n.json for n in pathresult]
'response': [n.json for n in pathresult]
}
return data
def compute_requests(network, data, equipment):
""" Main program calling functions
"""
# Build the network once using the default power defined in SI in eqpt config
# TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
# spacing, f_min and f_max
p_db = equipment['SI']['default'].power_dbm
if __name__ == '__main__':
args = parser.parse_args()
basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL))
logger.info(f'Computing path requests {args.service_filename} into JSON format')
# for debug
# print( args.eqpt_filename)
data = load_requests(args.service_filename,args.eqpt_filename)
equipment = load_equipment(args.eqpt_filename)
network = load_network(args.network_filename,equipment)
pths = requests_from_json(data, equipment)
print(pths)
test = compute_path(network, equipment, pths)
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
build_network(network, equipment, p_db, p_total_db)
save_network(ARGS.network_filename, network)
#TODO write results
oms_list = build_oms_list(network, equipment)
header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR']
try:
rqs = requests_from_json(data, equipment)
except ServiceError as this_e:
print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
raise this_e
# check that request ids are unique. Non unique ids, may
# mess the computation: better to stop the computation
all_ids = [r.request_id for r in rqs]
if len(all_ids) != len(set(all_ids)):
for item in list(set(all_ids)):
all_ids.remove(item)
msg = f'Requests id {all_ids} are not unique'
LOGGER.critical(msg)
raise ServiceError(msg)
try:
rqs = correct_route_list(network, rqs)
except ServiceError as this_e:
print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
raise this_e
#exit(1)
# pths = compute_path(network, equipment, rqs)
dsjn = disjunctions_from_json(data)
print('\x1b[1;34;40m' + f'List of disjunctions' + '\x1b[0m')
print(dsjn)
# need to warn or correct in case of wrong disjunction form
# disjunction must not be repeated with same or different ids
dsjn = correct_disjn(dsjn)
# Aggregate demands with same exact constraints
print('\x1b[1;34;40m' + f'Aggregating similar requests' + '\x1b[0m')
rqs, dsjn = requests_aggregation(rqs, dsjn)
# TODO export novel set of aggregated demands in a json file
print('\x1b[1;34;40m' + 'The following services have been requested:' + '\x1b[0m')
print(rqs)
print('\x1b[1;34;40m' + f'Computing all paths with constraints' + '\x1b[0m')
try:
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
except DisjunctionError as this_e:
print(f'{ansi_escapes.red}Disjunction error:{ansi_escapes.reset} {this_e}')
raise this_e
print('\x1b[1;34;40m' + f'Propagating on selected path' + '\x1b[0m')
propagatedpths, reversed_pths, reversed_propagatedpths = \
compute_path_with_disjunction(network, equipment, rqs, pths)
# Note that deepcopy used in compute_path_with_disjunction returns
# a list of nodes which are not belonging to network (they are copies of the node objects).
# so there can not be propagation on these nodes.
pth_assign_spectrum(pths, rqs, oms_list, reversed_pths)
print('\x1b[1;34;40m'+f'Result summary'+ '\x1b[0m')
header = ['req id', ' demand', ' snr@bandwidth A-Z (Z-A)', ' snr@0.1nm A-Z (Z-A)',\
' Receiver minOSNR', ' mode', ' Gbit/s', ' nb of tsp pairs',\
'N,M or blocking reason']
data = []
data.append(header)
for i, p in enumerate(test):
if p:
line = [f'{pths[i].source} to {pths[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\
f'{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}',\
f'{pths[i].OSNR}']
else:
line = [f'no path from {pths[i].source} to {pths[i].destination} ']
for i, this_p in enumerate(propagatedpths):
rev_pth = reversed_propagatedpths[i]
if rev_pth and this_p:
psnrb = f'{round(mean(this_p[-1].snr),2)} ({round(mean(rev_pth[-1].snr),2)})'
psnr = f'{round(mean(this_p[-1].snr_01nm), 2)}' +\
f' ({round(mean(rev_pth[-1].snr_01nm),2)})'
elif this_p:
psnrb = f'{round(mean(this_p[-1].snr),2)}'
psnr = f'{round(mean(this_p[-1].snr_01nm),2)}'
try :
if rqs[i].blocking_reason in BLOCKING_NOPATH:
line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} :',\
f'-', f'-', f'-', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9,2)}',\
f'-', f'{rqs[i].blocking_reason}']
else:
line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', psnrb,\
psnr, f'-', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9, 2)}',\
f'-', f'{rqs[i].blocking_reason}']
except AttributeError:
line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', psnrb,\
psnr, f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}', f'{round(rqs[i].path_bandwidth * 1e-9,2)}',\
f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }', f'({rqs[i].N},{rqs[i].M})']
data.append(line)
col_width = max(len(word) for row in data for word in row) # padding
col_width = max(len(word) for row in data for word in row[2:]) # padding
firstcol_width = max(len(row[0]) for row in data) # padding
secondcol_width = max(len(row[1]) for row in data) # padding
for row in data:
print(''.join(word.ljust(col_width) for word in row))
firstcol = ''.join(row[0].ljust(firstcol_width))
secondcol = ''.join(row[1].ljust(secondcol_width))
remainingcols = ''.join(word.center(col_width, ' ') for word in row[2:])
print(f'{firstcol} {secondcol} {remainingcols}')
print('\x1b[1;33;40m'+f'Result summary shows mean SNR and OSNR (average over all channels)' +\
'\x1b[0m')
return propagatedpths, reversed_propagatedpths, rqs
if args.output :
def launch_cli(network, data, equipment):
""" Compute requests using network, data and equipment with client line interface
"""
propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment)
#Generate the output
if ARGS.output :
result = []
for p in test:
result.append(Result_element(pths[test.index(p)],p))
with open(args.output, 'w') as f:
f.write(dumps(path_result_json(result), indent=2))
fnamecsv = next(s for s in args.output.split('.')) + '.csv'
with open(fnamecsv,"w") as fcsv :
jsontocsv(path_result_json(result),equipment,fcsv)
# assumes that list of rqs and list of propgatedpths have same order
for i, pth in enumerate(propagatedpths):
result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i]))
temp = path_result_json(result)
fnamecsv = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.csv'
fnamejson = f'{str(ARGS.output)[0:len(str(ARGS.output))-len(str(ARGS.output.suffix))]}.json'
with open(fnamejson, 'w', encoding='utf-8') as fjson:
fjson.write(dumps(path_result_json(result), indent=2, ensure_ascii=False))
with open(fnamecsv, "w", encoding='utf-8') as fcsv:
jsontocsv(temp, equipment, fcsv)
print('\x1b[1;34;40m'+f'saving in {ARGS.output} and {fnamecsv}'+ '\x1b[0m')
class GnpyAPI(Resource):
""" Compute requests using network, data and equipment with rest api
"""
def get(self):
return {"ping": True}, 200
def post(self):
data = request.get_json()
equipment = load_equipment('examples/2019-demo-equipment.json')
topo_json = load_json('examples/2019-demo-topology.json')
network = network_from_json(topo_json, equipment)
try:
propagatedpths, reversed_propagatedpths, rqs = compute_requests(network, data, equipment)
# Generate the output
result = []
#assumes that list of rqs and list of propgatedpths have same order
for i, pth in enumerate(propagatedpths):
result.append(Result_element(rqs[i], pth, reversed_propagatedpths[i]))
return {"result":path_result_json(result)}, 201
except ServiceError as this_e:
msg = f'Service error: {this_e}'
return {"result": msg}, 400
API.add_resource(GnpyAPI, '/gnpy-experimental')
def main(args):
""" main function that calls all functions
"""
LOGGER.info(f'Computing path requests {args.service_filename} into JSON format')
print('\x1b[1;34;40m' +\
f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m')
# for debug
# print( args.eqpt_filename)
try:
data = load_requests(args.service_filename, args.eqpt_filename, args.bidir)
equipment = load_equipment(args.eqpt_filename)
network = load_network(args.network_filename, equipment)
except EquipmentConfigError as this_e:
print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {this_e}')
exit(1)
except NetworkTopologyError as this_e:
print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {this_e}')
exit(1)
except ConfigurationError as this_e:
print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {this_e}')
exit(1)
except ServiceError as this_e:
print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {this_e}')
exit(1)
# input_str = raw_input("How will you use your program: c:[cli] , a:[api] ?")
# print(input_str)
#
if ((args.rest == 1) and (args.output is None)):
print('you have chosen the rest mode')
APP.run(host='0.0.0.0', port=5000, debug=True)
elif ((args.rest > 1) or ((args.rest == 1) and (args.output is not None))):
print('command is not well formulated')
else:
launch_cli(network, data, equipment)
if __name__ == '__main__':
ARGS = PARSER.parse_args()
basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(ARGS.verbose, DEBUG))
main(ARGS)

View File

@@ -0,0 +1,98 @@
{
"elements": [
{
"uid": "Site_A",
"type": "Transceiver",
"metadata": {
"location": {
"latitude": 0,
"longitude": 0,
"city": "Site A",
"region": ""
}
}
},
{
"uid": "Span1",
"type": "RamanFiber",
"type_variety": "SSMF",
"operational": {
"temperature": 283,
"raman_pumps": [
{
"power": 200e-3,
"frequency": 205e12,
"propagation_direction": "counterprop"
},
{
"power": 206e-3,
"frequency": 201e12,
"propagation_direction": "counterprop"
}
]
},
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
},
{
"uid": "Edfa1",
"type": "Edfa",
"type_variety": "std_low_gain",
"operational": {
"gain_target": 15.0,
"delta_p": -2,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 2,
"longitude": 0,
"city": null,
"region": ""
}
}
},
{
"uid": "Site_B",
"type": "Transceiver",
"metadata": {
"location": {
"latitude": 2,
"longitude": 0,
"city": "Site B",
"region": ""
}
}
}
],
"connections": [
{
"from_node": "Site_A",
"to_node": "Span1"
},
{
"from_node": "Span1",
"to_node": "Edfa1"
},
{
"from_node": "Edfa1",
"to_node": "Site_B"
}
]
}

180
examples/serviceDemov1.json Normal file
View File

@@ -0,0 +1,180 @@
{
"path-request": [
{
"request-id": "0",
"source": "trx site_a",
"destination": "trx site_b",
"src-tp-id": "trx site_a",
"dst-tp-id": "trx site_b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": null,
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 100000000000.0
}
}
},
{
"request-id": "1",
"source": "trx site_a",
"destination": "trx site_b",
"src-tp-id": "trx site_a",
"dst-tp-id": "trx site_b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 200000000000.0
}
},
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": "route-include-ero",
"index": 0,
"num-unnum-hop": {
"node-id": "Span1ab",
"link-tp-id": "link-tp-id is not used",
"hop-type": "STRICT"
}
}
]
}
},
{
"request-id": "2",
"source": "trx site_a",
"destination": "trx site_b",
"src-tp-id": "trx site_a",
"dst-tp-id": "trx site_b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 200000000000.0
}
},
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": "route-include-ero",
"index": 0,
"num-unnum-hop": {
"node-id": "roadm site_c",
"link-tp-id": "link-tp-id is not used",
"hop-type": "STRICT"
}
}
]
}
},
{
"request-id": "3",
"source": "trx site_a",
"destination": "trx site_b",
"src-tp-id": "trx site_a",
"dst-tp-id": "trx site_b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": null,
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 100000000000.0
}
}
},
{
"request-id": "4",
"source": "trx site_a",
"destination": "trx site_b",
"src-tp-id": "trx site_a",
"dst-tp-id": "trx site_b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": null,
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 100000000000.0
}
}
}
],
"synchronization": [
{
"synchronization-id": "x",
"svec": {
"relaxable": "false",
"disjointness": "node link",
"request-id-number": [
"3",
"0"
]
}
},
{
"synchronization-id": "y",
"svec": {
"relaxable": "false",
"disjointness": "node link",
"request-id-number": [
"4",
"3",
"0"
]
}
}
]
}

14
examples/sim_params.json Normal file
View File

@@ -0,0 +1,14 @@
{
"raman_computed_channels": [1, 18, 37, 56, 75],
"raman_parameters": {
"flag_raman": true,
"space_resolution": 10e3,
"tolerance": 1e-8
},
"nli_parameters": {
"nli_method_name": "ggn_spectrally_separated",
"wdm_grid_size": 50e9,
"dispersion_tolerance": 1,
"phase_shift_tollerance": 0.1
}
}

View File

@@ -4,6 +4,8 @@
0.0359549,
5.82851
],
"f_min": 191.35e12,
"f_max": 196.1e12,
"nf_ripple": [
-0.3110761646066259,
-0.3110761646066259,

703
examples/topoDemov1.json Normal file
View File

@@ -0,0 +1,703 @@
{
"elements": [
{
"uid": "trx site_a",
"type": "Transceiver",
"metadata": {
"location": {
"latitude": 0,
"longitude": 0,
"city": "Site a",
"region": ""
}
}
},
{
"uid": "roadm site_a",
"type": "Roadm",
"params": {
"target_pch_out_db": -20,
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": []
}
},
"metadata": {
"location": {
"latitude": 0,
"longitude": 0,
"city": "Site a",
"region": ""
}
}
},
{
"uid": "Span1ab",
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 100.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
},
{
"uid": "Span1ba",
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 100.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
},
{
"uid": "Span2ab",
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
},
{
"uid": "Span2ba",
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
},
{
"uid": "roadm site_b",
"type": "Roadm",
"params": {
"target_pch_out_db": -20,
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": []
}
},
"metadata": {
"location": {
"latitude": 0,
"longitude": 0,
"city": "Site b",
"region": ""
}
}
},
{
"uid": "trx site_b",
"type": "Transceiver",
"metadata": {
"location": {
"latitude": 2,
"longitude": 0,
"city": "Site b",
"region": ""
}
}
},
{
"uid": "booster1 site_a",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 19.0,
"delta_p": -1.0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site a",
"region": ""
}
}
},
{
"uid": "preamp site_b",
"type": "Edfa",
"type_variety": "std_low_gain",
"operational": {
"gain_target": 18.0,
"delta_p": 0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site b",
"region": ""
}
}
},
{
"uid": "booster1 site_b",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 19.0,
"delta_p": -1.0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site b",
"region": ""
}
}
},
{
"uid": "preamp1 site_a",
"type": "Edfa",
"type_variety": "std_low_gain",
"operational": {
"gain_target": 18.0,
"delta_p": 0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site_a",
"region": ""
}
}
},
{
"uid": "booster2 site_a",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 19.0,
"delta_p": -1.0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site a",
"region": ""
}
}
},
{
"uid": "preamp2 site_b",
"type": "Edfa",
"type_variety": "std_low_gain",
"operational": {
"gain_target": 18.0,
"delta_p": 0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site_b",
"region": ""
}
}
},
{
"uid": "booster2 site_b",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 19.0,
"delta_p": -1.0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site b",
"region": ""
}
}
},
{
"uid": "preamp2 site_a",
"type": "Edfa",
"type_variety": "std_low_gain",
"operational": {
"gain_target": 18.0,
"delta_p": 0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site_a",
"region": ""
}
}
},
{
"uid": "booster3 site_a",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 19.0,
"delta_p": -1.0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site a",
"region": ""
}
}
},
{
"uid": "preamp3 site_b",
"type": "Edfa",
"type_variety": "std_low_gain",
"operational": {
"gain_target": 18.0,
"delta_p": 0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site_b",
"region": ""
}
}
},
{
"uid": "booster3 site_b",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 19.0,
"delta_p": -1.0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site b",
"region": ""
}
}
},
{
"uid": "preamp3 site_a",
"type": "Edfa",
"type_variety": "std_low_gain",
"operational": {
"gain_target": 18.0,
"delta_p": 0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site_a",
"region": ""
}
}
},
{
"uid": "roadm site_c",
"type": "Roadm",
"params": {
"target_pch_out_db": -20,
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": []
}
},
"metadata": {
"location": {
"latitude": 0,
"longitude": 0,
"city": "Site c",
"region": ""
}
}
},
{
"uid": "booster1 site_c",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 19.0,
"delta_p": -1.0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site c",
"region": ""
}
}
},
{
"uid": "preamp1 site_c",
"type": "Edfa",
"type_variety": "std_low_gain",
"operational": {
"gain_target": 18.0,
"delta_p": 0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site_c",
"region": ""
}
}
},
{
"uid": "booster2 site_c",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 19.0,
"delta_p": -1.0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site c",
"region": ""
}
}
},
{
"uid": "preamp2 site_c",
"type": "Edfa",
"type_variety": "std_low_gain",
"operational": {
"gain_target": 18.0,
"delta_p": 0,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0,
"city": "Site_c",
"region": ""
}
}
},
{
"uid": "Span1ac",
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
},
{
"uid": "Span1ca",
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
},
{
"uid": "Span1bc",
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
},
{
"uid": "Span1cb",
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
}
],
"connections": [
{
"from_node": "trx site_a",
"to_node": "roadm site_a"
},
{
"from_node": "roadm site_a",
"to_node": "booster1 site_a"
},
{
"from_node": "booster1 site_a",
"to_node": "Span1ab"
},
{
"from_node": "Span1ab",
"to_node": "preamp site_b"
},
{
"from_node": "preamp site_b",
"to_node": "roadm site_b"
},
{
"from_node": "roadm site_b",
"to_node": "trx site_b"
},
{
"from_node": "roadm site_a",
"to_node": "booster2 site_a"
},
{
"from_node": "booster2 site_a",
"to_node": "Span2ab"
},
{
"from_node": "Span2ab",
"to_node": "preamp2 site_b"
},
{
"from_node": "preamp2 site_b",
"to_node": "roadm site_b"
},
{
"from_node": "roadm site_b",
"to_node": "booster1 site_b"
},
{
"from_node": "booster1 site_b",
"to_node": "Span1ba"
},
{
"from_node": "Span1ba",
"to_node": "preamp1 site_a"
},
{
"from_node": "preamp1 site_a",
"to_node": "roadm site_a"
},
{
"from_node": "roadm site_b",
"to_node": "booster2 site_b"
},
{
"from_node": "booster2 site_b",
"to_node": "Span2ba"
},
{
"from_node": "Span2ba",
"to_node": "preamp2 site_a"
},
{
"from_node": "preamp2 site_a",
"to_node": "roadm site_a"
},
{
"from_node": "roadm site_a",
"to_node": "booster3 site_a"
},
{
"from_node": "booster3 site_a",
"to_node": "Span1ac"
},
{
"from_node": "Span1ac",
"to_node": "preamp1 site_c"
},
{
"from_node": "preamp1 site_c",
"to_node": "roadm site_c"
},
{
"from_node": "roadm site_c",
"to_node": "booster1 site_c"
},
{
"from_node": "booster1 site_c",
"to_node": "Span1cb"
},
{
"from_node": "Span1cb",
"to_node": "preamp3 site_b"
},
{
"from_node": "preamp3 site_b",
"to_node": "roadm site_b"
},
{
"from_node": "roadm site_b",
"to_node": "booster3 site_b"
},
{
"from_node": "booster3 site_b",
"to_node": "Span1bc"
},
{
"from_node": "Span1bc",
"to_node": "preamp2 site_c"
},
{
"from_node": "preamp2 site_c",
"to_node": "roadm site_c"
},
{
"from_node": "roadm site_c",
"to_node": "booster2 site_c"
},
{
"from_node": "booster2 site_c",
"to_node": "Span1ca"
},
{
"from_node": "Span1ca",
"to_node": "preamp3 site_a"
},
{
"from_node": "preamp3 site_a",
"to_node": "roadm site_a"
}
]
}

View File

@@ -18,20 +18,21 @@ from pathlib import Path
from json import loads
from collections import Counter
from logging import getLogger, basicConfig, INFO, ERROR, DEBUG
from numpy import arange, mean
from matplotlib.pyplot import show, axis, figure, title
from numpy import linspace, mean, log10
from matplotlib.pyplot import show, axis, figure, title, text
from networkx import (draw_networkx_nodes, draw_networkx_edges,
draw_networkx_labels, dijkstra_path)
from gnpy.core.network import load_network, build_network, save_network
from gnpy.core.elements import Transceiver, Fiber, Edfa, Roadm
from gnpy.core.network import load_network, build_network, save_network, load_sim_params, configure_network
from gnpy.core.elements import Transceiver, Fiber, RamanFiber, Edfa, Roadm
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
from gnpy.core.request import Path_request, RequestParams, compute_constrained_path, propagate
from gnpy.core.request import Path_request, RequestParams, compute_constrained_path, propagate2
from gnpy.core.exceptions import ConfigurationError, EquipmentConfigError, NetworkTopologyError
import gnpy.core.ansi_escapes as ansi_escapes
logger = getLogger(__name__)
def plot_results(network, path, source, destination):
path_edges = set(zip(path[:-1], path[1:]))
edges = set(network.edges()) - path_edges
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)}
city_labels = set(labels.values())
@@ -44,16 +45,61 @@ def plot_results(network, path, source, destination):
fig = figure()
kwargs = {'figure': fig, 'pos': pos}
plot = draw_networkx_nodes(network, nodelist=network.nodes(), node_color='#ababab', **kwargs)
draw_networkx_nodes(network, nodelist=path, node_color='#ff0000', **kwargs)
draw_networkx_edges(network, edgelist=edges, edge_color='#ababab', **kwargs)
draw_networkx_labels(network, labels=labels, font_size=14, **{**kwargs, 'pos': label_pos})
axis('off')
show()
def plot_results(network, path, source, destination, infos):
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)}
city_labels = set(labels.values())
for n in network.nodes():
if n.location.city and n.location.city not in city_labels:
labels[n] = n.location.city
city_labels.add(n.location.city)
label_pos = pos
fig = figure()
kwargs = {'figure': fig, 'pos': pos}
all_nodes = [n for n in network.nodes() if n not in path]
plot = draw_networkx_nodes(network, nodelist=all_nodes, node_color='#ababab', node_size=50, **kwargs)
draw_networkx_nodes(network, nodelist=path, node_color='#ff0000', node_size=55, **kwargs)
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}')
axis('off')
heading = 'Spectral Information\n\n'
textbox = text(0.85, 0.20, heading, fontsize=14, fontname='Ubuntu Mono',
verticalalignment='top', transform=fig.axes[0].transAxes,
bbox={'boxstyle': 'round', 'facecolor': 'wheat', 'alpha': 0.5})
msgs = {(x, y): heading + '\n\n'.join(str(n) for n in ns if n in path)
for (x, y), ns in nodes.items()}
def hover(event):
if event.xdata is None or event.ydata is None:
return
if fig.contains(event):
x, y = round(event.xdata, 1), round(event.ydata, 1)
if (x, y) in msgs:
textbox.set_text(msgs[x, y])
else:
textbox.set_text(heading)
fig.canvas.draw_idle()
fig.canvas.mpl_connect('motion_notify_event', hover)
show()
def main(network, equipment, source, destination, req = None):
def main(network, equipment, source, destination, sim_params, req=None):
result_dicts = {}
network_data = [{
'network_name' : str(args.filename),
@@ -62,8 +108,8 @@ def main(network, equipment, source, destination, req = None):
}]
result_dicts.update({'network': network_data})
design_data = [{
'power_mode' : equipment['Spans']['default'].power_mode,
'span_power_range' : equipment['Spans']['default'].delta_power_range_db,
'power_mode' : equipment['Span']['default'].power_mode,
'span_power_range' : equipment['Span']['default'].delta_power_range_db,
'design_pch' : equipment['SI']['default'].power_dbm,
'baud_rate' : equipment['SI']['default'].baud_rate
}]
@@ -71,56 +117,89 @@ def main(network, equipment, source, destination, req = None):
simulation_data = []
result_dicts.update({'simulation results': simulation_data})
power_mode = equipment['Spans']['default'].power_mode
power_mode = equipment['Span']['default'].power_mode
print('\n'.join([f'Power mode is set to {power_mode}',
f'=> it can be modified in eqpt_config.json - Spans']))
f'=> it can be modified in eqpt_config.json - Span']))
pref_ch_db = lin2db(req.power*1e3) #reference channel power / span (SL=20dB)
pref_total_db = pref_ch_db + lin2db(req.nb_channel) #reference total power / span (SL=20dB)
build_network(network, equipment, pref_ch_db, pref_total_db)
path = compute_constrained_path(network, req)
spans = [s.length for s in path if isinstance(s, Fiber)]
print(f'\nThere are {len(spans)} fiber spans over {sum(spans):.0f}m between {source.uid} and {destination.uid}')
if len([s.length for s in path if isinstance(s, RamanFiber)]):
if sim_params is None:
print(f'{ansi_escapes.red}Invocation error:{ansi_escapes.reset} RamanFiber requires passing simulation params via --sim-params')
exit(1)
configure_network(network, sim_params)
spans = [s.length for s in path if isinstance(s, RamanFiber) or isinstance(s, Fiber)]
print(f'\nThere are {len(spans)} fiber spans over {sum(spans)/1000:.0f} km between {source.uid} and {destination.uid}')
print(f'\nNow propagating between {source.uid} and {destination.uid}:')
try:
power_range = list(arange(*equipment['SI']['default'].power_range_db))
last = equipment['SI']['default'].power_range_db[-2]
if len(power_range) == 0 : #bad input that will lead to no simulation
power_range = [0] #better than an error message
else:
power_range.append(last)
p_start, p_stop, p_step = equipment['SI']['default'].power_range_db
p_num = abs(int(round((p_stop - p_start)/p_step))) + 1 if p_step != 0 else 1
power_range = list(linspace(p_start, p_stop, p_num))
except TypeError:
print('invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]')
power_range = [0]
if not power_mode:
#power cannot be changed in gain mode
power_range = [0]
for dp_db in power_range:
req.power = db2lin(pref_ch_db + dp_db)*1e-3
print(f'\nPropagating with input power = {lin2db(req.power*1e3):.2f}dBm :')
propagate(path, req, equipment, show=len(power_range)==1)
print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :')
print(destination)
simulation_data.append({
'Pch_dBm' : pref_ch_db + dp_db,
'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2),
'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2),
'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2),
'SNR_total_signal_bw' : round(mean(destination.snr),2)
})
if power_mode:
print(f'\nPropagating with input power = {ansi_escapes.cyan}{lin2db(req.power*1e3):.2f} dBm{ansi_escapes.reset}:')
else:
print(f'\nPropagating in {ansi_escapes.cyan}gain mode{ansi_escapes.reset}: power cannot be set manually')
infos = propagate2(path, req, equipment)
if len(power_range) == 1:
for elem in path:
print(elem)
if power_mode:
print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:')
else:
print(f'\nTransmission results:')
print(f' Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}')
else:
print(path[-1])
#print(f'\n !!!!!!!!!!!!!!!!! TEST POINT !!!!!!!!!!!!!!!!!!!!!')
#print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}')
# => use "in" or "out" parameter
# => use "nli" or "ase" or "signal" or "total" parameter
if power_mode:
simulation_data.append({
'Pch_dBm' : pref_ch_db + dp_db,
'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2),
'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2),
'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2),
'SNR_total_signal_bw' : round(mean(destination.snr),2)
})
else:
simulation_data.append({
'gain_mode' : 'power canot be set',
'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2),
'OSNR_ASE_signal_bw' : round(mean(destination.osnr_ase),2),
'SNR_nli_signal_bw' : round(mean(destination.osnr_nli),2),
'SNR_total_signal_bw' : round(mean(destination.snr),2)
})
write_csv(result_dicts, 'simulation_result.csv')
return path
return path, infos
parser = ArgumentParser()
parser.add_argument('-e', '--equipment', type=Path,
default=Path(__file__).parent / 'eqpt_config.json')
parser.add_argument('-pl', '--plot', action='store_true', default=False)
parser.add_argument('-v', '--verbose', action='count')
parser.add_argument('-l', '--list-nodes', action='store_true', default=False, help='list all transceiver nodes')
parser.add_argument('--sim-params', type=Path,
default=None, help='Path to the JSON containing simulation parameters (required for Raman)')
parser.add_argument('--show-channels', action='store_true', help='Show final per-channel OSNR summary')
parser.add_argument('-pl', '--plot', action='store_true')
parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence')
parser.add_argument('-l', '--list-nodes', action='store_true', help='list all transceiver nodes')
parser.add_argument('-po', '--power', default=0, help='channel ref power in dBm')
#parser.add_argument('-plb', '--power-lower-bound', default=0, help='power sweep lower bound')
#parser.add_argument('-pub', '--power-upper-bound', default=1, help='power sweep upper bound')
parser.add_argument('-names', '--names-matching', action='store_true', help='display network names that are closed matches')
parser.add_argument('filename', nargs='?', type=Path,
default=Path(__file__).parent / 'edfa_example_network.json')
parser.add_argument('source', nargs='?', help='source node')
@@ -129,13 +208,24 @@ parser.add_argument('destination', nargs='?', help='destination node')
if __name__ == '__main__':
args = parser.parse_args()
basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, ERROR))
basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, DEBUG))
equipment = load_equipment(args.equipment)
# logger.info(equipment)
# print(args.filename)
network = load_network(args.filename, equipment)
# print(network)
try:
equipment = load_equipment(args.equipment)
network = load_network(args.filename, equipment, args.names_matching)
sim_params = load_sim_params(args.sim_params) if args.sim_params is not None else None
except EquipmentConfigError as e:
print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {e}')
exit(1)
except NetworkTopologyError as e:
print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}')
exit(1)
except ConfigurationError as e:
print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}')
exit(1)
if args.plot:
plot_baseline(network)
transceivers = {n.uid: n for n in network.nodes() if isinstance(n, Transceiver)}
@@ -149,37 +239,39 @@ if __name__ == '__main__':
print(uid)
exit()
#First try to find exact match if source/destination provided
if args.source:
try:
source = next(transceivers[uid] for uid in transceivers if uid == args.source)
except StopIteration as e:
#TODO code a more advanced regex to find nodes match
nodes_suggestion = [uid for uid in transceivers \
if args.source.lower() in uid.lower()]
source = transceivers[nodes_suggestion[0]] \
if len(nodes_suggestion)>0 else list(transceivers.values())[0]
print(f'invalid souce node specified, did you mean:\
\n{nodes_suggestion}?\
\n{args.source!r}, replaced with {source.uid}')
del transceivers[source.uid]
source = transceivers.pop(args.source, None)
valid_source = True if source else False
else:
source = None
logger.info('No source node specified: picking random transceiver')
source = list(transceivers.values())[0]
if args.destination:
try:
destination = next(transceivers[uid] for uid in transceivers if uid == args.destination)
except StopIteration as e:
nodes_suggestion = [uid for uid in transceivers \
if args.destination.lower() in uid.lower()]
destination = transceivers[nodes_suggestion[0]] \
if len(nodes_suggestion)>0 else list(transceivers.values())[0]
print(f'invalid destination node specified, did you mean:\
\n{nodes_suggestion}?\
\n{args.destination!r}, replaced with {destination.uid}')
destination = transceivers.pop(args.destination, None)
valid_destination = True if destination else False
else:
logger.info('No source node specified: picking random transceiver')
destination = list(transceivers.values())[1]
destination = None
logger.info('No destination node specified: picking random transceiver')
#If no exact match try to find partial match
if args.source and not source:
#TODO code a more advanced regex to find nodes match
source = next((transceivers.pop(uid) for uid in transceivers \
if args.source.lower() in uid.lower()), None)
if args.destination and not destination:
#TODO code a more advanced regex to find nodes match
destination = next((transceivers.pop(uid) for uid in transceivers \
if args.destination.lower() in uid.lower()), None)
#If no partial match or no source/destination provided pick random
if not source:
source = list(transceivers.values())[0]
del transceivers[source.uid]
if not destination:
destination = list(transceivers.values())[0]
logger.info(f'source = {args.source!r}')
logger.info(f'destination = {args.destination!r}')
@@ -190,16 +282,38 @@ if __name__ == '__main__':
params['trx_mode'] = ''
params['source'] = source.uid
params['destination'] = destination.uid
params['bidir'] = False
params['nodes_list'] = [destination.uid]
params['loose_list'] = ['strict']
params['format'] = ''
params['path_bandwidth'] = 0
trx_params = trx_mode_params(equipment)
if args.power:
trx_params['power'] = db2lin(float(args.power))*1e-3
params.update(trx_params)
req = Path_request(**params)
path = main(network, equipment, source, destination, req)
path, infos = main(network, equipment, source, destination, sim_params, req)
save_network(args.filename, network)
if args.show_channels:
print('\nThe total SNR per channel at the end of the line is:')
print('{:>5}{:>26}{:>26}{:>28}{:>28}{:>28}' \
.format('Ch. #', 'Channel frequency (THz)', 'Channel power (dBm)', 'OSNR ASE (signal bw, dB)', 'SNR NLI (signal bw, dB)', 'SNR total (signal bw, dB)'))
for final_carrier, ch_osnr, ch_snr_nl, ch_snr in zip(infos[path[-1]][1].carriers, path[-1].osnr_ase, path[-1].osnr_nli, path[-1].snr):
ch_freq = final_carrier.frequency * 1e-12
ch_power = lin2db(final_carrier.power.signal*1e3)
print('{:5}{:26.2f}{:26.2f}{:28.2f}{:28.2f}{:28.2f}' \
.format(final_carrier.channel_number, round(ch_freq, 2), round(ch_power, 2), round(ch_osnr, 2), round(ch_snr_nl, 2), round(ch_snr, 2)))
if not args.source:
print(f'\n(No source node specified: picked {source.uid})')
elif not valid_source:
print(f'\n(Invalid source node {args.source!r} replaced with {source.uid})')
if not args.destination:
print(f'\n(No destination node specified: picked {destination.uid})')
elif not valid_destination:
print(f'\n(Invalid destination node {args.destination!r} replaced with {destination.uid})')
if args.plot:
plot_results(network, path, source, destination)
plot_results(network, path, source, destination, infos)

View File

@@ -26,8 +26,8 @@ parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file
if __name__ == '__main__':
args = parser.parse_args()
with open(args.output_filename,"w") as file :
with open(args.filename) as f:
with open(args.output_filename, 'w', encoding='utf-8') as file:
with open(args.filename, encoding='utf-8') as f:
print(f'Reading {args.filename}')
json_data = loads(f.read())
equipment = load_equipment(args.eqpt_filename)

13
gnpy/core/ansi_escapes.py Normal file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
gnpy.core.ansi_escapes
======================
A random subset of ANSI terminal escape codes for colored messages
'''
red = '\x1b[1;31;40m'
cyan = '\x1b[1;36;40m'
reset = '\x1b[0m'

View File

@@ -30,49 +30,186 @@ from collections import namedtuple, Counter, defaultdict
from itertools import chain
from json import dumps
from pathlib import Path
from difflib import get_close_matches
from gnpy.core.utils import silent_remove
from gnpy.core.exceptions import NetworkTopologyError
import time
all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
class Node(namedtuple('Node', 'city state country region latitude longitude node_type')):
def __new__(cls, city, state='', country='', region='', latitude=0, longitude=0, node_type='ILA'):
values = [latitude, longitude, node_type]
default_values = [0, 0, 'ILA']
values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)]
return super().__new__(cls, city, state, country, region, *values)
class Node(object):
def __init__(self, **kwargs):
super(Node, self).__init__()
self.update_attr(kwargs)
class Link(namedtuple('Link', 'from_city to_city \
east_distance east_fiber east_lineic east_con_in east_con_out east_pmd east_cable \
west_distance west_fiber west_lineic west_con_in west_con_out west_pmd west_cable \
distance_units')):
def __new__(cls, from_city, to_city,
east_distance, east_fiber='SSMF', east_lineic=0.2,
east_con_in=None, east_con_out=None, east_pmd=0.1, east_cable='',
west_distance='', west_fiber='', west_lineic='',
west_con_in='', west_con_out='', west_pmd='', west_cable='',
distance_units='km'):
east_values = [east_distance, east_fiber, east_lineic, east_con_in, east_con_out,
east_pmd, east_cable]
west_values = [west_distance, west_fiber, west_lineic, west_con_in, west_con_out,
west_pmd, west_cable]
default_values = [80,'SSMF',0.2,None,None,0.1,'']
east_values = [x[0] if x[0] != '' else x[1] for x in zip(east_values,default_values)]
west_values = [x[0] if x[0] != '' else x[1] for x in zip(west_values,east_values)]
return super().__new__(cls, from_city, to_city, *east_values, *west_values, distance_units)
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)
class Eqpt(namedtuple('Eqpt', 'from_city to_city \
egress_amp_type egress_att_in egress_amp_gain egress_amp_tilt egress_amp_att_out\
ingress_amp_type ingress_att_in ingress_amp_gain ingress_amp_tilt ingress_amp_att_out')):
def __new__(cls, from_city='', to_city='',
egress_amp_type='', egress_att_in=0, egress_amp_gain=0, egress_amp_tilt=0, egress_amp_att_out=0,
ingress_amp_type='', ingress_att_in=0, ingress_amp_gain=0, ingress_amp_tilt=0, ingress_amp_att_out=0):
values = [from_city, to_city,
egress_amp_type, egress_att_in, egress_amp_gain, egress_amp_tilt, egress_amp_att_out,
ingress_amp_type, ingress_att_in, ingress_amp_gain, ingress_amp_tilt, ingress_amp_att_out]
default_values = ['','','',0,0,0,0,'',0,0,0,0]
values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)]
return super().__new__(cls, *values)
default_values = \
{
'city': '',
'state': '',
'country': '',
'region': '',
'latitude': 0,
'longitude': 0,
'node_type': 'ILA',
'booster_restriction' : '',
'preamp_restriction' : ''
}
class Link(object):
"""attribtes from west parse_ept_headers dict
+node_a, node_z, west_fiber_con_in, east_fiber_con_in
"""
def __init__(self, **kwargs):
super(Link, self).__init__()
self.update_attr(kwargs)
self.distance_units = 'km'
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)
k = 'west' + k.split('east')[-1]
v = clean_kwargs.get(k,v)
setattr(self, k, v)
def __eq__(self, link):
return (self.from_city == link.from_city and self.to_city == link.to_city) \
or (self.from_city == link.to_city and self.to_city == link.from_city)
default_values = \
{
'from_city': '',
'to_city': '',
'east_distance': 80,
'east_fiber': 'SSMF',
'east_lineic': 0.2,
'east_con_in': None,
'east_con_out': None,
'east_pmd': 0.1,
'east_cable': ''
}
class Eqpt(object):
def __init__(self, **kwargs):
super(Eqpt, 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_east = clean_kwargs.get(k,v)
setattr(self, k, v_east)
k = 'west' + k.split('east')[-1]
v_west = clean_kwargs.get(k,v)
setattr(self, k, v_west)
default_values = \
{
'from_city': '',
'to_city': '',
'east_amp_type': '',
'east_att_in': 0,
'east_amp_gain': None,
'east_amp_dp': None,
'east_tilt': 0,
'east_att_out': None
}
def read_header(my_sheet, line, slice_):
""" return the list of headers !:= ''
header_i = [(header, header_column_index), ...]
in a {line, slice1_x, slice_y} range
"""
Param_header = namedtuple('Param_header', 'header colindex')
try:
header = [x.value.strip() for x in my_sheet.row_slice(line, slice_[0], slice_[1])]
header_i = [Param_header(header,i+slice_[0]) for i, header in enumerate(header) if header != '']
except Exception:
header_i = []
if header_i != [] and header_i[-1].colindex != slice_[1]:
header_i.append(Param_header('',slice_[1]))
return header_i
def read_slice(my_sheet, line, slice_, header):
"""return the slice range of a given header
in a defined range {line, slice_x, slice_y}"""
header_i = read_header(my_sheet, line, slice_)
slice_range = (-1,-1)
if header_i != []:
try:
slice_range = next((h.colindex,header_i[i+1].colindex) \
for i,h in enumerate(header_i) if header in h.header)
except Exception:
pass
return slice_range
def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in):
"""return a dict of header_slice
key = column index
value = header name"""
for h0 in input_headers_dict:
slice_out = read_slice(my_sheet, start_line, slice_in, h0)
iteration = 1
while slice_out == (-1,-1) and iteration < 10:
#try next lines
#print(h0, iteration)
slice_out = read_slice(my_sheet, start_line+iteration, slice_in, h0)
iteration += 1
if slice_out == (-1, -1):
if h0 in ('east', 'Node A', 'Node Z', 'City') :
print(f'\x1b[1;31;40m'+f'CRITICAL: missing _{h0}_ header: EXECUTION ENDS'+ '\x1b[0m')
exit()
else:
print(f'missing header {h0}')
elif not isinstance(input_headers_dict[h0], dict):
headers[slice_out[0]] = input_headers_dict[h0]
else:
headers = parse_headers(my_sheet, input_headers_dict[h0], headers, start_line+1, slice_out)
if headers == {}:
print(f'\x1b[1;31;40m'+f'CRITICAL ERROR: could not find any header to read _ ABORT'+ '\x1b[0m')
exit()
return headers
def parse_row(row, headers):
#print([label for label in ept.values()])
#print([i for i in ept.keys()])
#print(row[i for i in ept.keys()])
return {f: r.value for f, r in \
zip([label for label in headers.values()], [row[i] for i in headers])}
#if r.ctype != XL_CELL_EMPTY}
def parse_sheet(my_sheet, input_headers_dict, header_line, start_line, column):
headers = parse_headers(my_sheet, input_headers_dict, {}, header_line, (0,column))
for row in all_rows(my_sheet, start=start_line):
yield parse_row(row[0: column], headers)
def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):
duplicate_links = []
for l1 in links:
for l2 in links:
if l1 is not l2 and l1 == l2 and l2 not in duplicate_links:
print(f'\nWARNING\n \
link {l1.from_city}-{l1.to_city} is duplicate \
\nthe 1st duplicate link will be removed but you should check Links sheet input')
duplicate_links.append(l1)
#if duplicate_links != []:
#time.sleep(3)
for l in duplicate_links:
links.remove(l)
def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city):
try :
test_nodes = [n for n in nodes_by_city if not n in links_by_city]
test_links = [n for n in links_by_city if not n in nodes_by_city]
@@ -81,7 +218,7 @@ def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city):
and (test_links == [] or test_links ==[''])\
and (test_eqpts == [] or test_eqpts ==[''])
except AssertionError:
print(f'!names in Nodes and Links sheets do no match, check:\
print(f'CRITICAL error: \nNames 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')
@@ -94,13 +231,14 @@ def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_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')
nodes_by_city[city] = nodes_by_city[city]._replace(node_type='ROADM')
nodes = [n._replace(node_type='ROADM') if n.city==city else n for n in nodes]
return nodes
nodes_by_city[city].node_type = 'ROADM'
for n in nodes:
if n.city==city:
n.node_type='ROADM'
return nodes, links
def convert_file(input_filename, filter_region=[]):
def convert_file(input_filename, names_matching=False, filter_region=[]):
nodes, links, eqpts = parse_excel(input_filename)
if filter_region:
nodes = [n for n in nodes if n.region.lower() in filter_region]
cities = {n.city for n in nodes}
@@ -111,6 +249,27 @@ def convert_file(input_filename, filter_region=[]):
global nodes_by_city
nodes_by_city = {n.city: n for n in nodes}
#create matching dictionary for node name mismatch analysis
cities = {''.join(c.strip() for c in n.city.split('C+L')).lower(): n.city for n in nodes}
cities_to_match = [k for k in cities]
city_match_dic = defaultdict(list)
for city in cities:
if city in cities_to_match:
cities_to_match.remove(city)
matches = get_close_matches(city, cities_to_match, 4, 0.85)
for m in matches:
city_match_dic[cities[city]].append(cities[m])
#check lower case/upper case
for city in nodes_by_city:
for match_city in nodes_by_city:
if match_city.lower() == city.lower() and match_city != city:
city_match_dic[city].append(match_city)
if names_matching:
print('\ncity match dictionary:',city_match_dic)
with open('name_match_dictionary.json', 'w', encoding='utf-8') as city_match_dic_file:
city_match_dic_file.write(dumps(city_match_dic, indent=2, ensure_ascii=False))
global links_by_city
links_by_city = defaultdict(list)
@@ -123,7 +282,7 @@ def convert_file(input_filename, filter_region=[]):
for eqpt in eqpts:
eqpts_by_city[eqpt.from_city].append(eqpt)
nodes = sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city)
nodes, links = sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city)
data = {
'elements':
@@ -140,22 +299,37 @@ def convert_file(input_filename, filter_region=[]):
'latitude': x.latitude,
'longitude': x.longitude}},
'type': 'Roadm'}
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] +
[{'uid': f'ingress fused spans in {x.city}',
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' \
and x.booster_restriction == '' and x.preamp_restriction == ''] +
[{'uid': f'roadm {x.city}',
'params' : {
'restrictions': {
'preamp_variety_list': silent_remove(x.preamp_restriction.split(' | '),''),
'booster_variety_list': silent_remove(x.booster_restriction.split(' | '),'')
}
},
'metadata': {'location': {'city': x.city,
'region': x.region,
'latitude': x.latitude,
'longitude': x.longitude}},
'type': 'Roadm'}
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' and \
(x.booster_restriction != '' or x.preamp_restriction != '')] +
[{'uid': f'west fused spans in {x.city}',
'metadata': {'location': {'city': x.city,
'region': x.region,
'latitude': x.latitude,
'longitude': x.longitude}},
'type': 'Fused'}
for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] +
[{'uid': f'egress fused spans in {x.city}',
[{'uid': f'east fused spans in {x.city}',
'metadata': {'location': {'city': x.city,
'region': x.region,
'latitude': x.latitude,
'longitude': x.longitude}},
'type': 'Fused'}
for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] +
[{'uid': f'fiber ({x.from_city} {x.to_city})-{x.east_cable}',
[{'uid': f'fiber ({x.from_city} \u2192 {x.to_city})-{x.east_cable}',
'metadata': {'location': midpoint(nodes_by_city[x.from_city],
nodes_by_city[x.to_city])},
'type': 'Fiber',
@@ -167,7 +341,7 @@ def convert_file(input_filename, filter_region=[]):
'con_out':x.east_con_out}
}
for x in links] +
[{'uid': f'fiber ({x.to_city} {x.from_city})-{x.west_cable}',
[{'uid': f'fiber ({x.to_city} \u2192 {x.from_city})-{x.west_cable}',
'metadata': {'location': midpoint(nodes_by_city[x.from_city],
nodes_by_city[x.to_city])},
'type': 'Fiber',
@@ -179,28 +353,56 @@ def convert_file(input_filename, filter_region=[]):
'con_out':x.west_con_out}
} # missing ILA construction
for x in links] +
[{'uid': f'egress edfa in {e.from_city} to {e.to_city}',
[{'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.egress_amp_type,
'operational': {'gain_target': e.egress_amp_gain,
'tilt_target': e.egress_amp_tilt}
}
for e in eqpts if e.egress_amp_type.lower() != ''] +
[{'uid': f'ingress edfa in {e.from_city} to {e.to_city}',
'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.ingress_amp_type,
'operational': {'gain_target': e.ingress_amp_gain,
'tilt_target': e.ingress_amp_tilt}
}
for e in eqpts if e.ingress_amp_type.lower() != ''],
'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'],
'connections':
list(chain.from_iterable([eqpt_connection_by_city(n.city)
for n in nodes]))
@@ -214,68 +416,106 @@ def convert_file(input_filename, filter_region=[]):
for x in nodes_by_city.values() if x.node_type.lower()=='roadm'])))
}
#print(dumps(data, indent=2))
# output_json_file_name = input_filename.split(".")[0]+".json"
suffix_filename = str(input_filename.suffixes[0])
full_input_filename = str(input_filename)
split_filename = [full_input_filename[0:len(full_input_filename)-len(suffix_filename)] , suffix_filename[1:]]
output_json_file_name = split_filename[0]+'.json'
with open(output_json_file_name,'w') as edfa_json_file:
edfa_json_file.write(dumps(data, indent=2))
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))
return output_json_file_name
def parse_excel(input_filename):
link_headers = \
{ 'Node A': 'from_city',
'Node Z': 'to_city',
'east':{
'Distance (km)': 'east_distance',
'Fiber type': 'east_fiber',
'lineic att': 'east_lineic',
'Con_in': 'east_con_in',
'Con_out': 'east_con_out',
'PMD': 'east_pmd',
'Cable id': 'east_cable'
},
'west':{
'Distance (km)': 'west_distance',
'Fiber type': 'west_fiber',
'lineic att': 'west_lineic',
'Con_in': 'west_con_in',
'Con_out': 'west_con_out',
'PMD': 'west_pmd',
'Cable id': 'west_cable'
}
}
node_headers = \
{ 'City': 'city',
'State': 'state',
'Country': 'country',
'Region': 'region',
'Latitude': 'latitude',
'Longitude': 'longitude',
'Type': 'node_type',
'Booster_restriction': 'booster_restriction',
'Preamp_restriction': 'preamp_restriction'
}
eqpt_headers = \
{ 'Node A': 'from_city',
'Node Z': 'to_city',
'east':{
'amp type': 'east_amp_type',
'att_in': 'east_att_in',
'amp gain': 'east_amp_gain',
'delta p': 'east_amp_dp',
'tilt': 'east_tilt',
'att_out': 'east_att_out'
},
'west':{
'amp type': 'west_amp_type',
'att_in': 'west_att_in',
'amp gain': 'west_amp_gain',
'delta p': 'west_amp_dp',
'tilt': 'west_tilt',
'att_out': 'west_att_out'
}
}
with open_workbook(input_filename) as wb:
nodes_sheet = wb.sheet_by_name('Nodes')
links_sheet = wb.sheet_by_name('Links')
try:
eqpt_sheet = wb.sheet_by_name('Eqpt')
except:
except Exception:
#eqpt_sheet is optional
eqpt_sheet = None
# sanity check
"""
header = [x.value.strip() for x in nodes_sheet.row(4)]
expected = ['City', 'State', 'Country', 'Region', 'Latitude', 'Longitude']
if header != expected:
raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}')
"""
nodes = []
for row in all_rows(nodes_sheet, start=5):
nodes.append(Node(*(x.value for x in row[0:NODES_COLUMN])))
#check input
expected_node_types = ('ROADM', 'ILA', 'FUSED')
nodes = [n._replace(node_type='ILA')
if not (n.node_type in expected_node_types) else n for n in nodes]
for node in parse_sheet(nodes_sheet, node_headers, NODES_LINE, NODES_LINE+1, NODES_COLUMN):
nodes.append(Node(**node))
expected_node_types = {'ROADM', 'ILA', 'FUSED'}
for n in nodes:
if n.node_type not in expected_node_types:
n.node_type = 'ILA'
# sanity check
"""
header = [x.value.strip() for x in links_sheet.row(4)]
expected = ['Node A', 'Node Z',
'Distance (km)', 'Fiber type', 'lineic att', 'Con_in', 'Con_out', 'PMD', 'Cable id',
'Distance (km)', 'Fiber type', 'lineic att', 'Con_in', 'Con_out', 'PMD', 'Cable id']
if header != expected:
raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}')
"""
links = []
for row in all_rows(links_sheet, start=5):
links.append(Link(*(x.value for x in row[0:LINKS_COLUMN])))
for link in parse_sheet(links_sheet, link_headers, LINKS_LINE, LINKS_LINE+2, LINKS_COLUMN):
links.append(Link(**link))
#print('\n', [l.__dict__ for l in links])
eqpts = []
if eqpt_sheet != None:
for row in all_rows(eqpt_sheet, start=5):
eqpts.append(Eqpt(*(x.value for x in row[0:EQPTS_COLUMN])))
for eqpt in parse_sheet(eqpt_sheet, eqpt_headers, EQPTS_LINE, EQPTS_LINE+2, EQPTS_COLUMN):
eqpts.append(Eqpt(**eqpt))
# sanity check
all_cities = Counter(n.city for n in nodes)
if len(all_cities) != len(nodes):
ValueError(f'Duplicate city: {all_cities}')
if any(ln.from_city not in all_cities or
ln.to_city not in all_cities for ln in links):
ValueError(f'Bad link.')
raise ValueError(f'Duplicate city: {all_cities}')
bad_links = []
for lnk in links:
if lnk.from_city not in all_cities or lnk.to_city not in all_cities:
bad_links.append([lnk.from_city, lnk.to_city])
if bad_links:
raise NetworkTopologyError(f'Bad link(s): {bad_links}.')
return nodes, links, eqpts
@@ -283,9 +523,9 @@ def parse_excel(input_filename):
def eqpt_connection_by_city(city_name):
other_cities = fiber_dest_from_source(city_name)
subdata = []
if nodes_by_city[city_name].node_type.lower() in ('ila', 'fused'):
if nodes_by_city[city_name].node_type.lower() in {'ila', 'fused'}:
# Then len(other_cities) == 2
direction = ['ingress', 'egress']
direction = ['west', 'east']
for i in range(2):
from_ = fiber_link(other_cities[i], city_name)
in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i])
@@ -299,7 +539,7 @@ def eqpt_connection_by_city(city_name):
subdata += connect_eqpt(from_, in_, to_)
from_ = fiber_link(other_city, city_name)
in_ = eqpt_in_city_to_city(city_name, other_city, "ingress")
in_ = eqpt_in_city_to_city(city_name, other_city, "west")
to_ = f'roadm {city_name}'
subdata += connect_eqpt(from_, in_, to_)
return subdata
@@ -315,8 +555,8 @@ def connect_eqpt(from_, in_, to_):
return connections
def eqpt_in_city_to_city(in_city, to_city, direction='egress'):
rev_direction = 'ingress' if direction == 'egress' else 'egress'
def eqpt_in_city_to_city(in_city, to_city, direction='east'):
rev_direction = 'west' if direction == 'east' else 'east'
amp_direction = f'{direction}_amp_type'
amp_rev_direction = f'{rev_direction}_amp_type'
return_eqpt = ''
@@ -352,9 +592,9 @@ def fiber_link(from_city, to_city):
link = links_by_city[from_city]
l = next(l for l in link if l.from_city in source_dest and l.to_city in source_dest)
if l.from_city == from_city:
fiber = f'fiber ({l.from_city} {l.to_city})-{l.east_cable}'
fiber = f'fiber ({l.from_city} \u2192 {l.to_city})-{l.east_cable}'
else:
fiber = f'fiber ({l.to_city} {l.from_city})-{l.west_cable}'
fiber = f'fiber ({l.to_city} \u2192 {l.from_city})-{l.west_cable}'
return fiber
@@ -375,9 +615,13 @@ def midpoint(city_a, city_b):
#output_json_file_name = 'coronet_conus_example.json'
#TODO get column size automatically from tupple size
NODES_COLUMN = 7
NODES_COLUMN = 10
NODES_LINE = 4
LINKS_COLUMN = 16
EQPTS_COLUMN = 12
LINKS_LINE = 3
EQPTS_LINE = 3
EQPTS_COLUMN = 14
parser = ArgumentParser()
parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls')
parser.add_argument('-f', '--filter-region', action='append', default=[])

View File

@@ -7,25 +7,26 @@ gnpy.core.elements
This module contains standard network elements.
A network element is a Python callable. It takes a .info.SpectralInformation
A network element is a Python callable. It takes a :class:`.info.SpectralInformation`
object and returns a copy with appropriate fields affected. This structure
represents spectral information that is "propogated" by this network element.
Network elements must have only a local "view" of the network and propogate
SpectralInformation using only this information. They should be independent and
:class:`.info.SpectralInformation` using only this information. They should be independent and
self-contained.
Network elements MUST implement two attributes .uid and .name representing a
unique identifier and a printable name.
'''
from numpy import abs, arange, arcsinh, array, exp
from numpy import abs, arange, array, exp, divide, errstate
from numpy import interp, log10, mean, pi, polyfit, polyval, sum
from scipy.constants import c, h
from collections import namedtuple
from gnpy.core.node import Node
from gnpy.core.units import UNITS
from gnpy.core.utils import lin2db, db2lin, itufs
from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum
from gnpy.core.science_utils import propagate_raman_fiber, _psi
class Transceiver(Node):
def __init__(self, *args, **kwargs):
@@ -35,23 +36,50 @@ class Transceiver(Node):
self.osnr_nli = None
self.snr = None
self.passive = False
self.baud_rate = None
def _calc_snr(self, spectral_info):
ase = [c.power.ase for c in spectral_info.carriers]
nli = [c.power.nli for c in spectral_info.carriers]
if min(ase) > 1e-20:
self.osnr_ase = [lin2db(c.power.signal/c.power.ase)
def _calc_snr(self, spectral_info):
with errstate(divide='ignore'):
self.baud_rate = [c.baud_rate for c in spectral_info.carriers]
ratio_01nm = [lin2db(12.5e9/b_rate) for b_rate in self.baud_rate]
#set raw values to record original calculation, before update_snr()
self.raw_osnr_ase = [lin2db(divide(c.power.signal, c.power.ase))
for c in spectral_info.carriers]
self.raw_osnr_ase_01nm = [ase - ratio for ase, ratio
in zip(self.raw_osnr_ase, ratio_01nm)]
self.raw_osnr_nli = [lin2db(divide(c.power.signal, c.power.nli))
for c in spectral_info.carriers]
ratio_01nm = [lin2db(12.5e9/c.baud_rate)
for c in spectral_info.carriers]
self.osnr_ase_01nm = [ase - ratio for ase, ratio
in zip(self.osnr_ase, ratio_01nm)]
if min(nli) > 1e-20:
self.osnr_nli = [lin2db(c.power.signal/c.power.nli)
for c in spectral_info.carriers]
self.snr = [lin2db(c.power.signal/(c.power.nli+c.power.ase))
self.raw_snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase))
for c in spectral_info.carriers]
self.raw_snr_01nm = [snr - ratio for snr, ratio
in zip(self.raw_snr, ratio_01nm)]
self.osnr_ase = self.raw_osnr_ase
self.osnr_ase_01nm = self.raw_osnr_ase_01nm
self.osnr_nli = self.raw_osnr_nli
self.snr = self.raw_snr
self.snr_01nm = self.raw_snr_01nm
def update_snr(self, *args):
"""
snr_added in 0.1nm
compute SNR penalties such as transponder Tx_osnr or Roadm add_drop_osnr
only applied in request.py / propagate on the last Trasceiver node of the path
all penalties are added in a single call because to avoid uncontrolled cumul
"""
#use raw_values so that the added snr penalties are not cumulated
snr_added = 0
for s in args:
snr_added += db2lin(-s)
snr_added = -lin2db(snr_added)
self.osnr_ase = list(map(lambda x,y:snr_sum(x,y,snr_added),
self.raw_osnr_ase, self.baud_rate))
self.snr = list(map(lambda x,y:snr_sum(x,y,snr_added),
self.raw_snr, self.baud_rate))
self.osnr_ase_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),
self.raw_osnr_ase_01nm))
self.snr_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added),
self.raw_snr_01nm))
@property
def to_json(self):
@@ -77,38 +105,45 @@ class Transceiver(Node):
snr = round(mean(self.snr),2)
osnr_ase = round(mean(self.osnr_ase),2)
osnr_ase_01nm = round(mean(self.osnr_ase_01nm), 2)
snr_01nm = round(mean(self.snr_01nm),2)
return '\n'.join([f'{type(self).__name__} {self.uid}',
f' OSNR ASE (1nm): {osnr_ase_01nm:.2f}',
f' OSNR ASE (signal bw): {osnr_ase:.2f}',
f' SNR total (signal bw): {snr:.2f}'])
f' OSNR ASE (0.1nm, dB): {osnr_ase_01nm:.2f}',
f' OSNR ASE (signal bw, dB): {osnr_ase:.2f}',
f' SNR total (signal bw, dB): {snr:.2f}',
f' SNR total (0.1nm, dB): {snr_01nm:.2f}'])
def __call__(self, spectral_info):
self._calc_snr(spectral_info)
return spectral_info
RoadmParams = namedtuple('RoadmParams', 'loss')
RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr restrictions per_degree_target_pch_out_db')
class Roadm(Node):
def __init__(self, *args, params=None, **kwargs):
if params is None:
# default loss value if not mentioned in loaded network json
params = {'loss':None}
def __init__(self, *args, params, **kwargs):
if 'per_degree_target_pch_out_db' not in params.keys():
params['per_degree_target_pch_out_db'] = []
super().__init__(*args, params=RoadmParams(**params), **kwargs)
self.loss = self.params.loss
self.pch_out = None
self.loss = 0 #auto-design interest
self.effective_loss = None
self.effective_pch_out_db = self.params.target_pch_out_db
self.passive = True
self.restrictions = self.params.restrictions
self.per_degree_target_pch_out_db = self.params.per_degree_target_pch_out_db
@property
def to_json(self):
return {'uid' : self.uid,
'type' : type(self).__name__,
'params' : {'loss' : self.loss},
'params' : {
'target_pch_out_db' : self.effective_pch_out_db,
'restrictions' : self.restrictions,
'per_degree_target_pch_out_db': self.per_degree_target_pch_out_db
},
'metadata' : {
'location': self.metadata['location']._asdict()
}
}
}
def __repr__(self):
@@ -116,27 +151,45 @@ class Roadm(Node):
def __str__(self):
return '\n'.join([f'{type(self).__name__} {self.uid}',
f' loss (dB): {self.loss:.2f}',
f' pch out (dBm): {self.pch_out!r}'])
f' effective loss (dB): {self.effective_loss:.2f}',
f' pch out (dBm): {self.effective_pch_out_db!r}'])
def propagate(self, *carriers):
attenuation = db2lin(self.loss)
for carrier in carriers:
def propagate(self, pref, *carriers, degree):
#pin_target and loss are read from eqpt_config.json['Roadm']
#all ingress channels in xpress are set to this power level
#but add channels are not, so we define an effective loss
#in the case of add channels
if self.per_degree_target_pch_out_db:
# find the target power on this degree
try:
temp = next(el['target_pch_out_db'] \
for el in self.per_degree_target_pch_out_db if el['to_node']==degree)
except StopIteration:
# if no target power is defined on this degree use the global one
temp = self.params.target_pch_out_db
else:
# if no per degree target power are defined, use the global one
temp = self.params.target_pch_out_db
self.effective_pch_out_db = min(pref.p_spani, temp)
self.effective_loss = pref.p_spani - self.effective_pch_out_db
carriers_power = array([c.power.signal +c.power.nli+c.power.ase for c in carriers])
carriers_att = list(map(lambda x : lin2db(x*1e3)-self.effective_pch_out_db, carriers_power))
exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default = 0)
carriers_att = list(map(lambda x: db2lin(x+exceeding_att), carriers_att))
for carrier_att, carrier in zip(carriers_att, carriers) :
pwr = carrier.power
pwr = pwr._replace(signal=pwr.signal/attenuation,
nonlinear_interference=pwr.nli/attenuation,
amplified_spontaneous_emission=pwr.ase/attenuation)
pwr = pwr._replace( signal = pwr.signal/carrier_att,
nli = pwr.nli/carrier_att,
ase = pwr.ase/carrier_att)
yield carrier._replace(power=pwr)
def update_pref(self, pref):
self.pch_out = round(pref.pi - self.loss, 2)
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
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.carriers))
def __call__(self, spectral_info, degree):
carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers, degree=degree))
pref = self.update_pref(spectral_info.pref)
return spectral_info.update(carriers=carriers, pref=pref)
return spectral_info._replace(carriers=carriers, pref=pref)
FusedParams = namedtuple('FusedParams', 'loss')
@@ -153,6 +206,9 @@ class Fused(Node):
def to_json(self):
return {'uid' : self.uid,
'type' : type(self).__name__,
'params' :{
'loss': self.loss
},
'metadata' : {
'location': self.metadata['location']._asdict()
}
@@ -171,17 +227,17 @@ class Fused(Node):
for carrier in carriers:
pwr = carrier.power
pwr = pwr._replace(signal=pwr.signal/attenuation,
nonlinear_interference=pwr.nli/attenuation,
amplified_spontaneous_emission=pwr.ase/attenuation)
nli=pwr.nli/attenuation,
ase=pwr.ase/attenuation)
yield carrier._replace(power=pwr)
def update_pref(self, pref):
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
return pref._replace(p_span0=pref.p_span0, p_spani=pref.p_spani - self.loss)
def __call__(self, spectral_info):
carriers = tuple(self.propagate(*spectral_info.carriers))
pref = self.update_pref(spectral_info.pref)
return spectral_info.update(carriers=carriers, pref=pref)
return spectral_info._replace(carriers=carriers, pref=pref)
FiberParams = namedtuple('FiberParams', 'type_variety length loss_coef length_units \
att_in con_in con_out dispersion gamma')
@@ -210,7 +266,9 @@ class Fiber(Node):
self.con_out = self.params.con_out
self.dispersion = self.params.dispersion # s/m/m
self.gamma = self.params.gamma # 1/W/m
self.pch_out = None
self.pch_out_db = None
self.carriers_in = None
self.carriers_out = None
# TODO|jla: discuss factor 2 in the linear lineic attenuation
@property
@@ -243,16 +301,17 @@ class Fiber(Node):
f' pad att_in (dB): {self.att_in:.2f}',
f' total loss (dB): {self.loss:.2f}',
f' (includes conn loss (dB) in: {self.con_in:.2f} out: {self.con_out:.2f})',
f' (conn loss out includes EOL margin defined in eqpt_config.json)'])
f' (conn loss out includes EOL margin defined in eqpt_config.json)',
f' pch out (dBm): {self.pch_out_db!r}'])
@property
def fiber_loss(self):
# dB fiber loss, not including padding attenuator
"""Fiber loss in dB, not including padding attenuator"""
return self.loss_coef * self.length + self.con_in + self.con_out
@property
def loss(self):
#total loss incluiding padding att_in: useful for polymorphism with roadm loss
"""total loss including padding att_in: useful for polymorphism with roadm loss"""
return self.loss_coef * self.length + self.con_in + self.con_out + self.att_in
@property
@@ -275,46 +334,46 @@ class Fiber(Node):
aleff = 1 / (2 * alpha)
return aleff
def beta2(self, ref_wavelength=None):
""" Returns beta2 from dispersion parameter.
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 beta2(self, ref_wavelength=1550e-9):
"""Returns beta2 from dispersion parameter.
Dispersion is entered in ps/nm/km.
Disperion can be a numpy array or a single value. If a
value ref_wavelength is not entered 1550e-9m will be assumed.
ref_wavelength can be a numpy array.
Disperion can be a numpy array or a single value.
:param ref_wavelength: can be a numpy array; default: 1550nm
"""
# TODO|jla: discuss beta2 as method or attribute
wl = 1550e-9 if ref_wavelength is None else ref_wavelength
D = abs(self.dispersion)
b2 = (wl ** 2) * D / (2 * pi * c) # 10^21 scales [ps^2/km]
b2 = (ref_wavelength ** 2) * D / (2 * pi * c) # 10^21 scales [ps^2/km]
return b2 # s/Hz/m
def dbkm_2_lin(self):
""" calculates the linear loss coefficient
"""
# alpha_pcoef is linear loss coefficient in dB/km^-1
# alpha_acoef is linear loss field amplitude coefficient in m^-1
"""calculates the linear loss coefficient"""
# linear loss coefficient in dB/km^-1
alpha_pcoef = self.loss_coef
# linear loss field amplitude coefficient in m^-1
alpha_acoef = alpha_pcoef / (2 * 10 * log10(exp(1)))
return alpha_pcoef, alpha_acoef
def _psi(self, carrier, interfering_carrier):
""" Calculates eq. 123 from arXiv:1209.0394.
"""
if carrier.num_chan == interfering_carrier.num_chan: # SCI
psi = arcsinh(0.5 * pi**2 * self.asymptotic_length
* abs(self.beta2()) * carrier.baud_rate**2)
else: # XCI
delta_f = carrier.freq - interfering_carrier.freq
psi = arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
* carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
psi -= arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
* carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
return psi
def _gn_analytic(self, carrier, *carriers):
""" Computes the nonlinear interference power on a single carrier.
The method uses eq. 120 from arXiv:1209.0394.
"""Computes the nonlinear interference power on a single carrier.
The method uses eq. 120 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__.
:param carrier: the signal under analysis
:param carriers: the full WDM comb
:return: carrier_nli: the amount of nonlinear interference in W on the under analysis
@@ -322,7 +381,7 @@ class Fiber(Node):
g_nli = 0
for interfering_carrier in carriers:
psi = self._psi(carrier, interfering_carrier)
psi = _psi(carrier, interfering_carrier, beta2=self.beta2(), asymptotic_length=self.asymptotic_length)
g_nli += (interfering_carrier.power.signal/interfering_carrier.baud_rate)**2 \
* (carrier.power.signal/carrier.baud_rate) * psi
@@ -341,8 +400,8 @@ class Fiber(Node):
for carrier in carriers:
pwr = carrier.power
pwr = pwr._replace(signal=pwr.signal/attenuation,
nonlinear_interference=pwr.nli/attenuation,
amplified_spontaneous_emission=pwr.ase/attenuation)
nli=pwr.nli/attenuation,
ase=pwr.ase/attenuation)
carrier = carrier._replace(power=pwr)
chan.append(carrier)
@@ -354,18 +413,77 @@ class Fiber(Node):
pwr = carrier.power
carrier_nli = self._gn_analytic(carrier, *carriers)
pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation/attenuation,
nonlinear_interference=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation,
amplified_spontaneous_emission=pwr.ase/self.lin_attenuation/attenuation)
nli=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation,
ase=pwr.ase/self.lin_attenuation/attenuation)
yield carrier._replace(power=pwr)
def update_pref(self, pref):
self.pch_out = round(pref.pi - self.loss, 2)
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
self.pch_out_db = round(pref.p_spani - self.loss, 2)
return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db)
def __call__(self, spectral_info):
self.carriers_in = spectral_info.carriers
carriers = tuple(self.propagate(*spectral_info.carriers))
pref = self.update_pref(spectral_info.pref)
return spectral_info.update(carriers=carriers, pref=pref)
self.carriers_out = carriers
return spectral_info._replace(carriers=carriers, pref=pref)
RamanFiberParams = namedtuple('RamanFiberParams', 'type_variety length loss_coef length_units \
att_in con_in con_out dispersion gamma raman_efficiency')
class RamanFiber(Fiber):
def __init__(self, *args, params=None, **kwargs):
if params is None:
params = {}
if 'con_in' not in params:
# if not defined in the network json connector loss in/out
# the None value will be updated in network.py[build_network]
# with default values from eqpt_config.json[Spans]
params['con_in'] = None
params['con_out'] = None
if 'att_in' not in params:
#fixed attenuator for padding
params['att_in'] = 0
# TODO: can we re-use the Fiber constructor in a better way?
Node.__init__(self, *args, params=RamanFiberParams(**params), **kwargs)
self.type_variety = self.params.type_variety
self.length = self.params.length * UNITS[self.params.length_units] # in m
self.loss_coef = self.params.loss_coef * 1e-3 # lineic loss dB/m
self.lin_loss_coef = self.params.loss_coef / (20 * log10(exp(1)))
self.att_in = self.params.att_in
self.con_in = self.params.con_in
self.con_out = self.params.con_out
self.dispersion = self.params.dispersion # s/m/m
self.gamma = self.params.gamma # 1/W/m
self.pch_out_db = None
self.carriers_in = None
self.carriers_out = None
# TODO|jla: discuss factor 2 in the linear lineic attenuation
@property
def sim_params(self):
return self._sim_params
@sim_params.setter
def sim_params(self, sim_params=None):
self._sim_params = sim_params
def update_pref(self, pref, *carriers):
pch_out_db = lin2db(mean([carrier.power.signal for carrier in carriers])) + 30
self.pch_out_db = round(pch_out_db, 2)
return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db)
def __call__(self, spectral_info):
self.carriers_in = spectral_info.carriers
carriers = tuple(self.propagate(*spectral_info.carriers))
pref = self.update_pref(spectral_info.pref, *carriers)
self.carriers_out = carriers
return spectral_info._replace(carriers=carriers, pref=pref)
def propagate(self, *carriers):
for propagated_carrier in propagate_raman_fiber(self, *carriers):
yield propagated_carrier
class EdfaParams:
def __init__(self, **params):
@@ -373,16 +491,16 @@ class EdfaParams:
if params == {}:
self.type_variety = ''
self.type_def = ''
self.gain_flatmax = 0
self.gain_min = 0
self.p_max = 0
self.nf_model = None
self.nf_fit_coeff = None
self.nf_ripple = None
self.dgt = None
self.gain_ripple = None
self.out_voa_auto = False
self.allowed_for_design = None
# self.gain_flatmax = 0
# self.gain_min = 0
# self.p_max = 0
# self.nf_model = None
# self.nf_fit_coeff = None
# self.nf_ripple = None
# self.dgt = None
# self.gain_ripple = None
# self.out_voa_auto = False
# self.allowed_for_design = None
def update_params(self, kwargs):
for k,v in kwargs.items() :
@@ -390,22 +508,33 @@ class EdfaParams:
if isinstance(v, dict) else v)
class EdfaOperational:
def __init__(self, gain_target, tilt_target, out_voa=None):
self.gain_target = gain_target
self.tilt_target = tilt_target
self.out_voa = out_voa
default_values = \
{
'gain_target': None,
'delta_p': None,
'out_voa': None,
'tilt_target': 0
}
def __init__(self, **operational):
self.update_attr(operational)
def update_attr(self, kwargs):
clean_kwargs = {k:v for k,v in kwargs.items() if v !=''}
for k,v in self.default_values.items():
setattr(self, k, clean_kwargs.get(k,v))
def __repr__(self):
return (f'{type(self).__name__}('
f'gain_target={self.gain_target!r}, '
f'tilt_target={self.tilt_target!r})')
class Edfa(Node):
def __init__(self, *args, params={}, operational={}, **kwargs):
#TBC is this useful? put in comment for now:
#if params is None:
# params = {}
#if operational is None:
# operational = {}
def __init__(self, *args, params=None, operational=None, **kwargs):
if params is None:
params = {}
if operational is None:
operational = {}
super().__init__(
*args,
params=EdfaParams(**params),
@@ -420,13 +549,18 @@ class Edfa(Node):
self.nf = None # dB edfa nf at operational.gain_target
self.gprofile = None
self.pin_db = None
self.nch = None
self.pout_db = None
self.dp_db = None #delta P with Pref (power swwep) in power mode
self.target_pch_db = None
self.effective_pch_db = None
self.target_pch_out_db = None
self.effective_pch_out_db = None
self.passive = False
self.effective_gain = self.operational.gain_target
self.att_in = None
self.carriers_in = None
self.carriers_out = None
self.effective_gain = self.operational.gain_target
self.delta_p = self.operational.delta_p #delta P with Pref (power swwep) in power mode
self.tilt_target = self.operational.tilt_target
self.out_voa = self.operational.out_voa
@property
def to_json(self):
@@ -434,9 +568,10 @@ class Edfa(Node):
'type' : type(self).__name__,
'type_variety' : self.params.type_variety,
'operational' : {
'gain_target' : self.operational.gain_target,
'tilt_target' : self.operational.tilt_target,
'out_voa' : self.operational.out_voa
'gain_target' : self.effective_gain,
'delta_p' : self.delta_p,
'tilt_target' : self.tilt_target,
'out_voa' : self.out_voa
},
'metadata' : {
'location': self.metadata['location']._asdict()
@@ -445,7 +580,7 @@ class Edfa(Node):
def __repr__(self):
return (f'{type(self).__name__}(uid={self.uid!r}, '
f'type_variety={self.params.type_variety!r}'
f'type_variety={self.params.type_variety!r}, '
f'interpol_dgt={self.interpol_dgt!r}, '
f'interpol_gain_ripple={self.interpol_gain_ripple!r}, '
f'interpol_nf_ripple={self.interpol_nf_ripple!r}, '
@@ -468,66 +603,128 @@ class Edfa(Node):
f' pad att_in (dB): {self.att_in:.2f}',
f' Power In (dBm): {self.pin_db:.2f}',
f' Power Out (dBm): {self.pout_db:.2f}',
f' Delta_P (dB): {self.dp_db!r}',
f' target pch (dBm): {self.target_pch_db!r}',
f' effective pch (dBm): {self.effective_pch_db!r}',
f' output VOA (dB): {self.operational.out_voa:.2f}'])
f' Delta_P (dB): {self.delta_p!r}',
f' target pch (dBm): {self.target_pch_out_db!r}',
f' effective pch (dBm): {self.effective_pch_out_db!r}',
f' output VOA (dB): {self.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
"""interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from JSON
set the edfa class __init__ None parameters :
self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple
"""
# TODO|jla: read amplifier actual frequencies from additional params in json
amplifier_freq = itufs(0.05) * 1e12 # Hz
amplifier_freq = arrange_frequencies(len(self.params.dgt), self.params.f_min, self.params.f_max) # Hz
self.channel_freq = frequencies
self.interpol_dgt = interp(self.channel_freq, amplifier_freq, self.params.dgt)
self.interpol_gain_ripple = interp(self.channel_freq, amplifier_freq, self.params.gain_ripple)
self.interpol_nf_ripple =interp(self.channel_freq, amplifier_freq, self.params.nf_ripple)
self.nch = frequencies.size
self.pin_db = lin2db(sum(pin*1e3))
"""check power saturation and correct target_gain accordingly:"""
if self.dp_db is not None:
self.target_pch_db = round(self.dp_db + pref.p0, 2)
self.effective_gain = self.target_pch_db - pref.pi
self.effective_gain = min(self.effective_gain, self.params.p_max - self.pin_db)
self.effective_pch_db = round(pref.pi + self.effective_gain, 2)
"""in power mode: delta_p is defined and can be used to calculate the power target
This power target is used calculate the amplifier gain"""
if self.delta_p is not None:
self.target_pch_out_db = round(self.delta_p + pref.p_span0, 2)
self.effective_gain = self.target_pch_out_db - pref.p_spani
"""check power saturation and correct effective gain & power accordingly:"""
self.effective_gain = min(
self.effective_gain,
self.params.p_max - (pref.p_spani + pref.neq_ch)
)
#print(self.uid, self.effective_gain, self.operational.gain_target)
self.effective_pch_out_db = round(pref.p_spani + self.effective_gain, 2)
"""check power saturation and correct target_gain accordingly:"""
#print(self.uid, self.effective_gain, self.pin_db, pref.p_spani)
self.nf = self._calc_nf()
self.gprofile = self._gain_profile(pin)
pout = (pin + self.noise_profile(baud_rates))*db2lin(self.gprofile)
self.pout_db = lin2db(sum(pout*1e3))
self.operational.gain_target = self.effective_gain
# ase & nli are only calculated in signal bandwidth
# pout_db is not the absolute full output power (negligible if sufficient channels)
def _nf(self, type_def, nf_model, nf_fit_coeff, gain_min, gain_flatmax, gain_target):
#if hybrid raman, use edfa_gain_flatmax attribute, else use gain_flatmax
#gain_flatmax = getattr(params, 'edfa_gain_flatmax', params.gain_flatmax)
pad = max(gain_min - gain_target, 0)
gain_target += pad
dg = max(gain_flatmax - gain_target, 0)
if type_def == 'variable_gain':
g1a = gain_target - nf_model.delta_p - dg
nf_avg = lin2db(db2lin(nf_model.nf1) + db2lin(nf_model.nf2)/db2lin(g1a))
elif type_def == 'fixed_gain':
nf_avg = nf_model.nf0
elif type_def == 'openroadm':
pin_ch = self.pin_db - lin2db(self.nch)
# model OSNR = f(Pin)
nf_avg = pin_ch - polyval(nf_model.nf_coef, pin_ch) + 58
elif type_def == 'advanced_model':
nf_avg = polyval(nf_fit_coeff, -dg)
else:
assert False, "Unrecognized amplifier type, this should have been checked by the JSON loader"
return nf_avg+pad, pad
def _calc_nf(self, avg = False):
"""nf calculation based on 2 models: self.params.nf_model.enabled from json import:
True => 2 stages amp modelling based on precalculated nf1, nf2 and delta_p in build_OA_json
False => polynomial fit based on self.params.nf_fit_coeff"""
# TODO|jla: TBD alarm rising or input VOA padding in case
# gain_min > gain_target TBD:
pad = max(self.params.gain_min - self.effective_gain, 0)
self.att_in = pad
gain_target = self.effective_gain + pad
dg = max(self.params.gain_flatmax - gain_target, 0)
if self.params.type_def == 'variable_gain':
g1a = gain_target - self.params.nf_model.delta_p - dg
nf_avg = lin2db(db2lin(self.params.nf_model.nf1) + db2lin(self.params.nf_model.nf2)/db2lin(g1a))
elif self.params.type_def == 'fixed_gain':
nf_avg = self.params.nf_model.nf0
else:
nf_avg = polyval(self.params.nf_fit_coeff, -dg)
if self.params.type_def == 'dual_stage':
g1 = self.params.preamp_gain_flatmax
g2 = self.effective_gain - g1
nf1_avg, pad = self._nf( self.params.preamp_type_def,
self.params.preamp_nf_model,
self.params.preamp_nf_fit_coeff,
self.params.preamp_gain_min,
self.params.preamp_gain_flatmax,
g1)
#no padding expected for the 1stage because g1 = gain_max
nf2_avg, pad = self._nf( self.params.booster_type_def,
self.params.booster_nf_model,
self.params.booster_nf_fit_coeff,
self.params.booster_gain_min,
self.params.booster_gain_flatmax,
g2)
nf_avg = lin2db(db2lin(nf1_avg) + db2lin(nf2_avg-g1))
#no padding expected for the 1stage because g1 = gain_max
pad = 0
else:
nf_avg, pad = self._nf( self.params.type_def,
self.params.nf_model,
self.params.nf_fit_coeff,
self.params.gain_min,
self.params.gain_flatmax,
self.effective_gain)
self.att_in = pad # not used to attenuate carriers, only used in _repr_ and _str_
if avg:
return nf_avg + pad
return nf_avg
else:
return self.interpol_nf_ripple + nf_avg + pad # input VOA = 1 for 1 NF degradation
return self.interpol_nf_ripple + nf_avg # input VOA = 1 for 1 NF degradation
def noise_profile(self, df):
""" noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
"""noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
noise is calculated at amplifier input
:bw: signal bandwidth = baud rate in Hz
@@ -617,7 +814,7 @@ class Edfa(Node):
# Calculate the target slope - currently assumes equal spaced channels
# TODO|jla: support arbitrary channel spacing
targ_slope = self.operational.tilt_target / (len(nb_channel) - 1)
targ_slope = self.tilt_target / (len(nb_channel) - 1)
# first estimate of DGT scaling
if abs(dgt_slope) > 0.001: # check for zero value due to flat dgt
@@ -682,7 +879,7 @@ class Edfa(Node):
return g1st - voa + array(self.interpol_dgt) * dgts3
def propagate(self, pref, *carriers):
"""add ase noise to the propagating carriers of SpectralInformation"""
"""add ASE noise to the propagating carriers of :class:`.info.SpectralInformation`"""
pin = array([c.power.signal+c.power.nli+c.power.ase for c in carriers]) # pin in W
freq = array([c.frequency for c in carriers])
brate = array([c.baud_rate for c in carriers])
@@ -691,20 +888,22 @@ class Edfa(Node):
gains = db2lin(self.gprofile)
carrier_ases = self.noise_profile(brate)
att = db2lin(self.operational.out_voa)
att = db2lin(self.out_voa)
for gain, carrier_ase, carrier in zip(gains, carrier_ases, carriers):
pwr = carrier.power
pwr = pwr._replace(signal=pwr.signal*gain/att,
nonlinear_interference=pwr.nli*gain/att,
amplified_spontaneous_emission=(pwr.ase+carrier_ase)*gain/att)
nli=pwr.nli*gain/att,
ase=(pwr.ase+carrier_ase)*gain/att)
yield carrier._replace(power=pwr)
def update_pref(self, pref):
return pref._replace(p_span0=pref.p0,
p_spani=pref.pi + self.effective_gain - self.operational.out_voa)
return pref._replace(p_span0=pref.p_span0,
p_spani=pref.p_spani + self.effective_gain - self.out_voa)
def __call__(self, spectral_info):
self.carriers_in = spectral_info.carriers
carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers))
pref = self.update_pref(spectral_info.pref)
return spectral_info.update(carriers=carriers, pref=pref)
self.carriers_out = carriers
return spectral_info._replace(carriers=carriers, pref=pref)

View File

@@ -9,86 +9,207 @@ This module contains functionality for specifying equipment.
'''
from numpy import clip, polyval
from sys import exit
from operator import itemgetter
from math import isclose
from pathlib import Path
from json import loads
from json import load
from gnpy.core.utils import lin2db, db2lin, load_json
from collections import namedtuple
from gnpy.core.elements import Edfa
from gnpy.core.exceptions import EquipmentConfigError
import time
Model_vg = namedtuple('Model_vg', 'nf1 nf2 delta_p')
Model_fg = namedtuple('Model_fg', 'nf0')
Fiber = namedtuple('Fiber', 'type_variety dispersion gamma')
Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \
max_loss padding EOL con_in con_out')
Transceiver = namedtuple('Transceiver', 'type_variety frequency mode')
Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pref')
SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \
power_dbm power_range_db OSNR bit_rate')
AmpBase = namedtuple(
'AmpBase',
'type_variety type_def gain_flatmax gain_min p_max'
' nf_model nf_fit_coeff nf_ripple dgt gain_ripple out_voa_auto allowed_for_design')
class Amp(AmpBase):
def __new__(cls,
type_variety, type_def, gain_flatmax, gain_min, p_max, nf_model=None,
nf_fit_coeff=None, nf_ripple=None, dgt=None, gain_ripple=None,
out_voa_auto=False, allowed_for_design=True):
return super().__new__(cls,
type_variety, type_def, gain_flatmax, gain_min, p_max,
nf_model, nf_fit_coeff, nf_ripple, dgt, gain_ripple,
out_voa_auto, allowed_for_design)
Model_openroadm = namedtuple('Model_openroadm', 'nf_coef')
Model_hybrid = namedtuple('Model_hybrid', 'nf_ram gain_ram edfa_variety')
Model_dual_stage = namedtuple('Model_dual_stage', 'preamp_variety booster_variety')
class common:
def update_attr(self, default_values, kwargs, name):
clean_kwargs = {k:v for k, v in kwargs.items() if v != ''}
for k, v in default_values.items():
setattr(self, k, clean_kwargs.get(k, v))
if k not in clean_kwargs and name != 'Amp':
print(f'\x1b[1;31;40m'+
f'\n WARNING missing {k} attribute in eqpt_config.json[{name}]'+
f'\n default value is {k} = {v}'+
f'\x1b[0m')
time.sleep(1)
class SI(common):
default_values =\
{
"f_min": 191.35e12,
"f_max": 196.1e12,
"baud_rate": 32e9,
"spacing": 50e9,
"power_dbm": 0,
"power_range_db": [0, 0, 0.5],
"roll_off": 0.15,
"tx_osnr": 45,
"sys_margins": 0
}
def __init__(self, **kwargs):
self.update_attr(self.default_values, kwargs, 'SI')
class Span(common):
default_values = \
{
'power_mode': True,
'delta_power_range_db': None,
'max_fiber_lineic_loss_for_raman': 0.25,
'target_extended_gain': 2.5,
'max_length': 150,
'length_units': 'km',
'max_loss': None,
'padding': 10,
'EOL': 0,
'con_in': 0,
'con_out': 0
}
def __init__(self, **kwargs):
self.update_attr(self.default_values, kwargs, 'Span')
class Roadm(common):
default_values = \
{
'target_pch_out_db': -17,
'add_drop_osnr': 100,
'restrictions': {
'preamp_variety_list':[],
'booster_variety_list':[]
}
}
def __init__(self, **kwargs):
self.update_attr(self.default_values, kwargs, 'Roadm')
class Transceiver(common):
default_values = \
{
'type_variety': None,
'frequency': None,
'mode': {}
}
def __init__(self, **kwargs):
self.update_attr(self.default_values, kwargs, 'Transceiver')
class Fiber(common):
default_values = \
{
'type_variety': '',
'dispersion': None,
'gamma': 0
}
def __init__(self, **kwargs):
self.update_attr(self.default_values, kwargs, 'Fiber')
class RamanFiber(common):
default_values = \
{
'type_variety': '',
'dispersion': None,
'gamma': 0,
'raman_efficiency': None
}
def __init__(self, **kwargs):
self.update_attr(self.default_values, kwargs, 'RamanFiber')
for param in ('cr', 'frequency_offset'):
if param not in self.raman_efficiency:
raise EquipmentConfigError(f'RamanFiber.raman_efficiency: missing "{param}" parameter')
if self.raman_efficiency['frequency_offset'] != sorted(self.raman_efficiency['frequency_offset']):
raise EquipmentConfigError(f'RamanFiber.raman_efficiency.frequency_offset is not sorted')
class Amp(common):
default_values = \
{
'f_min': 191.35e12,
'f_max': 196.1e12,
'type_variety': '',
'type_def': '',
'gain_flatmax': None,
'gain_min': None,
'p_max': None,
'nf_model': None,
'dual_stage_model': None,
'nf_fit_coeff': None,
'nf_ripple': None,
'dgt': None,
'gain_ripple': None,
'out_voa_auto': False,
'allowed_for_design': False,
'raman': False
}
def __init__(self, **kwargs):
self.update_attr(self.default_values, kwargs, 'Amp')
@classmethod
def from_advanced_json(cls, filename, **kwargs):
with open(filename) as f:
json_data = loads(f.read())
return cls(**{**kwargs, **json_data, 'type_def':None, 'nf_model':None})
def from_json(cls, filename, **kwargs):
config = Path(filename).parent / 'default_edfa_config.json'
@classmethod
def from_default_json(cls, filename, **kwargs):
with open(filename) as f:
json_data = loads(f.read())
type_variety = kwargs['type_variety']
type_def = kwargs.get('type_def', 'variable_gain') #default compatibility with older json eqpt files
type_def = kwargs.get('type_def', 'variable_gain') # default compatibility with older json eqpt files
nf_def = None
dual_stage_def = None
if type_def == 'fixed_gain':
try:
nf0 = kwargs.pop('nf0')
except KeyError: #nf0 is expected for a fixed gain amp
print(f'missing nf0 value input for amplifier: {type_variety} in eqpt_config.json')
exit()
try: #remove all remaining nf inputs
del kwargs['nf_min']
del kwargs['nf_max']
except KeyError: pass #nf_min and nf_max are not needed for fixed gain amp
raise EquipmentConfigError(f'missing nf0 value input for amplifier: {type_variety} in equipment config')
for k in ('nf_min', 'nf_max'):
try:
del kwargs[k]
except KeyError:
pass
nf_def = Model_fg(nf0)
elif type_def == 'advanced_model':
config = Path(filename).parent / kwargs.pop('advanced_config_from_json')
elif type_def == 'variable_gain':
gain_min, gain_max = kwargs['gain_min'], kwargs['gain_flatmax']
try: #nf_min and nf_max are expected for a variable gain amp
nf_min = kwargs.pop('nf_min')
nf_max = kwargs.pop('nf_max')
except KeyError:
print(f'missing nf_min/max value input for amplifier: {type_variety} in eqpt_config.json')
exit()
raise EquipmentConfigError(f'missing nf_min or nf_max value input for amplifier: {type_variety} in equipment config')
try: #remove all remaining nf inputs
del kwargs['nf0']
except KeyError: pass #nf0 is not needed for variable gain amp
nf1, nf2, delta_p = nf_model(type_variety, gain_min, gain_max, nf_min, nf_max)
nf_def = Model_vg(nf1, nf2, delta_p)
return cls(**{**kwargs, **json_data, 'nf_model': nf_def})
elif type_def == 'openroadm':
try:
nf_coef = kwargs.pop('nf_coef')
except KeyError: #nf_coef is expected for openroadm amp
raise EquipmentConfigError(f'missing nf_coef input for amplifier: {type_variety} in equipment config')
nf_def = Model_openroadm(nf_coef)
elif type_def == 'dual_stage':
try: #nf_ram and gain_ram are expected for a hybrid amp
preamp_variety = kwargs.pop('preamp_variety')
booster_variety = kwargs.pop('booster_variety')
except KeyError:
raise EquipmentConfigError(f'missing preamp/booster variety input for amplifier: {type_variety} in equipment config')
dual_stage_def = Model_dual_stage(preamp_variety, booster_variety)
with open(config, encoding='utf-8') as f:
json_data = load(f)
return cls(**{**kwargs, **json_data,
'nf_model': nf_def, 'dual_stage_model': dual_stage_def})
def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
if nf_min < -10:
print(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}')
exit()
raise EquipmentConfigError(f'Invalid nf_min value {nf_min!r} for amplifier {type_variety}')
if nf_max < -10:
print(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}')
exit()
raise EquipmentConfigError(f'Invalid nf_max value {nf_max!r} for amplifier {type_variety}')
# NF estimation model based on nf_min and nf_max
# delta_p: max power dB difference between first and second stage coils
@@ -103,8 +224,7 @@ def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
nf1 = lin2db(db2lin(nf_min) - db2lin(nf2)/db2lin(g1a_max))
if nf1 < 4:
print(f'First coil value too low {nf1} for amplifier {type_variety}')
exit()
raise EquipmentConfigError(f'First coil value too low {nf1} for amplifier {type_variety}')
# Check 1 dB < delta_p < 6 dB to ensure nf_min and nf_max values make sense.
# There shouldn't be high nf differences between the two coils:
@@ -115,21 +235,18 @@ def nf_model(type_variety, gain_min, gain_max, nf_min, nf_max):
g1a_max = lin2db(db2lin(nf2) / (db2lin(nf_min) - db2lin(nf1)))
delta_p = gain_max - g1a_max
g1a_min = gain_min - (gain_max-gain_min) - delta_p
if not 1 < delta_p < 6:
print(f'Computed \N{greek capital letter delta}P invalid \
if not 1 < delta_p < 11:
raise EquipmentConfigError(f'Computed \N{greek capital letter delta}P invalid \
\n 1st coil vs 2nd coil calculated DeltaP {delta_p:.2f} for \
\n amplifier {type_variety} is not valid: revise inputs \
\n calculated 1st coil NF = {nf1:.2f}, 2nd coil NF = {nf2:.2f}')
exit()
# Check calculated values for nf1 and nf2
calc_nf_min = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_max))
if not isclose(nf_min, calc_nf_min, abs_tol=0.01):
print(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}')
exit()
raise EquipmentConfigError(f'nf_min does not match calc_nf_min, {nf_min} vs {calc_nf_min} for amp {type_variety}')
calc_nf_max = lin2db(db2lin(nf1) + db2lin(nf2)/db2lin(g1a_min))
if not isclose(nf_max, calc_nf_max, abs_tol=0.01):
print(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}')
exit()
raise EquipmentConfigError(f'nf_max does not match calc_nf_max, {nf_max} vs {calc_nf_max} for amp {type_variety}')
return nf1, nf2, delta_p
@@ -137,66 +254,128 @@ def edfa_nf(gain_target, variety_type, equipment):
amp_params = equipment['Edfa'][variety_type]
amp = Edfa(
uid = f'calc_NF',
params = amp_params._asdict(),
params = amp_params.__dict__,
operational = {
'gain_target': gain_target,
'tilt_target': 0,
})
'tilt_target': 0
}
)
amp.pin_db = 0
amp.nch = 88
return amp._calc_nf(True)
def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=False):
"""return the trx and SI parameters from eqpt_config for a given type_variety and mode (ie format)"""
trx_params = {}
default_si_data = equipment['SI']['default']
try:
trxs = equipment['Transceiver']
mode_params = next(mode for trx in trxs \
if trx == trx_type_variety \
for mode in trxs[trx].mode \
if mode['format'] == trx_mode)
trx_params = {**mode_params}
trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency
#if called from path_requests_run.py, trx_mode is filled with None when not specified by user
#if called from transmission_main.py, trx_mode is ''
if trx_mode is not None:
mode_params = next(mode for trx in trxs \
if trx == trx_type_variety \
for mode in trxs[trx].mode \
if mode['format'] == trx_mode)
trx_params = {**mode_params}
# sanity check: spacing baudrate must be smaller than min spacing
if trx_params['baud_rate'] > trx_params['min_spacing'] :
raise EquipmentConfigError(f'Inconsistency in equipment library:\n Transpoder "{trx_type_variety}" mode "{trx_params["format"]}" '+\
f'has baud rate: {trx_params["baud_rate"]*1e-9} GHz greater than min_spacing {trx_params["min_spacing"]*1e-9}.')
else:
mode_params = {"format": "undetermined",
"baud_rate": None,
"OSNR": None,
"bit_rate": None,
"roll_off": None,
"tx_osnr":None,
"min_spacing":None,
"cost":None}
trx_params = {**mode_params}
trx_params['f_min'] = equipment['Transceiver'][trx_type_variety].frequency['min']
trx_params['f_max'] = equipment['Transceiver'][trx_type_variety].frequency['max']
# TODO: novel automatic feature maybe unwanted if spacing is specified
trx_params['spacing'] = automatic_spacing(trx_params['baud_rate'])
# trx_params['spacing'] = automatic_spacing(trx_params['baud_rate'])
# temp = trx_params['spacing']
# print(f'spacing {temp}')
except StopIteration :
if error_message:
print(f'could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library')
print('Computation stopped.')
exit()
raise EquipmentConfigError(f'Computation stoped: could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library')
else:
# default transponder charcteristics
trx_params['frequency'] = {'min': default_si_data.f_min, 'max': default_si_data.f_max}
# mainly used with transmission_main_example.py
trx_params['f_min'] = default_si_data.f_min
trx_params['f_max'] = default_si_data.f_max
trx_params['baud_rate'] = default_si_data.baud_rate
trx_params['spacing'] = default_si_data.spacing
trx_params['OSNR'] = default_si_data.OSNR
trx_params['bit_rate'] = default_si_data.bit_rate
trx_params['OSNR'] = None
trx_params['bit_rate'] = None
trx_params['cost'] = None
trx_params['roll_off'] = default_si_data.roll_off
trx_params['tx_osnr'] = default_si_data.tx_osnr
trx_params['min_spacing'] = None
nch = automatic_nch(trx_params['f_min'], trx_params['f_max'], trx_params['spacing'])
trx_params['nb_channel'] = nch
print(f'There are {nch} channels propagating')
trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3
trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'],
trx_params['frequency']['max'],
trx_params['spacing'])
print('N channels = ', trx_params['nb_channel'])
return trx_params
def automatic_spacing(baud_rate):
"""return the min possible channel spacing for a given baud rate"""
spacing_list = [(38e9,50e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples
#[(max_baud_rate, spacing_for_this_baud_rate)]
acceptable_spacing_list = list(filter(lambda x : x[0]>baud_rate, spacing_list))
if len(acceptable_spacing_list) < 1:
#can't find an adequate spacing from the list, so default to:
return baud_rate*1.2
else:
#chose the lowest possible spacing
return min(acceptable_spacing_list, key=itemgetter(0))[1]
# TODO : this should parametrized in a cfg file
# list of possible tuples [(max_baud_rate, spacing_for_this_baud_rate)]
spacing_list = [(33e9, 37.5e9), (38e9, 50e9), (50e9, 62.5e9), (67e9, 75e9), (92e9, 100e9)]
return min((s[1] for s in spacing_list if s[0] > baud_rate), default=baud_rate*1.2)
def automatic_nch(f_min, f_max, spacing):
return int((f_max - f_min)//spacing)
def automatic_fmax(f_min, spacing, nch):
return f_min + spacing * nch
def load_equipment(filename):
json_data = load_json(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():
if edfa.type_def == 'dual_stage':
edfa_preamp = edfa_dict[edfa.dual_stage_model.preamp_variety]
edfa_booster = edfa_dict[edfa.dual_stage_model.booster_variety]
for key, value in edfa_preamp.__dict__.items():
attr_k = 'preamp_' + key
setattr(edfa, attr_k, value)
for key, value in edfa_booster.__dict__.items():
attr_k = 'booster_' + key
setattr(edfa, attr_k, value)
edfa.p_max = edfa_booster.p_max
edfa.gain_flatmax = edfa_booster.gain_flatmax + edfa_preamp.gain_flatmax
if edfa.gain_min < edfa_preamp.gain_min:
raise EquipmentConfigError(f'Dual stage {edfa.type_variety} min gain is lower than its preamp min gain')
return equipment
def roadm_restrictions_sanity_check(equipment):
""" verifies that booster and preamp restrictions specified in roadm equipment are listed
in the edfa.
"""
restrictions = equipment['Roadm']['default'].restrictions['booster_variety_list'] + \
equipment['Roadm']['default'].restrictions['preamp_variety_list']
for amp_name in restrictions:
if amp_name not in equipment['Edfa']:
raise EquipmentConfigError(f'ROADM restriction {amp_name} does not refer to a defined EDFA name')
def equipment_from_json(json_data, filename):
"""build global dictionnary eqpt_library that stores all eqpt characteristics:
edfa type type_variety, fiber type_variety
@@ -208,17 +387,15 @@ def equipment_from_json(json_data, filename):
"""
equipment = {}
for key, entries in json_data.items():
equipment[key] = {}
typ = globals()[key]
for entry in entries:
if key not in equipment:
equipment[key] = {}
subkey = entry.get('type_variety', 'default')
typ = globals()[key]
if key == 'Edfa':
if 'advanced_config_from_json' in entry:
config = Path(filename).parent / entry.pop('advanced_config_from_json')
typ = lambda **kws: Amp.from_advanced_json(config, **kws)
else:
config = Path(filename).parent / 'default_edfa_config.json'
typ = lambda **kws: Amp.from_default_json(config, **kws)
equipment[key][subkey] = typ(**entry)
equipment[key][subkey] = Amp.from_json(filename, **entry)
else:
equipment[key][subkey] = typ(**entry)
equipment = update_trx_osnr(equipment)
equipment = update_dual_stage(equipment)
roadm_restrictions_sanity_check(equipment)
return equipment

29
gnpy/core/exceptions.py Normal file
View File

@@ -0,0 +1,29 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
gnpy.core.exceptions
====================
Exceptions thrown by other gnpy modules
'''
class ConfigurationError(Exception):
'''User-provided configuration contains an error'''
class EquipmentConfigError(ConfigurationError):
'''Incomplete or wrong configuration within the equipment library'''
class NetworkTopologyError(ConfigurationError):
'''Topology of user-provided network is wrong'''
class ServiceError(Exception):
'''Service of user-provided request is wrong'''
class DisjunctionError(ServiceError):
'''Disjunction of user-provided request can not be satisfied'''
class SpectrumError(Exception):
'''Spectrum errors of the program'''

View File

@@ -5,68 +5,50 @@
gnpy.core.info
==============
This module contains classes for modelling SpectralInformation.
This module contains classes for modelling :class:`SpectralInformation`.
'''
from collections import namedtuple
from numpy import array
from gnpy.core.utils import lin2db
from gnpy.core.utils import lin2db, db2lin
from json import loads
from gnpy.core.utils import load_json
from gnpy.core.equipment import automatic_nch, automatic_spacing
class ConvenienceAccess:
def __init_subclass__(cls):
for abbrev, field in getattr(cls, '_ABBREVS', {}).items():
setattr(cls, abbrev, property(lambda self, f=field: getattr(self, f)))
def update(self, **kwargs):
for abbrev, field in getattr(self, '_ABBREVS', {}).items():
if abbrev in kwargs:
kwargs[field] = kwargs.pop(abbrev)
return self._replace(**kwargs)
class Power(namedtuple('Power', 'signal nli ase')):
"""carriers power in W"""
class Power(namedtuple('Power', 'signal nonlinear_interference amplified_spontaneous_emission'), ConvenienceAccess):
_ABBREVS = {'nli': 'nonlinear_interference',
'ase': 'amplified_spontaneous_emission',}
class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power'), ConvenienceAccess):
_ABBREVS = {'channel': 'channel_number',
'num_chan': 'channel_number',
'ffs': 'frequency',
'freq': 'frequency',}
class Pref(namedtuple('Pref', 'p_span0, p_spani'), ConvenienceAccess):
_ABBREVS = {'p0' : 'p_span0',
'pi' : 'p_spani'}
class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers'), ConvenienceAccess):
def __new__(cls, pref=Pref(0, 0), *carriers):
return super().__new__(cls, pref, carriers)
def merge_input_spectral_information(*si):
"""mix channel combs of different baud rates and power"""
#TODO
class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off power')):
pass
def create_input_spectral_information(f_min, roll_off, baud_rate, power, spacing, nb_channel):
class Pref(namedtuple('Pref', 'p_span0, p_spani, neq_ch ')):
"""noiseless reference power in dBm:
p_span0: inital target carrier power
p_spani: carrier power after element i
neq_ch: equivalent channel count in dB"""
class SpectralInformation(namedtuple('SpectralInformation', 'pref carriers')):
def __new__(cls, pref, carriers):
return super().__new__(cls, pref, carriers)
def create_input_spectral_information(f_min, f_max, roll_off, baud_rate, power, spacing):
# pref in dB : convert power lin into power in dB
pref = lin2db(power * 1e3)
si = SpectralInformation(pref=Pref(pref, pref))
si = si.update(carriers=[
nb_channel = automatic_nch(f_min, f_max, spacing)
si = SpectralInformation(
pref=Pref(pref, pref, lin2db(nb_channel)),
carriers=[
Channel(f, (f_min+spacing*f),
baud_rate, roll_off, Power(power, 0, 0)) for f in range(1,nb_channel+1)
])
return si
if __name__ == '__main__':
pref = lin2db(power * 1e3)
si = SpectralInformation(
@@ -80,11 +62,11 @@ if __name__ == '__main__':
si = SpectralInformation()
spacing = 0.05 # THz
si = si.update(carriers=tuple(Channel(f+1, 191.3+spacing*(f+1), 32e9, 0.15, Power(1e-3, f, 1)) for f in range(96)))
si = si._replace(carriers=tuple(Channel(f+1, 191.3+spacing*(f+1), 32e9, 0.15, Power(1e-3, f, 1)) for f in range(96)))
print(f'si = {si}')
print(f'si = {si.carriers[0].power.nli}')
print(f'si = {si.carriers[20].power.nli}')
si2 = si.update(carriers=tuple(c.update(power = c.power.update(nli = c.power.nli * 1e5))
si2 = si._replace(carriers=tuple(c._replace(power = c.power._replace(nli = c.power.nli * 1e5))
for c in si.carriers))
print(f'si2 = {si2}')

View File

@@ -11,24 +11,27 @@ This module contains functions for constructing networks of network elements.
from gnpy.core.convert import convert_file
from networkx import DiGraph
from numpy import arange
from scipy.interpolate import interp1d
from logging import getLogger
from os import path
from operator import itemgetter
from operator import itemgetter, attrgetter
from gnpy.core import elements
from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused
from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused, RamanFiber
from gnpy.core.equipment import edfa_nf
from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError
from gnpy.core.units import UNITS
from gnpy.core.utils import load_json, save_json, round2float, db2lin, lin2db
from sys import exit
from gnpy.core.utils import (load_json, save_json, round2float, db2lin,
merge_amplifier_restrictions)
from gnpy.core.science_utils import SimParams
from collections import namedtuple
logger = getLogger(__name__)
def load_network(filename, equipment):
def load_network(filename, equipment, name_matching = False):
json_filename = ''
if filename.suffix.lower() == '.xls':
logger.info('Automatically generating topology JSON file')
json_filename = convert_file(filename)
json_filename = convert_file(filename, name_matching)
elif filename.suffix.lower() == '.json':
json_filename = filename
else:
@@ -51,11 +54,12 @@ def network_from_json(json_data, equipment):
variety = el_config.pop('type_variety', 'default')
if typ in equipment and variety in equipment[typ]:
extra_params = equipment[typ][variety]
el_config.setdefault('params', {}).update(extra_params._asdict())
elif typ in ['Edfa', 'Fiber']: #catch it now because the code will crash later!
print( f'The {typ} of variety type {variety} was not recognized:'
temp = el_config.setdefault('params', {})
temp = merge_amplifier_restrictions(temp, extra_params.__dict__)
el_config['params'] = temp
elif typ in ['Edfa', 'Fiber']: # catch it now because the code will crash later!
raise ConfigurationError(f'The {typ} of variety type {variety} was not recognized:'
'\nplease check it is properly defined in the eqpt_config json file')
exit()
cls = getattr(elements, typ)
el = cls(**el_config)
g.add_node(el)
@@ -64,7 +68,14 @@ def network_from_json(json_data, equipment):
for cx in json_data['connections']:
from_node, to_node = cx['from_node'], cx['to_node']
g.add_edge(nodes[from_node], nodes[to_node])
try:
if isinstance(nodes[from_node], Fiber):
edge_length = nodes[from_node].params.length
else:
edge_length = 0.01
g.add_edge(nodes[from_node], nodes[to_node], weight = edge_length)
except KeyError:
raise NetworkTopologyError(f'can not find {from_node} or {to_node} defined in {cx}')
return g
@@ -81,16 +92,27 @@ def network_to_json(network):
data.update(connections)
return data
def select_edfa(gain_target, power_target, equipment):
def select_edfa(raman_allowed, gain_target, power_target, equipment, uid, restrictions=None):
"""amplifer selection algorithm
@Orange Jean-Luc Augé
"""
Edfa_list = namedtuple('Edfa_list', 'variety power gain nf')
TARGET_EXTENDED_GAIN = 2.1
#MAX_EXTENDED_GAIN = 5
edfa_dict = equipment['Edfa']
Edfa_list = namedtuple('Edfa_list', 'variety power gain_min nf')
TARGET_EXTENDED_GAIN = equipment['Span']['default'].target_extended_gain
# for roadm restriction only: create a dict including not allowed for design amps
# because main use case is to have specific radm amp which are not allowed for ILA
# with the auto design
edfa_dict = {name: amp for (name, amp) in equipment['Edfa'].items()
if restrictions is None or name in restrictions}
pin = power_target - gain_target
# create 2 list of available amplifiers with relevant attributes for their selection
# edfa list with:
# extended gain min allowance of 3dB: could be parametrized, but a bit complex
# extended gain max allowance TARGET_EXTENDED_GAIN is coming from eqpt_config.json
# power attribut include power AND gain limitations
edfa_list = [Edfa_list(
variety=edfa_variety,
power=min(
@@ -100,80 +122,118 @@ def select_edfa(gain_target, power_target, equipment):
edfa.p_max
)
-power_target,
gain=edfa.gain_flatmax-gain_target,
gain_min=
gain_target+3
-edfa.gain_min,
nf=edfa_nf(gain_target, edfa_variety, equipment)) \
for edfa_variety, edfa in edfa_dict.items()
if edfa.allowed_for_design]
if ((edfa.allowed_for_design or restrictions is not None) and not edfa.raman)]
acceptable_gain_list = \
list(filter(lambda x : x.gain>-TARGET_EXTENDED_GAIN, edfa_list))
if len(acceptable_gain_list) < 1:
#no amplifier satisfies the required gain, so pick the highest gain:
gain_max = max(edfa_list, key=itemgetter(2)).gain
#pick up all amplifiers that share this max gain:
acceptable_gain_list = \
list(filter(lambda x : x.gain-gain_max>-0.1, edfa_list))
acceptable_power_list = \
list(filter(lambda x : x.power>=0, acceptable_gain_list))
#consider a Raman list because of different gain_min requirement:
#do not allow extended gain min for Raman
raman_list = [Edfa_list(
variety=edfa_variety,
power=min(
pin
+edfa.gain_flatmax
+TARGET_EXTENDED_GAIN,
edfa.p_max
)
-power_target,
gain_min=
gain_target
-edfa.gain_min,
nf=edfa_nf(gain_target, edfa_variety, equipment))
for edfa_variety, edfa in edfa_dict.items()
if (edfa.allowed_for_design and edfa.raman)] \
if raman_allowed else []
#merge raman and edfa lists
amp_list = edfa_list + raman_list
#filter on min gain limitation:
acceptable_gain_min_list = [x for x in amp_list if x.gain_min>0]
if len(acceptable_gain_min_list) < 1:
#do not take this empty list into account for the rest of the code
#but issue a warning to the user and do not consider Raman
#Raman below min gain should not be allowed because i is meant to be a design requirement
#and raman padding at the amplifier input is impossible!
if len(edfa_list) < 1:
raise ConfigurationError(f'auto_design could not find any amplifier \
to satisfy min gain requirement in node {uid} \
please increase span fiber padding')
else:
# TODO: convert to logging
print(
f'\x1b[1;31;40m'\
+ f'WARNING: target gain in node {uid} is below all available amplifiers min gain: \
amplifier input padding will be assumed, consider increase span fiber padding instead'\
+ '\x1b[0m'
)
acceptable_gain_min_list = edfa_list
#filter on gain+power limitation:
#this list checks both the gain and the power requirement
#because of the way .power is calculated in the list
acceptable_power_list = [x for x in acceptable_gain_min_list if x.power>0]
if len(acceptable_power_list) < 1:
#no amplifier satisfies the required power, so pick the highest power:
power_max = \
max(acceptable_gain_list, key=itemgetter(1)).power
#pick up all amplifiers that share this max gain:
acceptable_power_list = \
list(filter(lambda x : x.power-power_max>-0.1, acceptable_gain_list))
#no amplifier satisfies the required power, so pick the highest power(s):
power_max = max(acceptable_gain_min_list, key=attrgetter('power')).power
#check and pick if other amplifiers may have a similar gain/power
#allow a 0.3dB power range
#this allows to chose an amplifier with a better NF subsequentely
acceptable_power_list = [x for x in acceptable_gain_min_list
if x.power-power_max>-0.3]
# gain and power requirements are resolved,
# =>chose the amp with the best NF among the acceptable ones:
return min(acceptable_power_list, key=itemgetter(3)).variety #filter on NF
selected_edfa = min(acceptable_power_list, key=attrgetter('nf')) #filter on NF
#check what are the gain and power limitations of this amp
power_reduction = round(min(selected_edfa.power, 0),2)
if power_reduction < -0.5:
print(
f'\x1b[1;31;40m'\
+ f'WARNING: target gain and power in node {uid}\n \
is beyond all available amplifiers capabilities and/or extended_gain_range:\n\
a power reduction of {power_reduction} is applied\n'\
+ '\x1b[0m'
)
def set_roadm_loss(network, equipment, pref_ch_db):
roadms = [roadm for roadm in network if isinstance(roadm, Roadm)]
power_mode = equipment['Spans']['default'].power_mode
default_roadm_loss = equipment['Roadms']['default'].gain_mode_default_loss
pref_roadm_db = equipment['Roadms']['default'].power_mode_pref
roadm_loss = pref_ch_db - pref_roadm_db
for roadm in roadms:
if power_mode:
roadm.loss = roadm_loss
elif roadm.loss == None:
roadm.loss = default_roadm_loss
return selected_edfa.variety, power_reduction
def target_power(dp_from_gain, network, node, equipment): #get_fiber_dp
def target_power(network, node, equipment): #get_fiber_dp
SPAN_LOSS_REF = 20
POWER_SLOPE = 0.3
power_mode = equipment['Spans']['default'].power_mode
dp_range = list(equipment['Spans']['default'].delta_power_range_db)
power_mode = equipment['Span']['default'].power_mode
dp_range = list(equipment['Span']['default'].delta_power_range_db)
node_loss = span_loss(network, node)
dp_gain_mode = 0
try:
dp_power_mode = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2])
dp_power_mode = max(dp_range[0], dp_power_mode)
dp_power_mode = min(dp_range[1], dp_power_mode)
dp = round2float((node_loss - SPAN_LOSS_REF) * POWER_SLOPE, dp_range[2])
dp = max(dp_range[0], dp)
dp = min(dp_range[1], dp)
except KeyError:
print(f'invalid delta_power_range_db definition in eqpt_config[Spans]'
raise ConfigurationError(f'invalid delta_power_range_db definition in eqpt_config[Span]'
f'delta_power_range_db: [lower_bound, upper_bound, step]')
exit()
if dp_from_gain:
dp_power_mode = dp_from_gain
dp_gain_mode = dp_from_gain
if isinstance(node, Roadm):
dp_power_mode = 0
dp = dp_power_mode if power_mode else dp_gain_mode
#print(f'{repr(node)} delta power in:\n{dp}dB')
dp = 0
return dp
def prev_node_generator(network, node):
"""fused spans interest:
iterate over all predecessors while they are Fused or Fiber type"""
prev_node = next(n for n in network.predecessors(node))
try:
prev_node = next(n for n in network.predecessors(node))
except StopIteration:
raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology')
# yield and re-iterate
if isinstance(prev_node, Fused) or isinstance(node, Fused):
if isinstance(prev_node, Fused) or isinstance(node, Fused) and not isinstance(prev_node, Roadm):
yield prev_node
yield from prev_node_generator(network, prev_node)
else:
@@ -182,9 +242,12 @@ def prev_node_generator(network, node):
def next_node_generator(network, node):
"""fused spans interest:
iterate over all successors while they are Fused or Fiber type"""
next_node = next(n for n in network.successors(node))
try:
next_node = next(n for n in network.successors(node))
except StopIteration:
raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology')
# yield and re-iterate
if isinstance(next_node, Fused) or isinstance(node, Fused):
if isinstance(next_node, Fused) or isinstance(node, Fused) and not isinstance(next_node, Roadm):
yield next_node
yield from next_node_generator(network, next_node)
else:
@@ -226,23 +289,22 @@ def find_last_node(network, node):
pass
return this_node
def set_amplifier_voa(amp, pref_total_db, power_mode):
VOA_MARGIN = 0
if amp.operational.out_voa is None:
def set_amplifier_voa(amp, power_target, power_mode):
VOA_MARGIN = 1 #do not maximize the VOA optimization
if amp.out_voa is None:
if power_mode:
gain_target = amp.operational.gain_target
pout = pref_total_db + amp.dp_db
voa = min(amp.params.p_max-pout,
amp.params.gain_flatmax-amp.operational.gain_target)
voa = round2float(max(voa, 0), 0.5) - VOA_MARGIN if amp.params.out_voa_auto else 0
amp.dp_db = amp.dp_db + voa
amp.operational.gain_target = amp.operational.gain_target + voa
gain_target = amp.effective_gain
voa = min(amp.params.p_max-power_target,
amp.params.gain_flatmax-amp.effective_gain)
voa = max(round2float(max(voa, 0), 0.5) - VOA_MARGIN, 0) if amp.params.out_voa_auto else 0
amp.delta_p = amp.delta_p + voa
amp.effective_gain = amp.effective_gain + voa
else:
voa = 0 # no output voa optimization in gain mode
amp.operational.out_voa = voa
amp.out_voa = voa
def set_egress_amplifier(network, roadm, equipment, pref_total_db):
power_mode = equipment['Spans']['default'].power_mode
power_mode = equipment['Span']['default'].power_mode
next_oms = (n for n in network.successors(roadm) if not isinstance(n, Transceiver))
for oms in next_oms:
#go through all the OMS departing from the Roadm
@@ -253,30 +315,82 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
# node = find_last_node(next_node)
# next_node = next(n for n in network.successors(node))
# next_node = find_last_node(next_node)
prev_dp = 0
dp = 0
if node.per_degree_target_pch_out_db:
# find the target power on this degree
try:
prev_dp = next(el["target_pch_out_db"] for el in \
node.per_degree_target_pch_out_db if el["to_node"]==next_node.uid)
except StopIteration:
# if no target power is defined on this degree use the global one
prev_dp = getattr(node.params, 'target_pch_out_db', 0)
else:
# if no per degree target power is given use the global one
prev_dp = getattr(node.params, 'target_pch_out_db', 0)
dp = prev_dp
prev_voa = 0
voa = 0
while True:
#go through all nodes in the OMS (loop until next Roadm instance)
if isinstance(node, Edfa):
node_loss = span_loss(network, prev_node)
dp_from_gain = prev_dp + node.operational.gain_target - node_loss \
if node.operational.gain_target > 0 else None
dp = target_power(dp_from_gain, network, next_node, equipment)
gain_target = node_loss + dp - prev_dp
voa = node.out_voa if node.out_voa else 0
if node.delta_p is None:
dp = target_power(network, next_node, equipment)
else:
dp = node.delta_p
gain_from_dp = node_loss + dp - prev_dp + prev_voa
if node.effective_gain is None or power_mode:
gain_target = gain_from_dp
else: #gain mode with effective_gain
gain_target = node.effective_gain
dp = prev_dp - node_loss + gain_target
if power_mode:
node.dp_db = dp
node.operational.gain_target = gain_target
power_target = pref_total_db + dp
if node.params.type_variety == '':
power_target = pref_total_db + dp
edfa_variety = select_edfa(gain_target, power_target, equipment)
raman_allowed = False
if isinstance(prev_node, Fiber):
max_fiber_lineic_loss_for_raman = \
equipment['Span']['default'].max_fiber_lineic_loss_for_raman
raman_allowed = prev_node.params.loss_coef < max_fiber_lineic_loss_for_raman
# implementation of restrictions on roadm boosters
if isinstance(prev_node,Roadm):
if prev_node.restrictions['booster_variety_list']:
restrictions = prev_node.restrictions['booster_variety_list']
else:
restrictions = None
elif isinstance(next_node,Roadm):
# implementation of restrictions on roadm preamp
if next_node.restrictions['preamp_variety_list']:
restrictions = next_node.restrictions['preamp_variety_list']
else:
restrictions = None
else:
restrictions = None
if node.params.type_variety == '':
edfa_variety, power_reduction = select_edfa(raman_allowed,
gain_target, power_target, equipment, node.uid, restrictions)
extra_params = equipment['Edfa'][edfa_variety]
node.params.update_params(extra_params._asdict())
set_amplifier_voa(node, pref_total_db, power_mode)
node.params.update_params(extra_params.__dict__)
dp += power_reduction
gain_target += power_reduction
elif node.params.raman and not raman_allowed:
print(
f'\x1b[1;31;40m'\
+ f'WARNING: raman is used in node {node.uid}\n \
but fiber lineic loss is above threshold\n'\
+ '\x1b[0m'
)
node.delta_p = dp if power_mode else None
node.effective_gain = gain_target
set_amplifier_voa(node, power_target, power_mode)
if isinstance(next_node, Roadm) or isinstance(next_node, Transceiver):
break
prev_dp = dp
prev_voa = voa
prev_node = node
node = next_node
# print(f'{node.uid}')
@@ -292,13 +406,25 @@ def add_egress_amplifier(network, node):
amp = Edfa(
uid = f'Edfa{i}_{node.uid}',
params = {},
metadata = {
'location': {
'latitude': (node.lat * 2 + next_node.lat * 2) / 4,
'longitude': (node.lng * 2 + next_node.lng * 2) / 4,
'city': node.loc.city,
'region': node.loc.region,
}
},
operational = {
'gain_target': 0,
'gain_target': None,
'tilt_target': 0,
})
network.add_node(amp)
network.add_edge(node, amp)
network.add_edge(amp, next_node)
if isinstance(node,Fiber):
edgeweight = node.params.length
else:
edgeweight = 0.01
network.add_edge(node, amp, weight = edgeweight)
network.add_edge(amp, next_node, weight = 0.01)
def calculate_new_length(fiber_length, bounds, target_length):
@@ -334,37 +460,48 @@ def split_fiber(network, fiber, bounds, target_length, equipment):
next_node = next(network.successors(fiber))
prev_node = next(network.predecessors(fiber))
except StopIteration:
print(f'{repr(fiber)} is not properly connected, please check network topology')
exit()
raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology')
network.remove_edge(fiber, next_node)
network.remove_edge(prev_node, fiber)
network.remove_node(fiber)
# update connector loss parameter with default values
fiber_params = fiber.params._asdict()
fiber_params['length'] = new_length / UNITS[fiber.params.length_units]
fiber_params['con_in'] = fiber.con_in
fiber_params['con_out'] = fiber.con_out
new_spans = [
Fiber(
uid = f'{fiber.uid}_({span}/{n_spans})',
metadata = fiber.metadata,
params = fiber_params
) for span in range(n_spans)
]
for new_span in new_spans:
new_span.length = new_length
network.add_node(new_span)
network.add_edge(prev_node, new_span)
prev_node = new_span
network.add_edge(prev_node, next_node)
def add_connector_loss(fibers, con_in, con_out, EOL):
for fiber in fibers:
if fiber.con_in is None: fiber.con_in = con_in
if fiber.con_out is None:
fiber.con_out = con_out #con_out includes EOL
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)
for span, lng, lat in zip(range(n_spans), xpos, ypos):
new_span = Fiber(uid = f'{fiber.uid}_({span+1}/{n_spans})',
metadata = {
'location': {
'latitude': lat,
'longitude': lng,
'city': fiber.loc.city,
'region': fiber.loc.region,
}
},
params = fiber_params)
if isinstance(prev_node,Fiber):
edgeweight = prev_node.params.length
else:
fiber.con_out = fiber.con_out+EOL
edgeweight = 0.01
network.add_edge(prev_node, new_span, weight = edgeweight)
prev_node = new_span
if isinstance(prev_node,Fiber):
edgeweight = prev_node.params.length
else:
edgeweight = 0.01
network.add_edge(prev_node, next_node, weight = edgeweight)
def add_connector_loss(network, fibers, default_con_in, default_con_out, EOL):
for fiber in fibers:
if fiber.con_in is None: fiber.con_in = default_con_in
if fiber.con_out is None: fiber.con_out = default_con_out
next_node = next(n for n in network.successors(fiber))
if not isinstance(next_node, Fused):
fiber.con_out += EOL
def add_fiber_padding(network, fibers, padding):
"""last_fibers = (fiber for n in network.nodes()
@@ -373,30 +510,35 @@ def add_fiber_padding(network, fibers, padding):
if isinstance(fiber, Fiber))"""
for fiber in fibers:
this_span_loss = span_loss(network, fiber)
next_node = next(network.successors(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, Fused)):
#add a padding att_in at the input of the 1st fiber:
#address the case when several fibers are spliced together
first_fiber = find_first_node(network, fiber)
if first_fiber.att_in is None:
first_fiber.att_in = padding - this_span_loss
else :
first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
# in order to support no booster , fused might be placed
# just after a roadm: need to check that first_fiber is really a fiber
if isinstance(first_fiber,Fiber):
if first_fiber.att_in is None:
first_fiber.att_in = padding - this_span_loss
else:
first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
def build_network(network, equipment, pref_ch_db, pref_total_db):
default_span_data = equipment['Spans']['default']
default_span_data = equipment['Span']['default']
max_length = int(default_span_data.max_length * UNITS[default_span_data.length_units])
min_length = max(int(default_span_data.padding/0.2*1e3),50_000)
bounds = range(min_length, max_length)
target_length = max(min_length, 90_000)
con_in = default_span_data.con_in
con_out = default_span_data.con_out + default_span_data.EOL
default_con_in = default_span_data.con_in
default_con_out = default_span_data.con_out
padding = default_span_data.padding
#set raodm loss for gain_mode before to build network
set_roadm_loss(network, equipment, pref_ch_db)
#set roadm loss for gain_mode before to build network
fibers = [f for f in network.nodes() if isinstance(f, Fiber)]
add_connector_loss(fibers, con_in, con_out, default_span_data.EOL)
add_connector_loss(network, fibers, default_con_in, default_con_out, default_span_data.EOL)
add_fiber_padding(network, fibers, padding)
# don't group split fiber and add amp in the same loop
# =>for code clarity (at the expense of speed):
@@ -405,6 +547,7 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
amplified_nodes = [n for n in network.nodes()
if isinstance(n, Fiber) or isinstance(n, Roadm)]
for node in amplified_nodes:
add_egress_amplifier(network, node)
@@ -418,3 +561,11 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
for t in trx:
set_egress_amplifier(network, t, equipment, pref_total_db)
def load_sim_params(filename):
sim_params = load_json(filename)
return SimParams(params=sim_params)
def configure_network(network, sim_params):
for node in network.nodes:
if isinstance(node, RamanFiber):
node.sim_params = sim_params

View File

@@ -8,13 +8,13 @@ gnpy.core.node
This module contains the base class for a network element.
Strictly, a network element is any callable which accepts an immutable
.info.SpectralInformation object and returns a .info.SpectralInformation object
(a copy.)
:class:`.info.SpectralInformation` object and returns an :class:`.info.SpectralInformation` object
(a copy).
Network elements MUST implement two attributes .uid and .name representing a
unique identifier and a printable name.
This base class provides a mode convenient way to define a network element
This base class provides a more convenient way to define a network element
via subclassing.
'''
@@ -26,17 +26,19 @@ class Location(namedtuple('Location', 'latitude longitude city region')):
return super().__new__(cls, latitude, longitude, city, region)
class Node:
def __init__(self, uid, name=None, params=None, metadata={'location':{}}, operational=None):
def __init__(self, uid, name=None, params=None, metadata=None, operational=None):
if name is None:
name = uid
self.uid, self.name = uid, name
if metadata is None:
metadata = {'location': {}}
if metadata and not isinstance(metadata.get('location'), Location):
metadata['location'] = Location(**metadata.pop('location', {}))
self.params, self.metadata, self.operational = params, metadata, operational
@property
def coords(self):
return tuple(self.lng, self.lat)
return self.lng, self.lat
@property
def location(self):

File diff suppressed because it is too large Load Diff

820
gnpy/core/science_utils.py Normal file
View File

@@ -0,0 +1,820 @@
import numpy as np
from operator import attrgetter
from collections import namedtuple
from logging import getLogger
import scipy.constants as ph
from scipy.integrate import solve_bvp
from scipy.integrate import cumtrapz
from scipy.interpolate import interp1d
from scipy.optimize import OptimizeResult
from gnpy.core.utils import db2lin
logger = getLogger(__name__)
class RamanParams():
def __init__(self, params):
self._flag_raman = params['flag_raman']
self._space_resolution = params['space_resolution']
self._tolerance = params['tolerance']
@property
def flag_raman(self):
return self._flag_raman
@property
def space_resolution(self):
return self._space_resolution
@property
def tolerance(self):
return self._tolerance
class NLIParams():
def __init__(self, params):
self._nli_method_name = params['nli_method_name']
self._wdm_grid_size = params['wdm_grid_size']
self._dispersion_tolerance = params['dispersion_tolerance']
self._phase_shift_tollerance = params['phase_shift_tollerance']
self._f_cut_resolution = None
self._f_pump_resolution = None
@property
def nli_method_name(self):
return self._nli_method_name
@property
def wdm_grid_size(self):
return self._wdm_grid_size
@property
def dispersion_tolerance(self):
return self._dispersion_tolerance
@property
def phase_shift_tollerance(self):
return self._phase_shift_tollerance
@property
def f_cut_resolution(self):
return self._f_cut_resolution
@f_cut_resolution.setter
def f_cut_resolution(self, f_cut_resolution):
self._f_cut_resolution = f_cut_resolution
@property
def f_pump_resolution(self):
return self._f_pump_resolution
@f_pump_resolution.setter
def f_pump_resolution(self, f_pump_resolution):
self._f_pump_resolution = f_pump_resolution
class SimParams():
def __init__(self, params):
self._raman_computed_channels = params['raman_computed_channels']
self._raman_params = RamanParams(params=params['raman_parameters'])
self._nli_params = NLIParams(params=params['nli_parameters'])
@property
def raman_computed_channels(self):
return self._raman_computed_channels
@property
def raman_params(self):
return self._raman_params
@property
def nli_params(self):
return self._nli_params
class FiberParams():
def __init__(self, fiber):
self._loss_coef = 2 * fiber.dbkm_2_lin()[1]
self._length = fiber.length
self._gamma = fiber.gamma
self._beta2 = fiber.beta2()
self._beta3 = fiber.beta3 if hasattr(fiber, 'beta3') else 0
self._f_ref_beta = fiber.f_ref_beta if hasattr(fiber, 'f_ref_beta') else 0
self._raman_efficiency = fiber.params.raman_efficiency
self._temperature = fiber.operational['temperature']
@property
def loss_coef(self):
return self._loss_coef
@property
def length(self):
return self._length
@property
def gamma(self):
return self._gamma
@property
def beta2(self):
return self._beta2
@property
def beta3(self):
return self._beta3
@property
def f_ref_beta(self):
return self._f_ref_beta
@property
def raman_efficiency(self):
return self._raman_efficiency
@property
def temperature(self):
return self._temperature
def alpha0(self, f_ref=193.5e12):
""" It returns the zero element of the series expansion of attenuation coefficient alpha(f) in the
reference frequency f_ref
:param f_ref: reference frequency of series expansion [Hz]
:return: alpha0: power attenuation coefficient in f_ref [Neper/m]
"""
if not hasattr(self.loss_coef, 'alpha_power'):
alpha0 = self.loss_coef
else:
alpha_interp = interp1d(self.loss_coef['frequency'],
self.loss_coef['alpha_power'])
alpha0 = alpha_interp(f_ref)
return alpha0
pump = namedtuple('RamanPump', 'power frequency propagation_direction')
def propagate_raman_fiber(fiber, *carriers):
sim_params = fiber.sim_params
raman_params = fiber.sim_params.raman_params
nli_params = fiber.sim_params.nli_params
# apply input attenuation to carriers
attenuation_in = db2lin(fiber.con_in + fiber.att_in)
chan = []
for carrier in carriers:
pwr = carrier.power
pwr = pwr._replace(signal=pwr.signal / attenuation_in,
nli=pwr.nli / attenuation_in,
ase=pwr.ase / attenuation_in)
carrier = carrier._replace(power=pwr)
chan.append(carrier)
carriers = tuple(f for f in chan)
fiber_params = FiberParams(fiber)
# evaluate fiber attenuation involving also SRS if required by sim_params
if 'raman_pumps' in fiber.operational:
raman_pumps = tuple(pump(p['power'], p['frequency'], p['propagation_direction'])
for p in fiber.operational['raman_pumps'])
else:
raman_pumps = None
raman_solver = RamanSolver(raman_params=raman_params, fiber_params=fiber_params)
stimulated_raman_scattering = raman_solver.stimulated_raman_scattering(carriers=carriers,
raman_pumps=raman_pumps)
fiber_attenuation = (stimulated_raman_scattering.rho[:, -1])**-2
if not raman_params.flag_raman:
fiber_attenuation = tuple(fiber.lin_attenuation for _ in carriers)
# evaluate Raman ASE noise if required by sim_params and if raman pumps are present
if raman_params.flag_raman and raman_pumps:
raman_ase = raman_solver.spontaneous_raman_scattering.power[:, -1]
else:
raman_ase = tuple(0 for _ in carriers)
# evaluate nli and propagate in fiber
attenuation_out = db2lin(fiber.con_out)
nli_solver = NliSolver(nli_params=nli_params, fiber_params=fiber_params)
nli_solver.stimulated_raman_scattering = stimulated_raman_scattering
nli_frequencies = []
computed_nli = []
for carrier in (c for c in carriers if c.channel_number in sim_params.raman_computed_channels):
resolution_param = frequency_resolution(carrier, carriers, sim_params, fiber_params)
f_cut_resolution, f_pump_resolution, _, _ = resolution_param
nli_params.f_cut_resolution = f_cut_resolution
nli_params.f_pump_resolution = f_pump_resolution
nli_frequencies.append(carrier.frequency)
computed_nli.append(nli_solver.compute_nli(carrier, *carriers))
new_carriers = []
for carrier, attenuation, rmn_ase in zip(carriers, fiber_attenuation, raman_ase):
carrier_nli = np.interp(carrier.frequency, nli_frequencies, computed_nli)
pwr = carrier.power
pwr = pwr._replace(signal=pwr.signal/attenuation/attenuation_out,
nli=(pwr.nli+carrier_nli)/attenuation/attenuation_out,
ase=((pwr.ase/attenuation)+rmn_ase)/attenuation_out)
new_carriers.append(carrier._replace(power=pwr))
return new_carriers
def frequency_resolution(carrier, carriers, sim_params, fiber_params):
def _get_freq_res_k_phi(delta_count, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol):
res_phi = _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol)
res_k = _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol)
res_dict = {'res_phi': res_phi, 'res_k': res_k}
method = min(res_dict, key=res_dict.get)
return res_dict[method], method, res_dict
def _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol):
return k_tol * abs(alpha0) / abs(beta2) / (1 + delta_count) / (4 * np.pi ** 2 * grid_size)
def _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol):
return phi_tol / abs(beta2) / (1 + delta_count) / delta_z / (4 * np.pi ** 2 * grid_size)
grid_size = sim_params.nli_params.wdm_grid_size
delta_z = sim_params.raman_params.space_resolution
alpha0 = fiber_params.alpha0()
beta2 = fiber_params.beta2
k_tol = sim_params.nli_params.dispersion_tolerance
phi_tol = sim_params.nli_params.phase_shift_tollerance
f_pump_resolution, method_f_pump, res_dict_pump = \
_get_freq_res_k_phi(0, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol)
f_cut_resolution = {}
method_f_cut = {}
res_dict_cut = {}
for cut_carrier in carriers:
delta_number = cut_carrier.channel_number - carrier.channel_number
delta_count = abs(delta_number)
f_res, method, res_dict = \
_get_freq_res_k_phi(delta_count, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol)
f_cut_resolution[f'delta_{delta_number}'] = f_res
method_f_cut[delta_number] = method
res_dict_cut[delta_number] = res_dict
return [f_cut_resolution, f_pump_resolution, (method_f_cut, method_f_pump), (res_dict_cut, res_dict_pump)]
def raised_cosine_comb(f, *carriers):
""" Returns an array storing the PSD of a WDM comb of raised cosine shaped
channels at the input frequencies defined in array f
:param f: numpy array of frequencies in Hz
:param carriers: namedtuple describing the WDM comb
:return: PSD of the WDM comb evaluated over f
"""
psd = np.zeros(np.shape(f))
for carrier in carriers:
f_nch = carrier.frequency
g_ch = carrier.power.signal / carrier.baud_rate
ts = 1 / carrier.baud_rate
passband = (1 - carrier.roll_off) / (2 / carrier.baud_rate)
stopband = (1 + carrier.roll_off) / (2 / carrier.baud_rate)
ff = np.abs(f - f_nch)
tf = ff - passband
if carrier.roll_off == 0:
psd = np.where(tf <= 0, g_ch, 0.) + psd
else:
psd = g_ch * (np.where(tf <= 0, 1., 0.) + 1 / 2 * (1 + np.cos(np.pi * ts / carrier.roll_off * tf)) *
np.where(tf > 0, 1., 0.) * np.where(np.abs(ff) <= stopband, 1., 0.)) + psd
return psd
class RamanSolver:
def __init__(self, raman_params=None, fiber_params=None):
""" Initialize the fiber object with its physical parameters
:param length: fiber length in m.
:param alphap: fiber power attenuation coefficient vs frequency in 1/m. numpy array
:param freq_alpha: frequency axis of alphap in Hz. numpy array
:param cr_raman: Raman efficiency vs frequency offset in 1/W/m. numpy array
:param freq_cr: reference frequency offset axis for cr_raman. numpy array
:param raman_params: namedtuple containing the solver parameters (optional).
"""
self.fiber_params = fiber_params
self.raman_params = raman_params
self._carriers = None
self._stimulated_raman_scattering = None
self._spontaneous_raman_scattering = None
@property
def fiber_params(self):
return self._fiber_params
@fiber_params.setter
def fiber_params(self, fiber_params):
self._stimulated_raman_scattering = None
self._fiber_params = fiber_params
@property
def carriers(self):
return self._carriers
@carriers.setter
def carriers(self, carriers):
"""
:param carriers: tuple of namedtuples containing information about carriers
:return:
"""
self._carriers = carriers
self._stimulated_raman_scattering = None
@property
def raman_pumps(self):
return self._raman_pumps
@raman_pumps.setter
def raman_pumps(self, raman_pumps):
self._raman_pumps = raman_pumps
self._stimulated_raman_scattering = None
@property
def raman_params(self):
return self._raman_params
@raman_params.setter
def raman_params(self, raman_params):
"""
:param raman_params: namedtuple containing the solver parameters (optional).
:return:
"""
self._raman_params = raman_params
self._stimulated_raman_scattering = None
self._spontaneous_raman_scattering = None
@property
def spontaneous_raman_scattering(self):
if self._spontaneous_raman_scattering is None:
# SET STUFF
loss_coef = self.fiber_params.loss_coef
raman_efficiency = self.fiber_params.raman_efficiency
temperature = self.fiber_params.temperature
carriers = self.carriers
raman_pumps = self.raman_pumps
logger.debug('Start computing fiber Spontaneous Raman Scattering')
power_spectrum, freq_array, prop_direct, bn_array = self._compute_power_spectrum(carriers, raman_pumps)
if not hasattr(loss_coef, 'alpha_power'):
alphap_fiber = loss_coef * np.ones(freq_array.shape)
else:
interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power'])
alphap_fiber = interp_alphap(freq_array)
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
cr = interp_cr(freq_diff)
# z propagation axis
z_array = self._stimulated_raman_scattering.z
ase_bc = np.zeros(freq_array.shape)
# calculate ase power
spontaneous_raman_scattering = self._int_spontaneous_raman(z_array, self._stimulated_raman_scattering.power,
alphap_fiber, freq_array, cr, freq_diff, ase_bc,
bn_array, temperature)
setattr(spontaneous_raman_scattering, 'frequency', freq_array)
setattr(spontaneous_raman_scattering, 'z', z_array)
setattr(spontaneous_raman_scattering, 'power', spontaneous_raman_scattering.x)
delattr(spontaneous_raman_scattering, 'x')
logger.debug(spontaneous_raman_scattering.message)
self._spontaneous_raman_scattering = spontaneous_raman_scattering
return self._spontaneous_raman_scattering
@staticmethod
def _compute_power_spectrum(carriers, raman_pumps=None):
"""
Rearrangement of spectral and Raman pump information to make them compatible with Raman solver
:param carriers: a tuple of namedtuples describing the transmitted channels
:param raman_pumps: a namedtuple describing the Raman pumps
:return:
"""
# Signal power spectrum
pow_array = np.array([])
f_array = np.array([])
noise_bandwidth_array = np.array([])
for carrier in sorted(carriers, key=attrgetter('frequency')):
f_array = np.append(f_array, carrier.frequency)
pow_array = np.append(pow_array, carrier.power.signal)
ref_bw = carrier.baud_rate
noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw)
propagation_direction = np.ones(len(f_array))
# Raman pump power spectrum
if raman_pumps:
for pump in raman_pumps:
pow_array = np.append(pow_array, pump.power)
f_array = np.append(f_array, pump.frequency)
direction = +1 if pump.propagation_direction.lower() == 'coprop' else -1
propagation_direction = np.append(propagation_direction, direction)
noise_bandwidth_array = np.append(noise_bandwidth_array, ref_bw)
# Final sorting
ind = np.argsort(f_array)
f_array = f_array[ind]
pow_array = pow_array[ind]
propagation_direction = propagation_direction[ind]
return pow_array, f_array, propagation_direction, noise_bandwidth_array
def _int_spontaneous_raman(self, z_array, raman_matrix, alphap_fiber, freq_array, cr_raman_matrix, freq_diff, ase_bc, bn_array, temperature):
spontaneous_raman_scattering = OptimizeResult()
dx = self.raman_params.space_resolution
h = ph.value('Planck constant')
kb = ph.value('Boltzmann constant')
power_ase = np.nan * np.ones(raman_matrix.shape)
int_pump = cumtrapz(raman_matrix, z_array, dx=dx, axis=1, initial=0)
for f_ind, f_ase in enumerate(freq_array):
cr_raman = cr_raman_matrix[f_ind, :]
vibrational_loss = f_ase / freq_array[:f_ind]
eta = 1/(np.exp((h*freq_diff[f_ind, f_ind+1:])/(kb*temperature)) - 1)
int_fiber_loss = -alphap_fiber[f_ind] * z_array
int_raman_loss = np.sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(), axis=0)
int_raman_gain = np.sum((cr_raman[f_ind + 1:] * int_pump[f_ind + 1:, :].transpose()).transpose(), axis=0)
int_gain_loss = int_fiber_loss + int_raman_gain + int_raman_loss
new_ase = np.sum((cr_raman[f_ind+1:] * (1 + eta) * raman_matrix[f_ind+1:, :].transpose()).transpose() * h * f_ase * bn_array[f_ind], axis=0)
bc_evolution = ase_bc[f_ind] * np.exp(int_gain_loss)
ase_evolution = np.exp(int_gain_loss) * cumtrapz(new_ase*np.exp(-int_gain_loss), z_array, dx=dx, initial=0)
power_ase[f_ind, :] = bc_evolution + ase_evolution
spontaneous_raman_scattering.x = 2 * power_ase
spontaneous_raman_scattering.success = True
spontaneous_raman_scattering.message = "Spontaneous Raman Scattering evaluated successfully"
return spontaneous_raman_scattering
def stimulated_raman_scattering(self, carriers, raman_pumps=None):
""" Returns stimulated Raman scattering solution including
fiber gain/loss profile.
:return: self._stimulated_raman_scattering: the SRS problem solution.
scipy.interpolate.PPoly instance
"""
if self._stimulated_raman_scattering is None:
# fiber parameters
fiber_length = self.fiber_params.length
loss_coef = self.fiber_params.loss_coef
if self.raman_params.flag_raman:
raman_efficiency = self.fiber_params.raman_efficiency
else:
raman_efficiency = self.fiber_params.raman_efficiency
raman_efficiency['cr'] = np.array(raman_efficiency['cr']) * 0
# raman solver parameters
z_resolution = self.raman_params.space_resolution
tolerance = self.raman_params.tolerance
logger.debug('Start computing fiber Stimulated Raman Scattering')
power_spectrum, freq_array, prop_direct, _ = self._compute_power_spectrum(carriers, raman_pumps)
if not hasattr(loss_coef, 'alpha_power'):
alphap_fiber = loss_coef * np.ones(freq_array.shape)
else:
interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power'])
alphap_fiber = interp_alphap(freq_array)
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
cr = interp_cr(freq_diff)
# z propagation axis
z = np.arange(0, fiber_length+1, z_resolution)
ode_function = lambda z, p: self._ode_stimulated_raman(z, p, alphap_fiber, freq_array, cr, prop_direct)
boundary_residual = lambda ya, yb: self._residuals_stimulated_raman(ya, yb, power_spectrum, prop_direct)
initial_guess_conditions = self._initial_guess_stimulated_raman(z, power_spectrum, alphap_fiber, prop_direct)
# ODE SOLVER
stimulated_raman_scattering = solve_bvp(ode_function, boundary_residual, z, initial_guess_conditions, tol=tolerance)
rho = (stimulated_raman_scattering.y.transpose() / power_spectrum).transpose()
rho = np.sqrt(rho) # From power attenuation to field attenuation
setattr(stimulated_raman_scattering, 'frequency', freq_array)
setattr(stimulated_raman_scattering, 'z', stimulated_raman_scattering.x)
setattr(stimulated_raman_scattering, 'rho', rho)
setattr(stimulated_raman_scattering, 'power', stimulated_raman_scattering.y)
delattr(stimulated_raman_scattering, 'x')
delattr(stimulated_raman_scattering, 'y')
self.carriers = carriers
self.raman_pumps = raman_pumps
self._stimulated_raman_scattering = stimulated_raman_scattering
return self._stimulated_raman_scattering
def _residuals_stimulated_raman(self, ya, yb, power_spectrum, prop_direct):
computed_boundary_value = np.zeros(ya.size)
for index, direction in enumerate(prop_direct):
if direction == +1:
computed_boundary_value[index] = ya[index]
else:
computed_boundary_value[index] = yb[index]
return power_spectrum - computed_boundary_value
def _initial_guess_stimulated_raman(self, z, power_spectrum, alphap_fiber, prop_direct):
""" Computes the initial guess knowing the boundary conditions
:param z: patial axis [m]. numpy array
:param power_spectrum: power in each frequency slice [W]. Frequency axis is defined by freq_array. numpy array
:param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array
:param prop_direct: indicates the propagation direction of each power slice in power_spectrum:
+1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array
:return: power_guess: guess on the initial conditions [W]. The first ndarray index identifies the frequency slice,
the second ndarray index identifies the step in z. ndarray
"""
power_guess = np.empty((power_spectrum.size, z.size))
for f_index, power_slice in enumerate(power_spectrum):
if prop_direct[f_index] == +1:
power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z) * power_slice
else:
power_guess[f_index, :] = np.exp(-alphap_fiber[f_index] * z[::-1]) * power_slice
return power_guess
def _ode_stimulated_raman(self, z, power_spectrum, alphap_fiber, freq_array, cr_raman_matrix, prop_direct):
""" Aim of ode_raman is to implement the set of ordinary differential equations (ODEs) describing the Raman effect.
:param z: spatial axis (unused).
:param power_spectrum: power in each frequency slice [W]. Frequency axis is defined by freq_array. numpy array. Size n
:param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array. Size n
:param freq_array: reference frequency axis [Hz]. numpy array. Size n
:param cr_raman: Cr(f) Raman gain efficiency variation in frequency [1/W/m]. Frequency defined by freq_array. numpy ndarray. Size nxn
:param prop_direct: indicates the propagation direction of each power slice in power_spectrum:
+1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array. Size n
:return: dP/dz: the power variation in dz [W/m]. numpy array. Size n
"""
dpdz = np.nan * np.ones(power_spectrum.shape)
for f_ind, power in enumerate(power_spectrum):
cr_raman = cr_raman_matrix[f_ind, :]
vibrational_loss = freq_array[f_ind] / freq_array[:f_ind]
for z_ind, power_sample in enumerate(power):
raman_gain = np.sum(cr_raman[f_ind+1:] * power_spectrum[f_ind+1:, z_ind])
raman_loss = np.sum(vibrational_loss * cr_raman[:f_ind] * power_spectrum[:f_ind, z_ind])
dpdz_element = prop_direct[f_ind] * (-alphap_fiber[f_ind] + raman_gain - raman_loss) * power_sample
dpdz[f_ind][z_ind] = dpdz_element
return np.vstack(dpdz)
class NliSolver:
""" This class implements the NLI models.
Model and method can be specified in `self.nli_params.method`.
List of implemented methods:
'gn_model_analytic': brute force triple integral solution
'ggn_spectrally_separated_xpm_spm': XPM plus SPM
"""
def __init__(self, nli_params=None, fiber_params=None):
""" Initialize the fiber object with its physical parameters
"""
self.fiber_params = fiber_params
self.nli_params = nli_params
self.stimulated_raman_scattering = None
@property
def fiber_params(self):
return self._fiber_params
@fiber_params.setter
def fiber_params(self, fiber_params):
self._fiber_params = fiber_params
@property
def stimulated_raman_scattering(self):
return self._stimulated_raman_scattering
@stimulated_raman_scattering.setter
def stimulated_raman_scattering(self, stimulated_raman_scattering):
self._stimulated_raman_scattering = stimulated_raman_scattering
@property
def nli_params(self):
return self._nli_params
@nli_params.setter
def nli_params(self, nli_params):
"""
:param model_params: namedtuple containing the parameters used to compute the NLI.
"""
self._nli_params = nli_params
def compute_nli(self, carrier, *carriers):
""" Compute NLI power generated by the WDM comb `*carriers` on the channel under test `carrier`
at the end of the fiber span.
"""
if 'gn_model_analytic' == self.nli_params.nli_method_name.lower():
carrier_nli = self._gn_analytic(carrier, *carriers)
elif 'ggn_spectrally_separated' in self.nli_params.nli_method_name.lower():
eta_matrix = self._compute_eta_matrix(carrier, *carriers)
carrier_nli = self._carrier_nli_from_eta_matrix(eta_matrix, carrier, *carriers)
else:
raise ValueError(f'Method {self.nli_params.method_nli} not implemented.')
return carrier_nli
@staticmethod
def _carrier_nli_from_eta_matrix(eta_matrix, carrier, *carriers):
carrier_nli = 0
for pump_carrier_1 in carriers:
for pump_carrier_2 in carriers:
carrier_nli += eta_matrix[pump_carrier_1.channel_number-1, pump_carrier_2.channel_number-1] * \
pump_carrier_1.power.signal * pump_carrier_2.power.signal
carrier_nli *= carrier.power.signal
return carrier_nli
def _compute_eta_matrix(self, carrier_cut, *carriers):
cut_index = carrier_cut.channel_number - 1
# Matrix initialization
matrix_size = max(carriers, key=lambda x: getattr(x, 'channel_number')).channel_number
eta_matrix = np.zeros(shape=(matrix_size, matrix_size))
# SPM
logger.debug(f'Start computing SPM on channel #{carrier_cut.channel_number}')
# SPM GGN
if 'ggn' in self.nli_params.nli_method_name.lower():
partial_nli = self._generalized_spectrally_separated_spm(carrier_cut)
# SPM GN
elif 'gn' in self.nli_params.nli_method_name.lower():
partial_nli = self._gn_analytic(carrier_cut, *[carrier_cut])
eta_matrix[cut_index, cut_index] = partial_nli / (carrier_cut.power.signal**3)
# XPM
for pump_carrier in carriers:
pump_index = pump_carrier.channel_number - 1
if not (cut_index == pump_index):
logger.debug(f'Start computing XPM on channel #{carrier_cut.channel_number} '
f'from channel #{pump_carrier.channel_number}')
# XPM GGN
if 'ggn' in self.nli_params.nli_method_name.lower():
partial_nli = self._generalized_spectrally_separated_xpm(carrier_cut, pump_carrier)
# XPM GGN
elif 'gn' in self.nli_params.nli_method_name.lower():
partial_nli = self._gn_analytic(carrier_cut, *[pump_carrier])
eta_matrix[pump_index, pump_index] = partial_nli /\
(carrier_cut.power.signal * pump_carrier.power.signal**2)
return eta_matrix
# Methods for computing GN-model
def _gn_analytic(self, carrier, *carriers):
""" Computes the nonlinear interference power on a single carrier.
The method uses eq. 120 from arXiv:1209.0394.
:param carrier: the signal under analysis
:param carriers: the full WDM comb
:return: carrier_nli: the amount of nonlinear interference in W on the carrier under analysis
"""
alpha = self.fiber_params.alpha0() / 2
beta2 = self.fiber_params.beta2
gamma = self.fiber_params.gamma
length = self.fiber_params.length
effective_length = (1 - np.exp(-2 * alpha * length)) / (2 * alpha)
asymptotic_length = 1 / (2 * alpha)
g_nli = 0
for interfering_carrier in carriers:
g_interfearing = interfering_carrier.power.signal / interfering_carrier.baud_rate
g_signal = carrier.power.signal / carrier.baud_rate
g_nli += g_interfearing**2 * g_signal \
* _psi(carrier, interfering_carrier, beta2=self.fiber_params.beta2, asymptotic_length=1/self.fiber_params.alpha0())
g_nli *= (16.0 / 27.0) * (gamma * effective_length)**2 /\
(2 * np.pi * abs(beta2) * asymptotic_length)
carrier_nli = carrier.baud_rate * g_nli
return carrier_nli
# Methods for computing the GGN-model
def _generalized_spectrally_separated_spm(self, carrier):
f_cut_resolution = self.nli_params.f_cut_resolution['delta_0']
f_eval = carrier.frequency
g_cut = (carrier.power.signal / carrier.baud_rate)
spm_nli = carrier.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_cut**3 * \
self._generalized_psi(carrier, carrier, f_eval, f_cut_resolution, f_cut_resolution)
return spm_nli
def _generalized_spectrally_separated_xpm(self, carrier_cut, pump_carrier):
delta_index = pump_carrier.channel_number - carrier_cut.channel_number
f_cut_resolution = self.nli_params.f_cut_resolution[f'delta_{delta_index}']
f_pump_resolution = self.nli_params.f_pump_resolution
f_eval = carrier_cut.frequency
g_pump = (pump_carrier.power.signal / pump_carrier.baud_rate)
g_cut = (carrier_cut.power.signal / carrier_cut.baud_rate)
frequency_offset_threshold = self._frequency_offset_threshold(pump_carrier.baud_rate)
if abs(carrier_cut.frequency - pump_carrier.frequency) <= frequency_offset_threshold:
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \
2 * self._generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution)
else:
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \
2 * self._fast_generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution)
return xpm_nli
def _fast_generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution):
""" It computes the generalized psi function similarly to the one used in the GN model
:return: generalized_psi
"""
# Fiber parameters
alpha0 = self.fiber_params.alpha0(f_eval)
beta2 = self.fiber_params.beta2
beta3 = self.fiber_params.beta3
f_ref_beta = self.fiber_params.f_ref_beta
z = self.stimulated_raman_scattering.z
frequency_rho = self.stimulated_raman_scattering.frequency
rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
if len(frequency_rho) == 1:
rho_function = lambda f: rho_norm[0, :]
else:
rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate')
rho_norm_pump = rho_function(pump_carrier.frequency)
f1_array = np.array([pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2)])
f2_array = np.arange(carrier_cut.frequency,
carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
f_cut_resolution) # Only positive f2 is used since integrand_f2 is symmetric
integrand_f1 = np.zeros(len(f1_array))
for f1_index, f1 in enumerate(f1_array):
delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
(beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
integrand_f2 = self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0)
integrand_f1[f1_index] = 2 * np.trapz(integrand_f2, f2_array) # 2x since integrand_f2 is symmetric in f2
generalized_psi = 0.5 * sum(integrand_f1) * pump_carrier.baud_rate
return generalized_psi
def _generalized_psi(self, carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution):
""" It computes the generalized psi function similarly to the one used in the GN model
:return: generalized_psi
"""
# Fiber parameters
alpha0 = self.fiber_params.alpha0(f_eval)
beta2 = self.fiber_params.beta2
beta3 = self.fiber_params.beta3
f_ref_beta = self.fiber_params.f_ref_beta
z = self.stimulated_raman_scattering.z
frequency_rho = self.stimulated_raman_scattering.frequency
rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
if len(frequency_rho) == 1:
rho_function = lambda f: rho_norm[0, :]
else:
rho_function = interp1d(frequency_rho, rho_norm, axis=0, fill_value='extrapolate')
rho_norm_pump = rho_function(pump_carrier.frequency)
f1_array = np.arange(pump_carrier.frequency - (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
pump_carrier.frequency + (pump_carrier.baud_rate * (1 + pump_carrier.roll_off) / 2),
f_pump_resolution)
f2_array = np.arange(carrier_cut.frequency - (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
carrier_cut.frequency + (carrier_cut.baud_rate * (1 + carrier_cut.roll_off) / 2),
f_cut_resolution)
psd1 = raised_cosine_comb(f1_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal)
integrand_f1 = np.zeros(len(f1_array))
for f1_index, (f1, psd1_sample) in enumerate(zip(f1_array, psd1)):
f3_array = f1 + f2_array - f_eval
psd2 = raised_cosine_comb(f2_array, carrier_cut) * (carrier_cut.baud_rate / carrier_cut.power.signal)
psd3 = raised_cosine_comb(f3_array, pump_carrier) * (pump_carrier.baud_rate / pump_carrier.power.signal)
ggg = psd1_sample * psd2 * psd3
delta_beta = 4 * np.pi**2 * (f1 - f_eval) * (f2_array - f_eval) * \
(beta2 + np.pi * beta3 * (f1 + f2_array - 2 * f_ref_beta))
integrand_f2 = ggg * self._generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0)
integrand_f1[f1_index] = np.trapz(integrand_f2, f2_array)
generalized_psi = np.trapz(integrand_f1, f1_array)
return generalized_psi
@staticmethod
def _generalized_rho_nli(delta_beta, rho_norm_pump, z, alpha0):
w = 1j * delta_beta - alpha0
generalized_rho_nli = (rho_norm_pump[-1]**2 * np.exp(w * z[-1]) - rho_norm_pump[0]**2 * np.exp(w * z[0])) / w
for z_ind in range(0, len(z) - 1):
derivative_rho = (rho_norm_pump[z_ind + 1]**2 - rho_norm_pump[z_ind]**2) / (z[z_ind + 1] - z[z_ind])
generalized_rho_nli -= derivative_rho * (np.exp(w * z[z_ind + 1]) - np.exp(w * z[z_ind])) / (w**2)
generalized_rho_nli = np.abs(generalized_rho_nli)**2
return generalized_rho_nli
def _frequency_offset_threshold(self, symbol_rate):
k_ref = 5
beta2_ref = 21.3e-27
delta_f_ref = 50e9
rs_ref = 32e9
freq_offset_th = ((k_ref * delta_f_ref) * rs_ref * beta2_ref) / (self.fiber_params.beta2 * symbol_rate)
return freq_offset_th
def _psi(carrier, interfering_carrier, beta2, asymptotic_length):
"""Calculates eq. 123 from `arXiv:1209.0394 <https://arxiv.org/abs/1209.0394>`__"""
if carrier.channel_number == interfering_carrier.channel_number: # SCI, SPM
psi = np.arcsinh(0.5 * np.pi**2 * asymptotic_length * abs(beta2) * carrier.baud_rate**2)
else: # XCI, XPM
delta_f = carrier.frequency - interfering_carrier.frequency
psi = np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) *
carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
psi -= np.arcsinh(np.pi**2 * asymptotic_length * abs(beta2) *
carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
return psi

View File

@@ -22,8 +22,9 @@ from json import dumps
from pathlib import Path
from gnpy.core.equipment import load_equipment
from gnpy.core.utils import db2lin, lin2db
from gnpy.core.exceptions import ServiceError
SERVICES_COLUMN = 11
SERVICES_COLUMN = 12
#EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json'
all_rows = lambda sheet, start=0: (sheet.row(x) for x in range(start, sheet.nrows))
@@ -31,9 +32,9 @@ logger = getLogger(__name__)
# Type for input data
class Request(namedtuple('Request', 'request_id source destination trx_type mode \
spacing power nb_channel disjoint_from nodes_list is_loose')):
def __new__(cls, request_id, source, destination, trx_type, mode , spacing , power , nb_channel , disjoint_from ='' , nodes_list = None, is_loose = ''):
return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose)
spacing power nb_channel disjoint_from nodes_list is_loose path_bandwidth')):
def __new__(cls, request_id, source, destination, trx_type, mode=None , spacing= None , power = None, nb_channel = None , disjoint_from ='' , nodes_list = None, is_loose = '', path_bandwidth = None):
return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose, path_bandwidth)
# Type for output data: // from dutc
class Element:
@@ -43,147 +44,189 @@ class Element:
return hash((type(self), self.uid))
class Request_element(Element):
def __init__(self,Request,eqpt_filename):
def __init__(self, Request, eqpt_filename, bidir):
# request_id is str
# excel has automatic number formatting that adds .0 on integer values
# the next lines recover the pure int value, assuming this .0 is unwanted
if not isinstance(Request.request_id,str):
value = str(int(Request.request_id))
if value.endswith('.0'):
value = value[:-2]
self.request_id = value
else:
self.request_id = Request.request_id
self.source = Request.source
self.destination = Request.destination
self.request_id = correct_xlrd_int_to_str_reading(Request.request_id)
self.source = f'trx {Request.source}'
self.destination = f'trx {Request.destination}'
# TODO: the automatic naming generated by excel parser requires that source and dest name
# be a string starting with 'trx' : this is manually added here.
self.srctpid = f'trx {Request.source}'
self.dsttpid = f'trx {Request.destination}'
self.bidir = bidir
# test that trx_type belongs to eqpt_config.json
# if not replace it with a default
equipment = load_equipment(eqpt_filename)
try :
if equipment['Transceiver'][Request.trx_type]:
self.trx_type = Request.trx_type
if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]:
self.trx_type = correct_xlrd_int_to_str_reading(Request.trx_type)
if Request.mode is not None :
Requestmode = correct_xlrd_int_to_str_reading(Request.mode)
if [mode for mode in equipment['Transceiver'][Request.trx_type].mode if mode['format'] == Requestmode]:
self.mode = Requestmode
else :
msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Requestmode}\' in eqpt library \nComputation stopped.'
#print(msg)
logger.critical(msg)
exit(1)
else:
Requestmode = None
self.mode = Request.mode
except KeyError:
msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.'
msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Request.mode}\' in eqpt library \nComputation stopped.'
#print(msg)
logger.critical(msg)
exit()
raise ServiceError(msg)
# excel input are in GHz and dBm
self.spacing = Request.spacing * 1e9
self.power = db2lin(Request.power) * 1e-3
self.nb_channel = int(Request.nb_channel)
if not isinstance(Request.disjoint_from,str):
value = str(int(Request.disjoint_from))
if value.endswith('.0'):
value = value[:-2]
if Request.spacing is not None:
self.spacing = Request.spacing * 1e9
else:
value = Request.disjoint_from
self.disjoint_from = [n for n in value.split()]
msg = f'Request {self.request_id} missing spacing: spacing is mandatory.\ncomputation stopped'
logger.critical(msg)
raise ServiceError(msg)
if Request.power is not None:
self.power = db2lin(Request.power) * 1e-3
else:
self.power = None
if Request.nb_channel is not None :
self.nb_channel = int(Request.nb_channel)
else:
self.nb_channel = None
value = correct_xlrd_int_to_str_reading(Request.disjoint_from)
self.disjoint_from = [n for n in value.split(' | ') if value]
self.nodes_list = []
if Request.nodes_list :
self.nodes_list = Request.nodes_list.split(' | ')
# cleaning the list of nodes to remove source and destination
# (because the remaining of the program assumes that the nodes list are nodes
# on the path and should not include source and destination)
try :
self.nodes_list.remove(self.source)
msg = f'{self.source} removed from explicit path node-list'
logger.info(msg)
# print(msg)
except ValueError:
msg = f'{self.source} already removed from explicit path node-list'
logger.info(msg)
# print(msg)
try :
self.nodes_list.remove(self.destination)
msg = f'{self.destination} removed from explicit path node-list'
logger.info(msg)
# print(msg)
except ValueError:
msg = f'{self.destination} already removed from explicit path node-list'
logger.info(msg)
# print(msg)
self.loose = 'loose'
# the excel parser applies the same hop-type to all nodes in the route nodes_list.
# user can change this per node in the generated json
self.loose = 'LOOSE'
if Request.is_loose == 'no' :
self.loose = 'strict'
self.loose = 'STRICT'
self.path_bandwidth = None
if Request.path_bandwidth is not None:
self.path_bandwidth = Request.path_bandwidth * 1e9
else:
self.path_bandwidth = 0
uid = property(lambda self: repr(self))
@property
def pathrequest(self):
return {
# Default assumption for bidir is False
req_dictionnary = {
'request-id':self.request_id,
'source': self.source,
'destination': self.destination,
'src-tp-id': self.srctpid,
'dst-tp-id': self.dsttpid,
'bidirectional': self.bidir,
'path-constraints':{
'te-bandwidth': {
'technology': 'flexi-grid',
'trx_type' : self.trx_type,
'trx_mode' : self.mode,
'effective-freq-slot':[{'n': 'null','m': 'null'}] ,
'effective-freq-slot':[{'N': 'null', 'M': 'null'}],
'spacing' : self.spacing,
'max-nb-of-channel' : self.nb_channel,
'output-power' : self.power
}
},
'optimizations': {
'explicit-route-include-objects': [
{
'index': self.nodes_list.index(node),
'unnumbered-hop':{
'node-id': f'{node}',
'link-tp-id': 'link-tp-id is not used',
'hop-type': 'loose',
'direction': 'direction is not used'
},
'label-hop':{
'te-label': {
'generic': 'generic is not used',
'direction': 'direction is not used'
}
}
}
if self.nodes_list:
req_dictionnary['explicit-route-objects'] = {}
temp = {'route-object-include-exclude' : [
{'explicit-route-usage': 'route-include-ero',
'index': self.nodes_list.index(node),
'num-unnum-hop': {
'node-id': f'{node}',
'link-tp-id': 'link-tp-id is not used',
'hop-type': f'{self.loose}',
}
}
for node in self.nodes_list
]
}
}
for node in self.nodes_list]
}
req_dictionnary['explicit-route-objects'] = temp
if self.path_bandwidth is not None:
req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth
return req_dictionnary
@property
def pathsync(self):
if self.disjoint_from :
return {'synchonization-id':self.request_id,
return {'synchronization-id':self.request_id,
'svec': {
'relaxable' : 'False',
'link-diverse': 'True',
'node-diverse': 'True',
'relaxable' : 'false',
'disjointness': 'node link',
'request-id-number': [self.request_id]+ [n for n in self.disjoint_from]
}
}
else:
return None
# TO-DO: avoid multiple entries with same synchronisation vectors
@property
def json(self):
return self.pathrequest , self.pathsync
def convert_service_sheet(input_filename, eqpt_filename, output_filename='', filter_region=[]):
def convert_service_sheet(input_filename, eqpt_filename, output_filename='', bidir=False, filter_region=None):
""" converts a service sheet into a json structure
"""
if filter_region is None:
filter_region = []
service = parse_excel(input_filename)
req = [Request_element(n,eqpt_filename) for n in service]
req = [Request_element(n, eqpt_filename, bidir) for n in service]
# dumps the output into a json file with name
# split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]]
if output_filename=='':
output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json'
# for debug
# print(json_filename)
data = {
'path-request': [n.json[0] for n in req],
'synchronisation': [n.json[1] for n in req
if n.json[1] is not None]
}
with open(output_filename, 'w') as f:
f.write(dumps(data, indent=2))
# if there is no sync vector , do not write any synchronization
synchro = [n.json[1] for n in req if n.json[1] is not None]
if synchro:
data = {
'path-request': [n.json[0] for n in req],
'synchronization': synchro
}
else:
data = {
'path-request': [n.json[0] for n in req]
}
with open(output_filename, 'w', encoding='utf-8') as f:
f.write(dumps(data, indent=2, ensure_ascii=False))
return data
def correct_xlrd_int_to_str_reading(v) :
if not isinstance(v,str):
value = str(int(v))
if value.endswith('.0'):
value = value[:-2]
else:
value = v
return value
# to be used from dutc
def parse_row(row, fieldnames):
return {f: r.value for f, r in zip(fieldnames, row[0:SERVICES_COLUMN])
@@ -197,20 +240,29 @@ def parse_excel(input_filename):
return services
def parse_service_sheet(service_sheet):
logger.info(f'Validating headers on {service_sheet.name!r}')
header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN]]
expected = ['route id', 'Source', 'Destination', 'TRX type', \
'Mode', 'System: spacing', 'System: input power (dBm)', 'System: nb of channels',\
'routing: disjoint from', 'routing: path', 'routing: is loose?']
if header != expected:
msg = f'Malformed header on Service sheet: {header} != {expected}'
logger.critical(msg)
raise ValueError(msg)
""" reads each column according to authorized fieldnames. order is not important.
"""
logger.info(f'Validating headers on {service_sheet.name!r}')
# add a test on field to enable the '' field case that arises when columns on the
# right hand side are used as comments or drawing in the excel sheet
header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN]
if len(x.value.strip()) > 0]
service_fieldnames = 'request_id source destination trx_type mode spacing power nb_channel disjoint_from nodes_list is_loose'.split()
# Important Note: it reads all colum on each row so that
# it is not possible to write annotation in the excel sheet
# outside the SERVICES_COLUMN ... TO BE IMPROVED
# request_id should be unique for disjunction constraints (not used yet)
for row in all_rows(service_sheet, start=5):
yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames))
# create a service_fieldname independant from the excel column order
# to be compatible with any version of the sheet
# the following dictionnary records the excel field names and the corresponding parameter's name
authorized_fieldnames = {
'route id':'request_id', 'Source':'source', 'Destination':'destination', \
'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \
'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\
'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\
'routing: is loose?':'is_loose', 'path bandwidth':'path_bandwidth'}
try:
service_fieldnames = [authorized_fieldnames[e] for e in header]
except KeyError:
msg = f'Malformed header on Service sheet: {header} field not in {authorized_fieldnames}'
logger.critical(msg)
raise ValueError(msg)
for row in all_rows(service_sheet, start=5):
yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames))

View File

@@ -0,0 +1,386 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
gnpy.core.spectrum_assignment
=============================
This module contains the Oms and Bitmap classes and the different method to
select and assign spectrum. Spectrum_selection function identifies the free
slots and select_candidate selects the candidate spectrum according to
strategy: for example first fit
oms records its elements, and elements are updated with an oms to have
element/oms correspondace
"""
from collections import namedtuple
from logging import getLogger
from math import ceil
from gnpy.core.elements import Roadm, Transceiver
from gnpy.core.exceptions import SpectrumError
LOGGER = getLogger(__name__)
class Bitmap:
""" records the spectrum occupation
"""
def __init__(self, f_min, f_max, grid, guardband=0.15e12, bitmap=None):
# n is the min index including guardband. Guardband is require to be sure
# that a channel can be assigned with center frequency fmin (means that its
# slot occupation goes below freq_index_min
n_min = frequency_to_n(f_min-guardband, grid)
n_max = frequency_to_n(f_max+guardband, grid) - 1
self.n_min = n_min
self.n_max = n_max
self.freq_index_min = frequency_to_n(f_min)
self.freq_index_max = frequency_to_n(f_max)
self.freq_index = list(range(n_min, n_max+1))
if bitmap is None:
self.bitmap = [1] * (n_max-n_min+1)
elif len(bitmap) == len(self.freq_index):
self.bitmap = bitmap
else:
raise SpectrumError(f'bitmap is not consistant with f_min{f_min} - n: {n_min} and f_max{f_max}- n :{n_max}')
def getn(self, i):
""" converts the n (itu grid) into a local index
"""
return self.freq_index[i]
def geti(self, nvalue):
""" converts the local index into n (itu grid)
"""
return self.freq_index.index(nvalue)
def insert_left(self, newbitmap):
""" insert bitmap on the left to align oms bitmaps if their start frequencies are different
"""
self.bitmap = newbitmap + self.bitmap
temp = list(range(self.n_min-len(newbitmap), self.n_min))
self.freq_index = temp + self.freq_index
self.n_min = self.freq_index[0]
def insert_right(self, newbitmap):
""" insert bitmap on the right to align oms bitmaps if their stop frequencies are different
"""
self.bitmap = self.bitmap + newbitmap
self.freq_index = self.freq_index + list(range(self.n_max, self.n_max+len(newbitmap)))
self.n_max = self.freq_index[-1]
# +'grid available_slots f_min f_max services_list')
OMSParams = namedtuple('OMSParams', 'oms_id el_id_list el_list')
class OMS:
""" OMS class is the logical container that represent a link between two adjacent ROADMs and
records the crossed elements and the occupied spectrum
"""
def __init__(self, *args, **params):
params = OMSParams(**params)
self.oms_id = params.oms_id
self.el_id_list = params.el_id_list
self.el_list = params.el_list
self.spectrum_bitmap = []
self.nb_channels = 0
self.service_list = []
# TODO
def __str__(self):
return '\n\t'.join([f'{type(self).__name__} {self.oms_id}',
f'{self.el_id_list[0]} - {self.el_id_list[-1]}'])
def __repr__(self):
return '\n\t'.join([f'{type(self).__name__} {self.oms_id}',
f'{self.el_id_list[0]} - {self.el_id_list[-1]}', '\n'])
def add_element(self, elem):
""" records oms elements
"""
self.el_id_list.append(elem.uid)
self.el_list.append(elem)
def update_spectrum(self, f_min, f_max, guardband=0.15e12, existing_spectrum=None,
grid=0.00625e12):
""" frequencies expressed in Hz
"""
if existing_spectrum is None:
# add some 150 GHz margin to enable a center channel on f_min
# use ITU-T G694.1
# Flexible DWDM grid definition
# For the flexible DWDM grid, the allowed frequency slots have a nominal
# central frequency (in THz) defined by:
# 193.1 + n × 0.00625 where n is a positive or negative integer including 0
# and 0.00625 is the nominal central frequency granularity in THz
# and a slot width defined by:
# 12.5 × m where m is a positive integer and 12.5 is the slot width granularity in
# GHz.
# Any combination of frequency slots is allowed as long as no two frequency
# slots overlap.
# TODO : add explaination on that / parametrize ....
self.spectrum_bitmap = Bitmap(f_min, f_max, grid, guardband)
# print(len(self.spectrum_bitmap.bitmap))
def assign_spectrum(self, nvalue, mvalue):
""" change oms spectrum to mark spectrum assigned
"""
if (nvalue is None or mvalue is None or isinstance(nvalue, float)
or isinstance(mvalue, float) or mvalue == 0):
raise SpectrumError('could not assign None values')
startn, stopn = mvalue_to_slots(nvalue, mvalue)
# print(f'startn stop n {startn} , {stopn}')
# assumes that guardbands are sufficient to ensure that assigning a center channel
# at fmin or fmax is OK is startn > self.spectrum_bitmap.n_min
if (nvalue <= self.spectrum_bitmap.freq_index_max and
nvalue >= self.spectrum_bitmap.freq_index_min and
stopn <= self.spectrum_bitmap.n_max and
startn > self.spectrum_bitmap.n_min):
# verification that both length are identical
self.spectrum_bitmap.bitmap[self.spectrum_bitmap.geti(startn):self.spectrum_bitmap.geti(stopn)+1] = [0] * (stopn-startn+1)
return True
else:
msg = f'Could not assign n {nvalue}, m {mvalue} values:' +\
f' one or several slots are not available'
LOGGER.info(msg)
return False
def add_service(self, service_id, nb_wl):
""" record service and mark spectrum as occupied
"""
self.service_list.append(service_id)
self.nb_channels += nb_wl
def frequency_to_n(freq, grid=0.00625e12):
""" converts frequency into the n value (ITU grid)
"""
return (int)((freq-193.1e12)/grid)
def nvalue_to_frequency(nvalue, grid=0.00625e12):
""" converts n value into a frequency
"""
return 193.1e12 + nvalue * grid
def mvalue_to_slots(nvalue, mvalue):
""" convert center n an m into start and stop n
"""
startn = nvalue - mvalue
stopn = nvalue + mvalue -1
return startn, stopn
def slots_to_m(startn, stopn):
""" converts the start and stop n values to the center n and m value
"""
nvalue = (int)((startn+stopn+1)/2)
mvalue = (int)((stopn-startn+1)/2)
return nvalue, mvalue
def m_to_freq(nvalue, mvalue, grid=0.00625e12):
""" converts m into frequency range
"""
startn, stopn = mvalue_to_slots(nvalue, mvalue)
fstart = nvalue_to_frequency(startn, grid)
fstop = nvalue_to_frequency(stopn+1, grid)
return fstart, fstop
def align_grids(oms_list):
""" used to apply same grid to all oms : same starting n, stop n and slot size
out of grid slots are set to 0
"""
n_min = min([o.spectrum_bitmap.n_min for o in oms_list])
n_max = max([o.spectrum_bitmap.n_max for o in oms_list])
for this_o in oms_list:
if (this_o.spectrum_bitmap.n_min - n_min) > 0:
this_o.spectrum_bitmap.insert_left([0] * (this_o.spectrum_bitmap.n_min - n_min))
if (n_max - this_o.spectrum_bitmap.n_max) > 0:
this_o.spectrum_bitmap.insert_right([0] * (n_max - this_o.spectrum_bitmap.n_max))
return oms_list
def build_oms_list(network, equipment):
""" initialization of OMS list in the network
an oms is build reading all intermediate nodes between two adjacent ROADMs
each element within the list is being added an oms and oms_id to record the
oms it belongs to.
the function supports different spectrum width and supposes that the whole network
works with the min range among OMSs
"""
oms_id = 0
oms_list = []
for node in [n for n in network.nodes() if isinstance(n, Roadm)]:
for edge in network.edges([node]):
if not isinstance(edge[1], Transceiver):
nd_in = edge[0] # nd_in is a Roadm
try:
nd_in.oms_list.append(oms_id)
except AttributeError:
nd_in.oms_list = []
nd_in.oms_list.append(oms_id)
nd_out = edge[1]
params = {}
params['oms_id'] = oms_id
params['el_id_list'] = []
params['el_list'] = []
oms = OMS(**params)
oms.add_element(nd_in)
while not isinstance(nd_out, Roadm):
oms.add_element(nd_out)
# add an oms_id in the element
nd_out.oms_id = oms_id
nd_out.oms = oms
n_temp = nd_out
nd_out = next(n[1] for n in network.edges([n_temp]) if n[1].uid != nd_in.uid)
nd_in = n_temp
oms.add_element(nd_out)
# nd_out is a Roadm
try:
nd_out.oms_list.append(oms_id)
except AttributeError:
nd_out.oms_list = []
nd_out.oms_list.append(oms_id)
oms.update_spectrum(equipment['SI']['default'].f_min,
equipment['SI']['default'].f_max, grid=0.00625e12)
# oms.assign_spectrum(13,7) gives back (193137500000000.0, 193225000000000.0)
# as in the example in the standard
# oms.assign_spectrum(13,7)
oms_list.append(oms)
oms_id += 1
oms_list = align_grids(oms_list)
reversed_oms(oms_list)
return oms_list
def reversed_oms(oms_list):
""" identifies reversed OMS
only applicable for non parallel OMS
"""
for oms in oms_list:
has_reversed = False
for this_o in oms_list:
if (oms.el_id_list[0] == this_o.el_id_list[-1] and
oms.el_id_list[-1] == this_o.el_id_list[0]):
oms.reversed_oms = this_o
has_reversed = True
break
if not has_reversed:
oms.reversed_oms = None
def bitmap_sum(band1, band2):
""" a functions that marks occupied bitmap by 0 if the slot is occupied in band1 or in band2
"""
res = []
for i, elem in enumerate(band1):
if band2[i] * elem == 0:
res.append(0)
else:
res.append(1)
return res
def spectrum_selection(pth, oms_list, requested_m, requested_n=None):
""" collects spectrum availability and call the select_candidate function
# step 1 collects pth spectrum availability
# step 2 if n is not None try to assign the spectrum
# if the spectrum is not available then sends back an "error"
# if n is None selects candidate spectrum
# select spectrum that fits the policy ( first fit, random, ABP...)
# step3 returns the selection
"""
# use indexes instead of ITU-T n values
path_oms = []
for elem in pth:
if not isinstance(elem, Roadm) and not isinstance(elem, Transceiver):
# only edfa, fused and fibers have oms_id attribute
path_oms.append(elem.oms_id)
# remove duplicate oms_id, order is not important
path_oms = list(set(path_oms))
# assuming all oms have same freq index
if not path_oms:
candidate = (None, None, None)
return candidate, path_oms
freq_index = oms_list[path_oms[0]].spectrum_bitmap.freq_index
freq_index_min = oms_list[path_oms[0]].spectrum_bitmap.freq_index_min
freq_index_max = oms_list[path_oms[0]].spectrum_bitmap.freq_index_max
freq_availability = oms_list[path_oms[0]].spectrum_bitmap.bitmap
for oms in path_oms[1:]:
freq_availability = bitmap_sum(oms_list[oms].spectrum_bitmap.bitmap, freq_availability)
if requested_n is None:
# avoid slots reserved on the edge 0.15e-12 on both sides -> 24
candidates = [(freq_index[i]+requested_m, freq_index[i], freq_index[i]+2*requested_m-1)
for i in range(len(freq_availability))
if freq_availability[i:i+2*requested_m] == [1] * (2*requested_m)
and freq_index[i] >= freq_index_min
and freq_index[i+2*requested_m-1] <= freq_index_max]
candidate = select_candidate(candidates, policy='first_fit')
else:
i = oms_list[path_oms[0]].spectrum_bitmap.geti(requested_n)
# print(f'N {requested_n} i {i}')
# print(freq_availability[i-m:i+m] )
# print(freq_index[i-m:i+m])
if (freq_availability[i-requested_m:i+requested_m] == [1] * (2*requested_m) and
freq_index[i-requested_m] >= freq_index_min
and freq_index[i+requested_m-1] <= freq_index_max):
# candidate is the triplet center_n, startn and stopn
candidate = (requested_n, requested_n-requested_m, requested_n+requested_m-1)
else:
candidate = (None, None, None)
# print("coucou11")
# print(candidate)
# print(freq_availability[321:321+2*m])
# a = [i+321 for i in range(2*m)]
# print(a)
# print(candidate)
return candidate, path_oms
def select_candidate(candidates, policy):
""" selects a candidate among all available spectrum
"""
if policy == 'first_fit':
if candidates:
return candidates[0]
else:
return (None, None, None)
else:
raise ServiceError('Only first_fit spectrum assignment policy is implemented.')
def pth_assign_spectrum(pths, rqs, oms_list, rpths):
""" basic first fit assignment
if reversed path are provided, means that occupation is bidir
"""
for i, pth in enumerate(pths):
# computes the number of channels required
try:
if rqs[i].blocking_reason:
rqs[i].blocked = True
rqs[i].N = 0
rqs[i].M = 0
except AttributeError:
nb_wl = ceil(rqs[i].path_bandwidth / rqs[i].bit_rate)
# computes the total nb of slots according to requested spacing
# TODO : express superchannels
# assumes that all channels must be grouped
# TODO : enables non contiguous reservation in case of blocking
requested_m = ceil(rqs[i].spacing / 0.0125e12) * nb_wl
# concatenate all path and reversed path elements to derive slots availability
(center_n, startn, stopn), path_oms = spectrum_selection(pth + rpths[i], oms_list, requested_m,
requested_n=None)
# checks that requested_m is fitting startm and stopm
# if not None, center_n and start, stop frequencies are applicable to all oms of pth
# checks that spectrum is not None else indicate blocking reason
if center_n is not None:
# checks that requested_m is fitting startm and stopm
if 2 * requested_m > (stopn - startn + 1):
msg = f'candidate: {(center_n, startn, stopn)} is not consistant ' +\
f'with {requested_m}'
LOGGER.critical(msg)
raise ValueError(msg)
for oms_elem in path_oms:
oms_list[oms_elem].assign_spectrum(center_n, requested_m)
oms_list[oms_elem].add_service(rqs[i].request_id, nb_wl)
rqs[i].blocked = False
rqs[i].N = center_n
rqs[i].M = requested_m
else:
rqs[i].blocked = True
rqs[i].N = 0
rqs[i].M = 0
rqs[i].blocking_reason = 'NO_SPECTRUM'

View File

@@ -11,21 +11,21 @@ This module contains utility functions that are used with gnpy.
import json
import numpy as np
from csv import writer
import numpy as np
from numpy import pi, cos, sqrt, log10
from scipy import constants
def load_json(filename):
with open(filename, 'r') as f:
with open(filename, 'r', encoding='utf-8') as f:
data = json.load(f)
return data
def save_json(obj, filename):
with open(filename, 'w') as f:
json.dump(obj, f, indent=2)
with open(filename, 'w', encoding='utf-8') as f:
json.dump(obj, f, indent=2, ensure_ascii=False)
def write_csv(obj, filename):
"""
@@ -55,7 +55,7 @@ def write_csv(obj, filename):
result_category 2
...
"""
with open(filename, 'w') as f:
with open(filename, 'w', encoding='utf-8') as f:
w = writer(f)
for data_key, data_list in obj.items():
#main header
@@ -73,21 +73,19 @@ def c():
return constants.c
def itufs(spacing, startf=191.35, stopf=196.10):
"""Creates an array of frequencies whose default range is
191.35-196.10 THz
def arrange_frequencies(length, start, stop):
"""Create an array of frequencies
:param spacing: Frequency spacing in THz
:param starf: Start frequency in THz
:param stopf: Stop frequency in THz
:type spacing: float
:type startf: float
:type stopf: float
:return an array of frequnecies determined by the spacing parameter
:param length: number of elements
:param star: Start frequency in THz
:param stop: Stop frequency in THz
:type length: integer
:type start: float
:type stop: float
:return an array of frequencies determined by the spacing parameter
:rtype: numpy.ndarray
"""
return np.arange(startf, stopf + spacing / 2, spacing)
return np.linspace(start, stop, length)
def h():
"""
@@ -120,6 +118,10 @@ def freq2wavelength(value):
"""
return c() / value
def snr_sum(snr, bw, snr_added, bw_added=12.5e9):
snr_added = snr_added - lin2db(bw/bw_added)
snr = -lin2db(db2lin(-snr)+db2lin(-snr_added))
return snr
def deltawl2deltaf(delta_wl, wavelength):
""" deltawl2deltaf(delta_wl, wavelength):
@@ -181,3 +183,43 @@ def rrc(ffs, baud_rate, alpha):
p_inds = np.where(np.logical_and(np.abs(ffs) > 0, np.abs(ffs) < l_lim))
hf[p_inds] = 1
return sqrt(hf)
def merge_amplifier_restrictions(dict1, dict2):
"""Updates contents of dicts recursively
>>> d1 = {'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}}
>>> d2 = {'params': {'target_pch_out_db': -20}}
>>> merge_amplifier_restrictions(d1, d2)
{'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}, 'target_pch_out_db': -20}}
>>> d3 = {'params': {'restrictions': {'preamp_variety_list': ['foo'], 'booster_variety_list': ['bar']}}}
>>> merge_amplifier_restrictions(d1, d3)
{'params': {'restrictions': {'preamp_variety_list': [], 'booster_variety_list': []}}}
"""
copy_dict1 = dict1.copy()
for key in dict2:
if key in dict1:
if isinstance(dict1[key], dict):
copy_dict1[key] = merge_amplifier_restrictions(copy_dict1[key], dict2[key])
else:
copy_dict1[key] = dict2[key]
return copy_dict1
def silent_remove(this_list, elem):
"""Remove matching elements from a list without raising ValueError
>>> li = [0, 1]
>>> li = silent_remove(li, 1)
>>> li
[0]
>>> li = silent_remove(li, 1)
>>> li
[0]
"""
try:
this_list.remove(elem)
except ValueError:
pass
return this_list

View File

@@ -23,7 +23,7 @@ type][subtype]=object**
Every equipment type is defined in JSON root with according name and
array of parameters as value.
.. code-block::
.. code-block:: none
{"Edfa": [...],
"Fiber": [...]
@@ -40,26 +40,33 @@ object contains **"type_variety":”type name”** name:value combination,
if only one subtype exists **"type_variety"** name is not mandatory and
it will be marked with **”default”** value.
.. code-block::
.. code-block:: json
{"Edfa": [{
"type_variety": "std_medium_gain",
"type_def": "variable_gain",
"gain_flatmax": 26,
"gain_min": 15,
"p_max": 21,
"p_max": 23,
"nf_min": 6,
"nf_max": 10
"nf_max": 10,
"out_voa_auto": false,
"allowed_for_design": true
},
{
"type_variety": "std_low_gain",
"type_def": "variable_gain",
"gain_flatmax": 16,
"gain_min": 8,
"p_max": 21,
"nf_min": 7,
"nf_max": 11
"p_max": 23,
"nf_min": 6.5,
"nf_max": 11,
"out_voa_auto": false,
"allowed_for_design": true
}
],
"Fiber": [{
"type_variety": "SSMF",
"dispersion": 1.67e-05,
"gamma": 0.00127
}
@@ -74,66 +81,167 @@ it will be marked with **”default”** value.
1.2.1. EDFA element
*******************
Two types of EDFA definition are possible. Description JSON file
Four types of EDFA definition are possible. Description JSON file
location is in **transmission_main_example.py** folder:
- Advanced with JSON file describing gain/noise figure tilt and
gain/noise figure ripple. **"advanced_config_from_json"** value
contains filename.
.. code-block::
.. code-block:: json-object
"Edfa":[{
"type_variety": "high_detail_model_example",
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"advanced_config_from_json": "std_medium_gain_advanced_config.json"
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
"out_voa_auto": false,
"allowed_for_design": false
}
]
- Default with JSON file describing gain figure tilt and gain/noise
- Variable gain with JSON file describing gain figure tilt and gain/noise
figure ripple. **”default_edfa_config.json”** as source file.
.. code-block::
.. code-block:: json-object
"Edfa":[{
"type_variety": "std_medium_gain",
"type_def": "variable_gain",
"gain_flatmax": 26,
"gain_min": 15,
"p_max": 21,
"p_max": 23,
"nf_min": 6,
"nf_max": 10
"nf_max": 10,
"out_voa_auto": false,
"allowed_for_design": true
}
]
- Fixed gain with JSON file describing gain figure tilt and gain/noise
figure ripple. **”default_edfa_config.json”** as source file.
.. code-block:: json-object
"Edfa":[{
"type_variety": "std_fixed_gain",
"type_def": "fixed_gain",
"gain_flatmax": 21,
"gain_min": 20,
"p_max": 21,
"nf0": 5.5,
"allowed_for_design": false
}
]
- openroadm with JSON file describing gain figure tilt and gain/noise
figure ripple. **”default_edfa_config.json”** as source file.
.. code-block:: json-object
"Edfa":[{
"type_variety": "low_noise",
"type_def": "openroadm",
"gain_flatmax": 27,
"gain_min": 12,
"p_max": 22,
"nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62],
"allowed_for_design": false
}
]
1.2.2. Fiber element
********************
Fiber element with its parameters:
.. code-block::
.. code-block:: json-object
"Fiber":[{
"type_variety": "SSMF",
"dispersion": 1.67e-05,
"gamma": 0.00127
}
]
RamanFiber element
******************
A special variant of the regular ``Fiber`` where the simulation engine accounts for the Raman effect.
The newly added parameters are nested in the ``raman_efficiency`` dictionary.
Its shape corresponds to typical properties of silica.
More details are available from :cite:`curri_merit_2016`.
The ``cr`` property is the normailzed Raman efficiency, so it is is (almost) independent of the fiber type, while the coefficient actually giving Raman gain is g_R=C_R/Aeff.
The ``frequency_offset`` represents the spectral difference between the pumping photon and the one receiving energy.
.. code-block:: json-object
"RamanFiber":[{
"type_variety": "SSMF",
"dispersion": 1.67e-05,
"gamma": 0.00127,
"raman_efficiency": {
"cr":[
0, 9.4E-06, 2.92E-05, 4.88E-05, 6.82E-05, 8.31E-05, 9.4E-05, 0.0001014, 0.0001069, 0.0001119,
0.0001217, 0.0001268, 0.0001365, 0.000149, 0.000165, 0.000181, 0.0001977, 0.0002192, 0.0002469,
0.0002749, 0.0002999, 0.0003206, 0.0003405, 0.0003592, 0.000374, 0.0003826, 0.0003841, 0.0003826,
0.0003802, 0.0003756, 0.0003549, 0.0003795, 0.000344, 0.0002933, 0.0002024, 0.0001158, 8.46E-05,
7.14E-05, 6.86E-05, 8.5E-05, 8.93E-05, 9.01E-05, 8.15E-05, 6.67E-05, 4.37E-05, 3.28E-05, 2.96E-05,
2.65E-05, 2.57E-05, 2.81E-05, 3.08E-05, 3.67E-05, 5.85E-05, 6.63E-05, 6.36E-05, 5.5E-05, 4.06E-05,
2.77E-05, 2.42E-05, 1.87E-05, 1.6E-05, 1.4E-05, 1.13E-05, 1.05E-05, 9.8E-06, 9.8E-06, 1.13E-05,
1.64E-05, 1.95E-05, 2.38E-05, 2.26E-05, 2.03E-05, 1.48E-05, 1.09E-05, 9.8E-06, 1.05E-05, 1.17E-05,
1.25E-05, 1.21E-05, 1.09E-05, 9.8E-06, 8.2E-06, 6.6E-06, 4.7E-06, 2.7E-06, 1.9E-06, 1.2E-06, 4E-07,
2E-07, 1E-07
],
"frequency_offset":[
0, 0.5e12, 1e12, 1.5e12, 2e12, 2.5e12, 3e12, 3.5e12, 4e12, 4.5e12, 5e12, 5.5e12, 6e12, 6.5e12, 7e12,
7.5e12, 8e12, 8.5e12, 9e12, 9.5e12, 10e12, 10.5e12, 11e12, 11.5e12, 12e12, 12.5e12, 12.75e12,
13e12, 13.25e12, 13.5e12, 14e12, 14.5e12, 14.75e12, 15e12, 15.5e12, 16e12, 16.5e12, 17e12,
17.5e12, 18e12, 18.25e12, 18.5e12, 18.75e12, 19e12, 19.5e12, 20e12, 20.5e12, 21e12, 21.5e12,
22e12, 22.5e12, 23e12, 23.5e12, 24e12, 24.5e12, 25e12, 25.5e12, 26e12, 26.5e12, 27e12, 27.5e12, 28e12,
28.5e12, 29e12, 29.5e12, 30e12, 30.5e12, 31e12, 31.5e12, 32e12, 32.5e12, 33e12, 33.5e12, 34e12, 34.5e12,
35e12, 35.5e12, 36e12, 36.5e12, 37e12, 37.5e12, 38e12, 38.5e12, 39e12, 39.5e12, 40e12, 40.5e12, 41e12,
41.5e12, 42e12
]
}
}
]
1.2.3 Roadm element
*******************
Roadm element with its parameters:
.. code-block:: json-object
"Roadms":[{
"gain_mode_default_loss": 20,
"power_mode_pout_target": -20,
"add_drop_osnr": 38
}
]
1.2.3. Spans element
********************
Spans element with its parameters:
.. code-block::
.. code-block:: json-object
"Spans":[{
"power_mode":true,
"delta_power_range_db": [0,0,0.5],
"max_length": 150,
"length_units": "km",
"max_loss": 28,
"padding": 10,
"EOL": 1,
"con_loss": 0.5
"EOL": 0,
"con_in": 0,
"con_out": 0
}
]
@@ -143,15 +251,18 @@ Spans element with its parameters:
Spectral information with its parameters:
.. code-block::
.. code-block:: json-object
"SI":[{
"f_min": 191.3e12,
"Nch": 80,
"baud_rate": 32e9,
"spacing": 75e9,
"f_max":195.1e12,
"spacing": 50e9,
"power_dbm": 0,
"power_range_db": [0,0,0.5],
"roll_off": 0.15,
"power": 1.2589e-3
"tx_osnr": 40,
"sys_margins": 0
}
]
@@ -162,7 +273,9 @@ Spectral information with its parameters:
Transceiver element with its parameters. **”mode”** can contain multiple
Transceiver operation formats.
.. code-block::
Note that ``OSNR`` parameter refers to the receiver's minimal OSNR threshold for a given mode.
.. code-block:: json-object
"Transceiver":[{
"frequency":{
@@ -171,16 +284,24 @@ Transceiver operation formats.
},
"mode":[
{
"format": "PS_SP64_1",
"baudrate": 32e9,
"OSNR": 9,
"bit_rate": 100e9
"format": "mode 1",
"baud_rate": 32e9,
"OSNR": 11,
"bit_rate": 100e9,
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 37.5e9,
"cost":1
},
{
"format": "PS_SP64_2",
"baudrate": 66e9,
"OSNR": 10,
"bit_rate": 200e9
"format": "mode 2",
"baud_rate": 66e9,
"OSNR": 15,
"bit_rate": 200e9,
"roll_off": 0.15,
"tx_osnr": 40,
"min_spacing": 75e9,
"cost":1
}
]
}
@@ -215,7 +336,7 @@ Network description JSON file root consist of three unordered parts:
- connections contains array of unidirectional connection objects
.. code-block::
.. code-block:: none
{"network_name": "Example Network",
"elements": [{...},
@@ -244,10 +365,10 @@ obligatory.
Transceiver element with its parameters.
.. code-block::
.. code-block:: json
{"uid": "trx Site_A",
metadata": {
"metadata": {
"location": {
"city": "Site_A",
"region": "",
@@ -266,7 +387,7 @@ Transceiver element with its parameters.
ROADM element with its parameters. **“params”** is optional, if not used
default loss value of 20dB is used.
.. code-block::
.. code-block:: json
{"uid": "roadm Site_A",
"metadata": {
@@ -290,12 +411,12 @@ default loss value of 20dB is used.
Fused element with its parameters. **“params”** is optional, if not used
default loss value of 1dB is used.
.. code-block::
.. code-block:: json
{"uid": "ingress fused spans in Site_B",
"metadata": {
"location": {
city": "Site_B",
"city": "Site_B",
"region": "",
"latitude": 0,
"longitude": 0
@@ -313,7 +434,7 @@ default loss value of 1dB is used.
Fiber element with its parameters.
.. code-block::
.. code-block:: json
{"uid": "fiber (Site_A \\u2192 Site_B)",
"metadata": {
@@ -333,13 +454,56 @@ Fiber element with its parameters.
}
}
2.2.5. RamanFiber element
*************************
2.2.5. EDFA element
.. code-block:: json
{
"uid": "Span1",
"type": "RamanFiber",
"type_variety": "SSMF",
"operational": {
"temperature": 283,
"raman_pumps": [
{
"power": 200e-3,
"frequency": 205e12,
"propagation_direction": "counterprop"
},
{
"power": 206e-3,
"frequency": 201e12,
"propagation_direction": "counterprop"
}
]
},
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
}
2.2.6. EDFA element
********************
EDFA element with its parameters.
.. code-block::
.. code-block:: json
{"uid": "Edfa1",
"type": "Edfa",
@@ -365,8 +529,31 @@ Each unidirectional connection object in connections array consist of
two unordered **”from_node”** and **”to_node”** name pair with values
corresponding to element **”uid”**
.. code-block::
.. code-block:: json
{"from_node": "roadm Site_C",
"to_node": "trx Site_C"
}
************************
3. Simulation Parameters
************************
Additional details of the simulation are controlled via ``sim_params.json``:
.. code-block:: json
{
"raman_computed_channels": [1, 18, 37, 56, 75],
"raman_parameters": {
"flag_raman": true,
"space_resolution": 10e3,
"tolerance": 1e-8
},
"nli_parameters": {
"nli_method_name": "ggn_spectrally_separated",
"wdm_grid_size": 50e9,
"dispersion_tolerance": 1,
"phase_shift_tollerance": 0.1
}
}

View File

@@ -1,40 +1,62 @@
{
"paths": [
"response": [
{
"path": {
"path-id": null,
"path-properties": {
"path-metric": [
{
"metric-type": null,
"accumulative-value": null
}
],
"path-srlgs": {
"usage": "not used yet",
"values": ["not used yet"]
"response-id": null,
"path-properties": {
"path-metric": [
{
"metric-type": "SNR@bandwidth",
"accumulative-value": null
},
"path-route-objects": [
{
"path-route-object": {
"index": null,
"unnumbered-hop": {
"node-id": null,
"link-tp-id": null,
"hop-type": null,
"direction": "not used"
},
"label-hop": {
"te-label": {
"generic": "not used yet",
"direction": "not used yet"
}
}
{
"metric-type": "SNR@0.1nm",
"accumulative-value": null
},
{
"metric-type": "OSNR@bandwidth",
"accumulative-value": null
},
{
"metric-type": "OSNR@0.1nm",
"accumulative-value": null
},
{
"metric-type": "reference_power",
"accumulative-value": null
},
{
"metric-type": "path_bandwidth",
"accumulative-value": null
}
],
"path-route-objects": [
{
"path-route-object": {
"index": 0,
"num-unnum-hop": {
"node-id": null,
"link-tp-id": null
}
}
]
}
},
{
"path-route-object": {
"index": 1,
"transponder": {
"transponder-type": null,
"transponder-mode": null
}
}
},
{
"path-route-object": {
"index": 2,
"num-unnum-hop": {
"node-id": null,
"link-tp-id": null
}
}
}
]
}
}
]
}
},

View File

@@ -1,2 +1,2 @@
[pytest]
addopts = -p no:warnings
addopts = --doctest-modules

View File

@@ -1,40 +1,14 @@
alabaster==0.7.12
atomicwrites==1.2.1
attrs==18.2.0
Babel==2.6.0
certifi==2018.10.15
chardet==3.0.4
cycler==0.10.0
decorator==4.3.0
docutils==0.14
idna==2.7
imagesize==1.1.0
Jinja2==2.10
kiwisolver==1.0.1
latexcodec==1.0.5
MarkupSafe==1.0
matplotlib==3.0.0
more-itertools==4.3.0
networkx==2.2
numpy==1.15.2
oset==0.1.3
packaging==18.0
pluggy==0.7.1
py==1.7.0
pybtex==0.21
pybtex-docutils==0.2.1
Pygments==2.2.0
pyparsing==2.2.2
pytest==3.8.2
python-dateutil==2.7.3
pytz==2018.5
PyYAML==3.13
requests==2.19.1
scipy==1.1.0
six==1.11.0
snowballstemmer==1.2.1
Sphinx==1.8.1
sphinxcontrib-bibtex==0.4.0
sphinxcontrib-websupport==1.1.0
urllib3==1.23
xlrd==1.1.0
alabaster>=0.7.12,<1
docutils==0.15.2
flask==1.0.2
flask-restful==0.3.7
matplotlib>=3.1.0,<4
networkx>=2.3,<3
numpy>=1.16.1,<2
pandas==0.24.2
Pygments>=2.4.2,<3
pytest>=4.0.0,<5
scipy>=1.3.0,<2
Sphinx>=2.1.1,<3
sphinxcontrib-bibtex>=0.4.2,<1
xlrd>=1.2.0,<2

View File

@@ -6,54 +6,72 @@
"destination": null,
"src-tp-id": null,
"dst-tp-id": null,
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": null,
"index": null,
"num-unnum-hop": {
"node-id": null,
"link-tp-id": null,
"hop-type": null
}
},
{
"explicit-route-usage": null,
"index": null,
"label-hop": {
"N": null,
"M": null
}
},
{
"explicit-route-usage": null,
"index": null,
"transponder": {
"transponder-type": null,
"transponder-mode": null
}
},
{
"explicit-route-usage": null,
"index": null,
"regenerator": {
"regenerator-id": null,
"transponder-type": null,
"transponder-mode": null
}
}
]
},
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": null,
"trx_mode": null,
"trx_type": "name of the tsp type_variety as listed in the library",
"trx_mode": "optional, name of the mode as listed in the tsp type_variety",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": null,
"max-nb-of-channel": null,
"output-power": null
}
},
"optimizations": {
"explicit-route-include-objects": {
"route-object-include-object": [
{
"index": null,
"unnumbered-hop": {
"node-id": null,
"link-tp-id": "link-tp-id is not used",
"hop-type": null,
"direction": "direction is not used"
},
"label-hop": {
"te-label": {
"generic": "generic is not used",
"direction": "direction is not used"
}
}
}
]
"spacing": mandatory decimal Hz,
"max-nb-of-channel": optional integer,
"output-power": optional decimal W,
"path_bandwidth": optional bit/s
}
}
}],
"synchronization": [
}
],
"synchronization": [ list of disjunctions, optional
{
"synchronization-id": null,
"synchronization-id": "3",
"svec": {
"relaxable": "True",
"link-diverse": "False",
"node-diverse": "False",
"disjointness": "node link",
"request-id-number": [
null ]
null, null ]
},
}
]
}
]
}

View File

@@ -11,13 +11,13 @@ with open(path.join(here, 'README.rst'), encoding='utf-8') as f:
setup(
name='gnpy',
version='0.1.3',
version='2.1',
description='route planning and optimization tool for mesh optical networks',
long_description=long_description,
long_description_content_type='text/x-rst; charset=UTF-8',
url='https://github.com/Telecominfraproject/gnpy',
author='Telecom Infra Project',
author_email='james.powell@telecominfraproject.com',
author_email='jan.kundrat@telecominfraproject.com',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',

View File

@@ -205,6 +205,36 @@
"longitude": 0
}
}
},
{
"uid": "Att_B",
"type": "Fused",
"params":{
"loss":16
},
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 1.0,
"city": "Corlay",
"region": "RLD"
}
}
},
{
"uid": "Att_F",
"type": "Fused",
"params":{
"loss":16
},
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 1.0,
"city": "Corlay",
"region": "RLD"
}
}
}
],
@@ -247,6 +277,10 @@
},
{
"from_node": "Edfa5",
"to_node": "Att_F"
},
{
"from_node": "Att_F",
"to_node": "trx F"
},
{
@@ -255,6 +289,10 @@
},
{
"from_node": "Edfa1",
"to_node": "Att_B"
},
{
"from_node": "Att_B",
"to_node": "trx B"
}
]

View File

@@ -77,8 +77,11 @@ def compare_networks(expected, actual):
def compare_services(expected, actual):
requests = compare(expected['path-request'], actual['path-request'],
key=lambda el: el['request-id'])
synchronizations = compare(expected['synchronisation'], actual['synchronisation'],
key=lambda el: el['synchonization-id'])
synchronizations = compare(expected['path-request'], expected['path-request'],
key=lambda el: el['request-id'])
if 'synchronization' in expected.keys():
synchronizations = compare(expected['synchronization'], actual['synchronization'],
key=lambda el: el['synchronization-id'])
return ServicesResults(requests, synchronizations)
def compare_paths(expected_output, actual_output):
@@ -105,16 +108,16 @@ def encode_sets(obj):
if __name__ == '__main__':
args = parser.parse_args()
with open(args.expected_output) as f:
with open(args.expected_output, encoding='utf-8') as f:
expected = load(f)
with open(args.actual_output) as f:
with open(args.actual_output, encoding='utf-8') as f:
actual = load(f)
result = COMPARISONS[args.comparison](expected, actual)
if args.output:
with open(args.output, 'w') as f:
dump(result, f, default=encode_sets, indent=2)
with open(args.output, 'w', encoding='utf-8') as f:
dump(result, f, default=encode_sets, indent=2, ensure_ascii=False)
else:
print(str(result))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +1,11 @@
{ "Edfa":[{
"type_variety": "CienaDB_medium_gain",
"type_def": "advanced_model",
"gain_flatmax": 25,
"gain_min": 15,
"p_max": 21,
"advanced_config_from_json": "std_medium_gain_advanced_config.json",
"out_voa_auto": false,
"allowed_for_design": true
},
{
@@ -14,6 +16,7 @@
"p_max": 21,
"nf_min": 6,
"nf_max": 10,
"out_voa_auto": false,
"allowed_for_design": true
},
{
@@ -24,6 +27,7 @@
"p_max": 21,
"nf_min": 7,
"nf_max": 11,
"out_voa_auto": false,
"allowed_for_design": true
},
{
@@ -34,6 +38,7 @@
"p_max": 21,
"nf_min": 5.8,
"nf_max": 10,
"out_voa_auto": false,
"allowed_for_design": true
},
{
@@ -44,7 +49,16 @@
"p_max": 21,
"nf0": 5,
"allowed_for_design": true
}
},
{
"type_variety": "std_booster",
"type_def": "fixed_gain",
"gain_flatmax": 21,
"gain_min": 20,
"p_max": 21,
"nf0": 5,
"allowed_for_design": false
}
],
"Fiber":[{
"type_variety": "SSMF",
@@ -52,9 +66,11 @@
"gamma": 0.00127
}
],
"Spans":[{
"power_mode": true,
"delta_power_range_db": [0,0,1],
"Span":[{
"power_mode":true,
"delta_power_range_db": [0,0,0.5],
"max_fiber_lineic_loss_for_raman": 0.25,
"target_extended_gain": 2.5,
"max_length": 150,
"length_units": "km",
"max_loss": 28,
@@ -64,9 +80,13 @@
"con_out": 0
}
],
"Roadms":[{
"gain_mode_default_loss": 20,
"power_mode_pref": -20
"Roadm":[{
"target_pch_out_db": -20,
"add_drop_osnr": 38,
"restrictions": {
"preamp_variety_list":[],
"booster_variety_list":[]
}
}],
"SI":[{
"f_min": 191.3e12,
@@ -74,10 +94,10 @@
"baud_rate": 32e9,
"spacing": 50e9,
"power_dbm": 0,
"power_range_db": [0,0.5,0.5],
"power_range_db": [0,0,0.5],
"roll_off": 0.15,
"OSNR": 15,
"bit_rate":100e9
"tx_osnr": 100,
"sys_margins": 0
}],
"Transceiver":[
{
@@ -92,15 +112,41 @@
"baud_rate": 32e9,
"OSNR": 11,
"bit_rate": 100e9,
"roll_off": 0.15
"roll_off": 0.15,
"tx_osnr": 100,
"min_spacing": 50e9,
"cost":1
},
{
"format": "PS_SP64_2",
"baud_rate": 64e9,
"OSNR": 15,
"bit_rate": 200e9,
"roll_off": 0.15
}
"roll_off": 0.15,
"tx_osnr": 100,
"min_spacing": 75e9,
"cost":1
},
{
"format": "mode 1",
"baud_rate": 32e9,
"OSNR": 11,
"bit_rate": 100e9,
"roll_off": 0.15,
"tx_osnr": 100,
"min_spacing": 50e9,
"cost":1
},
{
"format": "mode 2",
"baud_rate": 64e9,
"OSNR": 15,
"bit_rate": 200e9,
"roll_off": 0.15,
"tx_osnr": 100,
"min_spacing": 75e9,
"cost":1
}
]
},
{
@@ -115,7 +161,69 @@
"baud_rate": 32e9,
"OSNR": 19,
"bit_rate": 200e9,
"roll_off": 0.15
"roll_off": 0.15,
"tx_osnr": 100,
"min_spacing": 50e9,
"cost":1
}
]
},
{
"type_variety": "Voyager",
"frequency":{
"min": 191.35e12,
"max": 196.1e12
},
"mode":[
{
"format": "mode 1",
"baud_rate": 32e9,
"OSNR": 12,
"bit_rate": 100e9,
"roll_off": 0.15,
"tx_osnr": 45,
"min_spacing": 50e9,
"cost":1
},
{
"format": "mode 3",
"baud_rate": 44e9,
"OSNR": 18,
"bit_rate": 300e9,
"roll_off": 0.15,
"tx_osnr": 45,
"min_spacing": 62.5e9,
"cost":1
},
{
"format": "mode 2",
"baud_rate": 66e9,
"OSNR": 21,
"bit_rate": 400e9,
"roll_off": 0.15,
"tx_osnr": 45,
"min_spacing": 75e9,
"cost":1
},
{
"format": "mode 2 - fake",
"baud_rate": 66e9,
"OSNR": 21,
"bit_rate": 400e9,
"roll_off": 0.15,
"tx_osnr": 45,
"min_spacing": 75e9,
"cost":1
},
{
"format": "mode 4",
"baud_rate": 66e9,
"OSNR": 16,
"bit_rate": 200e9,
"roll_off": 0.15,
"tx_osnr": 45,
"min_spacing": 75e9,
"cost":1
}
]
}

Binary file not shown.

View File

@@ -1,694 +0,0 @@
{
"elements": [
{
"uid": "trx Lannion_CAS",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Transceiver"
},
{
"uid": "trx Lorient_KMA",
"metadata": {
"location": {
"city": "Lorient_KMA",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Transceiver"
},
{
"uid": "trx Vannes_KBE",
"metadata": {
"location": {
"city": "Vannes_KBE",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Transceiver"
},
{
"uid": "trx Rennes_STA",
"metadata": {
"location": {
"city": "Rennes_STA",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Transceiver"
},
{
"uid": "trx Brest_KLA",
"metadata": {
"location": {
"city": "Brest_KLA",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Transceiver"
},
{
"uid": "roadm Lannion_CAS",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Roadm"
},
{
"uid": "roadm Lorient_KMA",
"metadata": {
"location": {
"city": "Lorient_KMA",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Roadm"
},
{
"uid": "roadm Vannes_KBE",
"metadata": {
"location": {
"city": "Vannes_KBE",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Roadm"
},
{
"uid": "roadm Rennes_STA",
"metadata": {
"location": {
"city": "Rennes_STA",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Roadm"
},
{
"uid": "roadm Brest_KLA",
"metadata": {
"location": {
"city": "Brest_KLA",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Roadm"
},
{
"uid": "ingress fused spans in Corlay",
"metadata": {
"location": {
"city": "Corlay",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Fused"
},
{
"uid": "ingress fused spans in Loudeac",
"metadata": {
"location": {
"city": "Loudeac",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Fused"
},
{
"uid": "egress fused spans in Corlay",
"metadata": {
"location": {
"city": "Corlay",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Fused"
},
{
"uid": "egress fused spans in Loudeac",
"metadata": {
"location": {
"city": "Loudeac",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Fused"
},
{
"uid": "fiber (Lannion_CAS \u2192 Corlay)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 20.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Corlay \u2192 Loudeac)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 50.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F01",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 10.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 65.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 40.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Morlaix \u2192 Brest_KLA)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 35.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Corlay \u2192 Lannion_CAS)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 20.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Loudeac \u2192 Corlay)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 50.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F01",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 10.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 65.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 40.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Brest_KLA \u2192 Morlaix)-",
"metadata": {
"location": {
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 35.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "egress edfa in Lannion_CAS to Morlaix",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Edfa",
"type_variety": "test",
"operational": {
"gain_target": 0,
"tilt_target": 0
}
},
{
"uid": "egress edfa in Lannion_CAS to Corlay",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Edfa",
"type_variety": "test",
"operational": {
"gain_target": 0,
"tilt_target": 0
}
},
{
"uid": "egress edfa in Lannion_CAS to Stbrieuc",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Edfa",
"type_variety": "test",
"operational": {
"gain_target": 0,
"tilt_target": 0
}
},
{
"uid": "egress edfa in Corlay to Loudeac",
"metadata": {
"location": {
"city": "Corlay",
"region": "RLD",
"latitude": 0,
"longitude": 0
}
},
"type": "Edfa",
"type_variety": "test",
"operational": {
"gain_target": 0,
"tilt_target": 0
}
}
],
"connections": [
{
"from_node": "roadm Lannion_CAS",
"to_node": "egress edfa in Lannion_CAS to Corlay"
},
{
"from_node": "egress edfa in Lannion_CAS to Corlay",
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-"
},
{
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "roadm Lannion_CAS",
"to_node": "egress edfa in Lannion_CAS to Stbrieuc"
},
{
"from_node": "egress edfa in Lannion_CAS to Stbrieuc",
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-"
},
{
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "roadm Lannion_CAS",
"to_node": "egress edfa in Lannion_CAS to Morlaix"
},
{
"from_node": "egress edfa in Lannion_CAS to Morlaix",
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-"
},
{
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-",
"to_node": "ingress fused spans in Corlay"
},
{
"from_node": "ingress fused spans in Corlay",
"to_node": "fiber (Corlay \u2192 Loudeac)-"
},
{
"from_node": "fiber (Loudeac \u2192 Corlay)-",
"to_node": "egress fused spans in Corlay"
},
{
"from_node": "egress fused spans in Corlay",
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-"
},
{
"from_node": "fiber (Corlay \u2192 Loudeac)-",
"to_node": "ingress fused spans in Loudeac"
},
{
"from_node": "ingress fused spans in Loudeac",
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-"
},
{
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-",
"to_node": "egress fused spans in Loudeac"
},
{
"from_node": "egress fused spans in Loudeac",
"to_node": "fiber (Loudeac \u2192 Corlay)-"
},
{
"from_node": "roadm Lorient_KMA",
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-"
},
{
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-",
"to_node": "roadm Lorient_KMA"
},
{
"from_node": "roadm Lorient_KMA",
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F01"
},
{
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F01",
"to_node": "roadm Lorient_KMA"
},
{
"from_node": "roadm Vannes_KBE",
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F01"
},
{
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F01",
"to_node": "roadm Vannes_KBE"
},
{
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-",
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-"
},
{
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-",
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-"
},
{
"from_node": "roadm Rennes_STA",
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-"
},
{
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-",
"to_node": "roadm Rennes_STA"
},
{
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-",
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-"
},
{
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-",
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-"
},
{
"from_node": "roadm Brest_KLA",
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-"
},
{
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-",
"to_node": "roadm Brest_KLA"
},
{
"from_node": "trx Lannion_CAS",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "roadm Lannion_CAS",
"to_node": "trx Lannion_CAS"
},
{
"from_node": "trx Lorient_KMA",
"to_node": "roadm Lorient_KMA"
},
{
"from_node": "roadm Lorient_KMA",
"to_node": "trx Lorient_KMA"
},
{
"from_node": "trx Vannes_KBE",
"to_node": "roadm Vannes_KBE"
},
{
"from_node": "roadm Vannes_KBE",
"to_node": "trx Vannes_KBE"
},
{
"from_node": "trx Rennes_STA",
"to_node": "roadm Rennes_STA"
},
{
"from_node": "roadm Rennes_STA",
"to_node": "trx Rennes_STA"
},
{
"from_node": "trx Brest_KLA",
"to_node": "roadm Brest_KLA"
},
{
"from_node": "roadm Brest_KLA",
"to_node": "trx Brest_KLA"
}
]
}

View File

@@ -1,82 +0,0 @@
{
"path-request": [
{
"request-id": "0",
"source": "BREST_KLA",
"destination": "Vannes_KBE",
"src-tp-id": "trx BREST_KLA",
"dst-tp-id": "trx Vannes_KBE",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001
}
},
"optimizations": {
"explicit-route-include-objects": []
}
},
{
"request-id": "1",
"source": "Lorient_KMA",
"destination": "Vannes_KBE",
"src-tp-id": "trx Lorient_KMA",
"dst-tp-id": "trx Vannes_KBE",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001
}
},
"optimizations": {
"explicit-route-include-objects": []
}
}
],
"synchronisation": [
{
"synchonization-id": "0",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"request-id-number": [
"0",
"1"
]
}
},
{
"synchonization-id": "1",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"request-id-number": [
"1",
"0"
]
}
}
]
}

View File

@@ -0,0 +1,97 @@
,signal,ase,nli
0,0.0002869472910750076,3.829243751386179e-08,2.157043502374111e-07
1,0.000284426444181902,3.8108068606265256e-08,2.1799950841472648e-07
2,0.0002819286625240274,3.7925434667811625e-08,2.2023841125044652e-07
3,0.0002794537215642205,3.774451238936698e-08,2.224218994135113e-07
4,0.0002756243295734432,3.739256063612741e-08,2.2343448272114653e-07
5,0.0002718482755003954,3.7044477620123535e-08,2.2437826192962217e-07
6,0.0002681247979313455,3.6700201831013766e-08,2.2525495466695055e-07
7,0.0002644507001383656,3.635953568122817e-08,2.2606415187870565e-07
8,0.0002608253488031495,3.602242321653821e-08,2.268074852150968e-07
9,0.00025690468888571607,3.564391587795796e-08,2.2718285844824803e-07
10,0.0002530414048173237,3.5269661038482016e-08,2.2749429758476786e-07
11,0.0002492279873568786,3.4899736994459975e-08,2.277374766526846e-07
12,0.0002454639458992114,3.4534068616323406e-08,2.2791414400784552e-07
13,0.00024174879168999762,3.417258192135115e-08,2.280260208417629e-07
14,0.00023798746912556782,3.3802278288721e-08,2.2798420759779948e-07
15,0.00023427697848575827,3.3436265380528345e-08,2.2788101592690985e-07
16,0.00023061678363205047,3.30744682841412e-08,2.2771816297652923e-07
17,0.00022700656967542085,3.271682680678683e-08,2.2749755602884014e-07
18,0.0002234457948096593,3.236326805537296e-08,2.236182244259085e-07
19,0.0002195336193536736,3.195819496314336e-08,2.193976173454328e-07
20,0.00021568313139087874,3.155821230359698e-08,2.1524945887103656e-07
21,0.00021189361260563733,3.116322489050993e-08,2.1117277567390236e-07
22,0.00020816423698459606,3.0773141693336075e-08,2.0716649124094935e-07
23,0.0002044941867087381,3.038787321635763e-08,2.032295417993187e-07
24,0.00020116081520673765,3.00440338127331e-08,1.9963693210324778e-07
25,0.00019787569461895006,2.9704199888387147e-08,1.9610141536963145e-07
26,0.00019463824873065924,2.9368302916351224e-08,1.9262221997372471e-07
27,0.0001914486066928752,2.903632427420397e-08,1.8919927457565086e-07
28,0.00018830616497930887,2.870819640079397e-08,1.858317840670677e-07
29,0.00018521032563368435,2.838385281897912e-08,1.8251896218718178e-07
30,0.00018216049720979434,2.8063228018898468e-08,1.7926003240909075e-07
31,0.0001791561867005718,2.7746255438682553e-08,1.76054318231933e-07
32,0.00017619680881744213,2.7432871709278503e-08,1.7290105534292413e-07
33,0.00017328178390236163,2.7123014438128492e-08,1.6979948820364567e-07
34,0.00017049664136784971,2.6828118382010868e-08,1.668331233176527e-07
35,0.0001677518922618999,2.6536524600591003e-08,1.639139770351797e-07
36,0.00016504703499520338,2.6248178236430935e-08,1.6104139135571758e-07
37,0.0001623826677977635,2.596311344676757e-08,1.579538179464147e-07
38,0.0001597582427278653,2.5681275450827438e-08,1.549209871570718e-07
39,0.0001571732182028194,2.5402610321183817e-08,1.5194201541886346e-07
40,0.00015462705891566638,2.512706495768609e-08,1.490160317195833e-07
41,0.00015212101646392648,2.4854546722771583e-08,1.4614388817377845e-07
42,0.00014965447757986727,2.4585006051161647e-08,1.4332463586636234e-07
43,0.00014722683809507942,2.4318394065447274e-08,1.4055734193947907e-07
44,0.0001447164668892396,2.4034548127308286e-08,1.3772590008270512e-07
45,0.00014224784112375704,2.3753926686114635e-08,1.3494914625939818e-07
46,0.00013982028367499942,2.3476475779461364e-08,1.3222606385780792e-07
47,0.00013743418748445304,2.3202244204140228e-08,1.2955665313419502e-07
48,0.00013508884015386575,2.2931178307200807e-08,1.269398709602497e-07
49,0.00013278354172499636,2.2663225269637508e-08,1.243746944213211e-07
50,0.0001305176041972383,2.2398333101097452e-08,1.2186012017916144e-07
51,0.00012829168984639723,2.2136419884279648e-08,1.1939640981690787e-07
52,0.00012610506317956035,2.1877436733290284e-08,1.169825203056231e-07
53,0.000123957002859191,2.1621335420785434e-08,1.1461743054419468e-07
54,0.00012180241033649304,2.1360152817604167e-08,1.1225922783038433e-07
55,0.00011968650905779935,2.1101906890578305e-08,1.0994951537259513e-07
56,0.000117608577762061,2.0846548870078847e-08,1.0757395097864581e-07
57,0.00011556891128259058,2.0594151467353748e-08,1.0524972555992308e-07
58,0.00011356676177301841,2.0344667169015006e-08,1.0297570549831857e-07
59,0.00011160139690545192,2.00980493433389e-08,1.0075078305548045e-07
60,0.00010967209909252646,1.985425227516509e-08,9.857387536569511e-08
61,0.00010777915187087522,1.9613208260272527e-08,9.644480679616336e-08
62,0.00010592181397175155,1.937487453011716e-08,9.436248424611683e-08
63,0.00010409936038610526,1.913920913597429e-08,9.23258408012148e-08
64,0.00010246447558375888,1.8936226281729442e-08,9.046927135291653e-08
65,0.00010085803630104006,1.87354387522902e-08,8.865067925960373e-08
66,9.927950010553608e-05,1.853681852284204e-08,8.686925127146881e-08
67,9.772837346090978e-05,1.834034443508121e-08,8.512422533827548e-08
68,9.620413430112097e-05,1.8145990199784238e-08,8.341482250639003e-08
69,9.470627135913274e-05,1.795373041706864e-08,8.174028142913882e-08
70,9.323428359797426e-05,1.776354066998682e-08,8.009985766376296e-08
71,9.178813743816942e-05,1.7575386852678668e-08,7.849321446941785e-08
72,9.03673300948529e-05,1.7389247191220127e-08,7.691961625609547e-08
73,8.897136946427622e-05,1.7205101122769978e-08,7.537834446342857e-08
74,8.760740745800998e-05,1.7025337039390582e-08,7.387513417420477e-08
75,8.626710469266086e-05,1.684760610568072e-08,7.274492099363918e-08
76,8.495000573672162e-05,1.6671894857242002e-08,7.163427447510873e-08
77,8.365569697520994e-05,1.649819993412593e-08,7.054284583689279e-08
78,8.238374036674246e-05,1.6326513144182658e-08,6.947026569965565e-08
79,8.113370706498376e-05,1.6156829499842502e-08,6.841617243780552e-08
80,7.990517700269747e-05,1.5989147949913657e-08,6.738021182874466e-08
81,7.86978423091888e-05,1.5823469853370494e-08,6.636212425984957e-08
82,7.751129541079691e-05,1.5659805288834794e-08,6.536156604375694e-08
83,7.634513730458643e-05,1.549817228640182e-08,6.4378200720386e-08
84,7.530262080974352e-05,1.5364274253504764e-08,6.349909645089537e-08
85,7.427675504203847e-05,1.523236211656126e-08,6.263403294276386e-08
86,7.326723873728748e-05,1.5102509684796054e-08,6.17827561543225e-08
87,7.227232864621635e-05,1.497407531211962e-08,6.094379608688325e-08
88,7.129179755315639e-05,1.4847053209180731e-08,6.011696114034632e-08
89,7.032542203609286e-05,1.4721438007057792e-08,5.930206291361871e-08
90,6.937298231674387e-05,1.4597224779058979e-08,5.8498916078193026e-08
91,6.843339696762452e-05,1.4474430063551042e-08,5.7706608718023995e-08
92,6.750649045006184e-05,1.435304906112738e-08,5.692499280974924e-08
93,6.659208967850971e-05,1.4233077472549144e-08,5.615392239861094e-08
94,6.554258932109723e-05,1.4075047005202515e-08,5.5268928972034715e-08
95,6.450957734109015e-05,1.3918652473373596e-08,5.439783940505763e-08
1 signal ase nli
2 0 0.0002869472910750076 3.829243751386179e-08 2.157043502374111e-07
3 1 0.000284426444181902 3.8108068606265256e-08 2.1799950841472648e-07
4 2 0.0002819286625240274 3.7925434667811625e-08 2.2023841125044652e-07
5 3 0.0002794537215642205 3.774451238936698e-08 2.224218994135113e-07
6 4 0.0002756243295734432 3.739256063612741e-08 2.2343448272114653e-07
7 5 0.0002718482755003954 3.7044477620123535e-08 2.2437826192962217e-07
8 6 0.0002681247979313455 3.6700201831013766e-08 2.2525495466695055e-07
9 7 0.0002644507001383656 3.635953568122817e-08 2.2606415187870565e-07
10 8 0.0002608253488031495 3.602242321653821e-08 2.268074852150968e-07
11 9 0.00025690468888571607 3.564391587795796e-08 2.2718285844824803e-07
12 10 0.0002530414048173237 3.5269661038482016e-08 2.2749429758476786e-07
13 11 0.0002492279873568786 3.4899736994459975e-08 2.277374766526846e-07
14 12 0.0002454639458992114 3.4534068616323406e-08 2.2791414400784552e-07
15 13 0.00024174879168999762 3.417258192135115e-08 2.280260208417629e-07
16 14 0.00023798746912556782 3.3802278288721e-08 2.2798420759779948e-07
17 15 0.00023427697848575827 3.3436265380528345e-08 2.2788101592690985e-07
18 16 0.00023061678363205047 3.30744682841412e-08 2.2771816297652923e-07
19 17 0.00022700656967542085 3.271682680678683e-08 2.2749755602884014e-07
20 18 0.0002234457948096593 3.236326805537296e-08 2.236182244259085e-07
21 19 0.0002195336193536736 3.195819496314336e-08 2.193976173454328e-07
22 20 0.00021568313139087874 3.155821230359698e-08 2.1524945887103656e-07
23 21 0.00021189361260563733 3.116322489050993e-08 2.1117277567390236e-07
24 22 0.00020816423698459606 3.0773141693336075e-08 2.0716649124094935e-07
25 23 0.0002044941867087381 3.038787321635763e-08 2.032295417993187e-07
26 24 0.00020116081520673765 3.00440338127331e-08 1.9963693210324778e-07
27 25 0.00019787569461895006 2.9704199888387147e-08 1.9610141536963145e-07
28 26 0.00019463824873065924 2.9368302916351224e-08 1.9262221997372471e-07
29 27 0.0001914486066928752 2.903632427420397e-08 1.8919927457565086e-07
30 28 0.00018830616497930887 2.870819640079397e-08 1.858317840670677e-07
31 29 0.00018521032563368435 2.838385281897912e-08 1.8251896218718178e-07
32 30 0.00018216049720979434 2.8063228018898468e-08 1.7926003240909075e-07
33 31 0.0001791561867005718 2.7746255438682553e-08 1.76054318231933e-07
34 32 0.00017619680881744213 2.7432871709278503e-08 1.7290105534292413e-07
35 33 0.00017328178390236163 2.7123014438128492e-08 1.6979948820364567e-07
36 34 0.00017049664136784971 2.6828118382010868e-08 1.668331233176527e-07
37 35 0.0001677518922618999 2.6536524600591003e-08 1.639139770351797e-07
38 36 0.00016504703499520338 2.6248178236430935e-08 1.6104139135571758e-07
39 37 0.0001623826677977635 2.596311344676757e-08 1.579538179464147e-07
40 38 0.0001597582427278653 2.5681275450827438e-08 1.549209871570718e-07
41 39 0.0001571732182028194 2.5402610321183817e-08 1.5194201541886346e-07
42 40 0.00015462705891566638 2.512706495768609e-08 1.490160317195833e-07
43 41 0.00015212101646392648 2.4854546722771583e-08 1.4614388817377845e-07
44 42 0.00014965447757986727 2.4585006051161647e-08 1.4332463586636234e-07
45 43 0.00014722683809507942 2.4318394065447274e-08 1.4055734193947907e-07
46 44 0.0001447164668892396 2.4034548127308286e-08 1.3772590008270512e-07
47 45 0.00014224784112375704 2.3753926686114635e-08 1.3494914625939818e-07
48 46 0.00013982028367499942 2.3476475779461364e-08 1.3222606385780792e-07
49 47 0.00013743418748445304 2.3202244204140228e-08 1.2955665313419502e-07
50 48 0.00013508884015386575 2.2931178307200807e-08 1.269398709602497e-07
51 49 0.00013278354172499636 2.2663225269637508e-08 1.243746944213211e-07
52 50 0.0001305176041972383 2.2398333101097452e-08 1.2186012017916144e-07
53 51 0.00012829168984639723 2.2136419884279648e-08 1.1939640981690787e-07
54 52 0.00012610506317956035 2.1877436733290284e-08 1.169825203056231e-07
55 53 0.000123957002859191 2.1621335420785434e-08 1.1461743054419468e-07
56 54 0.00012180241033649304 2.1360152817604167e-08 1.1225922783038433e-07
57 55 0.00011968650905779935 2.1101906890578305e-08 1.0994951537259513e-07
58 56 0.000117608577762061 2.0846548870078847e-08 1.0757395097864581e-07
59 57 0.00011556891128259058 2.0594151467353748e-08 1.0524972555992308e-07
60 58 0.00011356676177301841 2.0344667169015006e-08 1.0297570549831857e-07
61 59 0.00011160139690545192 2.00980493433389e-08 1.0075078305548045e-07
62 60 0.00010967209909252646 1.985425227516509e-08 9.857387536569511e-08
63 61 0.00010777915187087522 1.9613208260272527e-08 9.644480679616336e-08
64 62 0.00010592181397175155 1.937487453011716e-08 9.436248424611683e-08
65 63 0.00010409936038610526 1.913920913597429e-08 9.23258408012148e-08
66 64 0.00010246447558375888 1.8936226281729442e-08 9.046927135291653e-08
67 65 0.00010085803630104006 1.87354387522902e-08 8.865067925960373e-08
68 66 9.927950010553608e-05 1.853681852284204e-08 8.686925127146881e-08
69 67 9.772837346090978e-05 1.834034443508121e-08 8.512422533827548e-08
70 68 9.620413430112097e-05 1.8145990199784238e-08 8.341482250639003e-08
71 69 9.470627135913274e-05 1.795373041706864e-08 8.174028142913882e-08
72 70 9.323428359797426e-05 1.776354066998682e-08 8.009985766376296e-08
73 71 9.178813743816942e-05 1.7575386852678668e-08 7.849321446941785e-08
74 72 9.03673300948529e-05 1.7389247191220127e-08 7.691961625609547e-08
75 73 8.897136946427622e-05 1.7205101122769978e-08 7.537834446342857e-08
76 74 8.760740745800998e-05 1.7025337039390582e-08 7.387513417420477e-08
77 75 8.626710469266086e-05 1.684760610568072e-08 7.274492099363918e-08
78 76 8.495000573672162e-05 1.6671894857242002e-08 7.163427447510873e-08
79 77 8.365569697520994e-05 1.649819993412593e-08 7.054284583689279e-08
80 78 8.238374036674246e-05 1.6326513144182658e-08 6.947026569965565e-08
81 79 8.113370706498376e-05 1.6156829499842502e-08 6.841617243780552e-08
82 80 7.990517700269747e-05 1.5989147949913657e-08 6.738021182874466e-08
83 81 7.86978423091888e-05 1.5823469853370494e-08 6.636212425984957e-08
84 82 7.751129541079691e-05 1.5659805288834794e-08 6.536156604375694e-08
85 83 7.634513730458643e-05 1.549817228640182e-08 6.4378200720386e-08
86 84 7.530262080974352e-05 1.5364274253504764e-08 6.349909645089537e-08
87 85 7.427675504203847e-05 1.523236211656126e-08 6.263403294276386e-08
88 86 7.326723873728748e-05 1.5102509684796054e-08 6.17827561543225e-08
89 87 7.227232864621635e-05 1.497407531211962e-08 6.094379608688325e-08
90 88 7.129179755315639e-05 1.4847053209180731e-08 6.011696114034632e-08
91 89 7.032542203609286e-05 1.4721438007057792e-08 5.930206291361871e-08
92 90 6.937298231674387e-05 1.4597224779058979e-08 5.8498916078193026e-08
93 91 6.843339696762452e-05 1.4474430063551042e-08 5.7706608718023995e-08
94 92 6.750649045006184e-05 1.435304906112738e-08 5.692499280974924e-08
95 93 6.659208967850971e-05 1.4233077472549144e-08 5.615392239861094e-08
96 94 6.554258932109723e-05 1.4075047005202515e-08 5.5268928972034715e-08
97 95 6.450957734109015e-05 1.3918652473373596e-08 5.439783940505763e-08

Binary file not shown.

View File

@@ -1,804 +0,0 @@
{
"elements": [
{
"uid": "trx Lannion_CAS",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 2.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "trx Lorient_KMA",
"metadata": {
"location": {
"city": "Lorient_KMA",
"region": "RLD",
"latitude": 2.0,
"longitude": 3.0
}
},
"type": "Transceiver"
},
{
"uid": "trx Vannes_KBE",
"metadata": {
"location": {
"city": "Vannes_KBE",
"region": "RLD",
"latitude": 2.0,
"longitude": 4.0
}
},
"type": "Transceiver"
},
{
"uid": "trx Rennes_STA",
"metadata": {
"location": {
"city": "Rennes_STA",
"region": "RLD",
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "trx Brest_KLA",
"metadata": {
"location": {
"city": "Brest_KLA",
"region": "RLD",
"latitude": 4.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "trx toto",
"metadata": {
"location": {
"city": "toto",
"region": "",
"latitude": 6.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "trx tata",
"metadata": {
"location": {
"city": "tata",
"region": "",
"latitude": 7.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "roadm Lannion_CAS",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 2.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "roadm Lorient_KMA",
"metadata": {
"location": {
"city": "Lorient_KMA",
"region": "RLD",
"latitude": 2.0,
"longitude": 3.0
}
},
"type": "Roadm"
},
{
"uid": "roadm Vannes_KBE",
"metadata": {
"location": {
"city": "Vannes_KBE",
"region": "RLD",
"latitude": 2.0,
"longitude": 4.0
}
},
"type": "Roadm"
},
{
"uid": "roadm Rennes_STA",
"metadata": {
"location": {
"city": "Rennes_STA",
"region": "RLD",
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "roadm Brest_KLA",
"metadata": {
"location": {
"city": "Brest_KLA",
"region": "RLD",
"latitude": 4.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "roadm toto",
"metadata": {
"location": {
"city": "toto",
"region": "",
"latitude": 6.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "roadm tata",
"metadata": {
"location": {
"city": "tata",
"region": "",
"latitude": 7.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "ingress fused spans in Corlay",
"metadata": {
"location": {
"city": "Corlay",
"region": "RLD",
"latitude": 2.0,
"longitude": 1.0
}
},
"type": "Fused"
},
{
"uid": "ingress fused spans in Loudeac",
"metadata": {
"location": {
"city": "Loudeac",
"region": "RLD",
"latitude": 2.0,
"longitude": 2.0
}
},
"type": "Fused"
},
{
"uid": "ingress fused spans in Morlaix",
"metadata": {
"location": {
"city": "Morlaix",
"region": "RLD",
"latitude": 3.0,
"longitude": 0.0
}
},
"type": "Fused"
},
{
"uid": "egress fused spans in Corlay",
"metadata": {
"location": {
"city": "Corlay",
"region": "RLD",
"latitude": 2.0,
"longitude": 1.0
}
},
"type": "Fused"
},
{
"uid": "egress fused spans in Loudeac",
"metadata": {
"location": {
"city": "Loudeac",
"region": "RLD",
"latitude": 2.0,
"longitude": 2.0
}
},
"type": "Fused"
},
{
"uid": "egress fused spans in Morlaix",
"metadata": {
"location": {
"city": "Morlaix",
"region": "RLD",
"latitude": 3.0,
"longitude": 0.0
}
},
"type": "Fused"
},
{
"uid": "fiber (Lannion_CAS \u2192 Corlay)-F061",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 0.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 20.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Corlay \u2192 Loudeac)-F010",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 1.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 50.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 2.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 3.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 10.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
"metadata": {
"location": {
"latitude": 1.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 65.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
"metadata": {
"location": {
"latitude": 2.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 40.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Morlaix \u2192 Brest_KLA)-F060",
"metadata": {
"location": {
"latitude": 3.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 35.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (toto \u2192 tata)-",
"metadata": {
"location": {
"latitude": 6.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 80.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Corlay \u2192 Lannion_CAS)-F061",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 0.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 20.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Loudeac \u2192 Corlay)-F010",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 1.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 50.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 2.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 3.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 10.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
"metadata": {
"location": {
"latitude": 1.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 65.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
"metadata": {
"location": {
"latitude": 2.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 40.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Brest_KLA \u2192 Morlaix)-F060",
"metadata": {
"location": {
"latitude": 3.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 35.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (tata \u2192 toto)-",
"metadata": {
"location": {
"latitude": 6.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 80.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "egress edfa in Lannion_CAS to Corlay",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 2.0,
"longitude": 0.0
}
},
"type": "Edfa",
"type_variety": "test",
"operational": {
"gain_target": 0,
"tilt_target": 0
}
},
{
"uid": "egress edfa in Lorient_KMA to Loudeac",
"metadata": {
"location": {
"city": "Lorient_KMA",
"region": "RLD",
"latitude": 2.0,
"longitude": 3.0
}
},
"type": "Edfa",
"type_variety": "test",
"operational": {
"gain_target": 0,
"tilt_target": 0
}
}
],
"connections": [
{
"from_node": "roadm Lannion_CAS",
"to_node": "egress edfa in Lannion_CAS to Corlay"
},
{
"from_node": "egress edfa in Lannion_CAS to Corlay",
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061"
},
{
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "roadm Lannion_CAS",
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056"
},
{
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "roadm Lannion_CAS",
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059"
},
{
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061",
"to_node": "ingress fused spans in Corlay"
},
{
"from_node": "ingress fused spans in Corlay",
"to_node": "fiber (Corlay \u2192 Loudeac)-F010"
},
{
"from_node": "fiber (Loudeac \u2192 Corlay)-F010",
"to_node": "egress fused spans in Corlay"
},
{
"from_node": "egress fused spans in Corlay",
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061"
},
{
"from_node": "fiber (Corlay \u2192 Loudeac)-F010",
"to_node": "ingress fused spans in Loudeac"
},
{
"from_node": "ingress fused spans in Loudeac",
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054"
},
{
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
"to_node": "egress fused spans in Loudeac"
},
{
"from_node": "egress fused spans in Loudeac",
"to_node": "fiber (Loudeac \u2192 Corlay)-F010"
},
{
"from_node": "roadm Lorient_KMA",
"to_node": "egress edfa in Lorient_KMA to Loudeac"
},
{
"from_node": "egress edfa in Lorient_KMA to Loudeac",
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054"
},
{
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
"to_node": "roadm Lorient_KMA"
},
{
"from_node": "roadm Lorient_KMA",
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055"
},
{
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
"to_node": "roadm Lorient_KMA"
},
{
"from_node": "roadm Vannes_KBE",
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055"
},
{
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
"to_node": "roadm Vannes_KBE"
},
{
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057"
},
{
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056"
},
{
"from_node": "roadm Rennes_STA",
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057"
},
{
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
"to_node": "roadm Rennes_STA"
},
{
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
"to_node": "ingress fused spans in Morlaix"
},
{
"from_node": "ingress fused spans in Morlaix",
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060"
},
{
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060",
"to_node": "egress fused spans in Morlaix"
},
{
"from_node": "egress fused spans in Morlaix",
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059"
},
{
"from_node": "roadm Brest_KLA",
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060"
},
{
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060",
"to_node": "roadm Brest_KLA"
},
{
"from_node": "roadm toto",
"to_node": "fiber (toto \u2192 tata)-"
},
{
"from_node": "fiber (tata \u2192 toto)-",
"to_node": "roadm toto"
},
{
"from_node": "roadm tata",
"to_node": "fiber (tata \u2192 toto)-"
},
{
"from_node": "fiber (toto \u2192 tata)-",
"to_node": "roadm tata"
},
{
"from_node": "trx Lannion_CAS",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "roadm Lannion_CAS",
"to_node": "trx Lannion_CAS"
},
{
"from_node": "trx Lorient_KMA",
"to_node": "roadm Lorient_KMA"
},
{
"from_node": "roadm Lorient_KMA",
"to_node": "trx Lorient_KMA"
},
{
"from_node": "trx Vannes_KBE",
"to_node": "roadm Vannes_KBE"
},
{
"from_node": "roadm Vannes_KBE",
"to_node": "trx Vannes_KBE"
},
{
"from_node": "trx Rennes_STA",
"to_node": "roadm Rennes_STA"
},
{
"from_node": "roadm Rennes_STA",
"to_node": "trx Rennes_STA"
},
{
"from_node": "trx Brest_KLA",
"to_node": "roadm Brest_KLA"
},
{
"from_node": "roadm Brest_KLA",
"to_node": "trx Brest_KLA"
},
{
"from_node": "trx toto",
"to_node": "roadm toto"
},
{
"from_node": "roadm toto",
"to_node": "trx toto"
},
{
"from_node": "trx tata",
"to_node": "roadm tata"
},
{
"from_node": "roadm tata",
"to_node": "trx tata"
}
]
}

View File

@@ -1,219 +0,0 @@
{
"path-request": [
{
"request-id": "0",
"source": "Lorient_KMA",
"destination": "Vannes_KBE",
"src-tp-id": "trx Lorient_KMA",
"dst-tp-id": "trx Vannes_KBE",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
}
},
"optimizations": {
"explicit-route-include-objects": []
}
},
{
"request-id": "1",
"source": "Brest_KLA",
"destination": "Vannes_KBE",
"src-tp-id": "trx Brest_KLA",
"dst-tp-id": "trx Vannes_KBE",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
}
},
"optimizations": {
"explicit-route-include-objects": [
{
"index": 0,
"unnumbered-hop": {
"node-id": "Lannion_CAS",
"link-tp-id": "link-tp-id is not used",
"hop-type": "loose",
"direction": "direction is not used"
},
"label-hop": {
"te-label": {
"generic": "generic is not used",
"direction": "direction is not used"
}
}
},
{
"index": 1,
"unnumbered-hop": {
"node-id": "Lorient_KMA",
"link-tp-id": "link-tp-id is not used",
"hop-type": "loose",
"direction": "direction is not used"
},
"label-hop": {
"te-label": {
"generic": "generic is not used",
"direction": "direction is not used"
}
}
}
]
}
},
{
"request-id": "3",
"source": "Lannion_CAS",
"destination": "Rennes_STA",
"src-tp-id": "trx Lannion_CAS",
"dst-tp-id": "trx Rennes_STA",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
}
},
"optimizations": {
"explicit-route-include-objects": []
}
},
{
"request-id": "4",
"source": "Rennes_STA",
"destination": "Lannion_CAS",
"src-tp-id": "trx Rennes_STA",
"dst-tp-id": "trx Lannion_CAS",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_2",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": 64,
"output-power": 0.0019952623149688794
}
},
"optimizations": {
"explicit-route-include-objects": []
}
},
{
"request-id": "5",
"source": "Lorient_KMA",
"destination": "Lannion_CAS",
"src-tp-id": "trx Lorient_KMA",
"dst-tp-id": "trx Lannion_CAS",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
}
},
"optimizations": {
"explicit-route-include-objects": [
{
"index": 0,
"unnumbered-hop": {
"node-id": "toto",
"link-tp-id": "link-tp-id is not used",
"hop-type": "loose",
"direction": "direction is not used"
},
"label-hop": {
"te-label": {
"generic": "generic is not used",
"direction": "direction is not used"
}
}
}
]
}
}
],
"synchronisation": [
{
"synchonization-id": "0",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"request-id-number": [
"0",
"0"
]
}
},
{
"synchonization-id": "3",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"request-id-number": [
"3",
"4"
]
}
},
{
"synchonization-id": "5",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"request-id-number": [
"5",
"0"
]
}
}
]
}

View File

@@ -1,762 +0,0 @@
{
"elements": [
{
"uid": "trx Lannion_CAS",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 2.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "trx Lorient_KMA",
"metadata": {
"location": {
"city": "Lorient_KMA",
"region": "RLD",
"latitude": 2.0,
"longitude": 3.0
}
},
"type": "Transceiver"
},
{
"uid": "trx Vannes_KBE",
"metadata": {
"location": {
"city": "Vannes_KBE",
"region": "RLD",
"latitude": 2.0,
"longitude": 4.0
}
},
"type": "Transceiver"
},
{
"uid": "trx Rennes_STA",
"metadata": {
"location": {
"city": "Rennes_STA",
"region": "RLD",
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "trx Brest_KLA",
"metadata": {
"location": {
"city": "Brest_KLA",
"region": "RLD",
"latitude": 4.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "trx toto",
"metadata": {
"location": {
"city": "toto",
"region": "",
"latitude": 6.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "trx tata",
"metadata": {
"location": {
"city": "tata",
"region": "",
"latitude": 7.0,
"longitude": 0.0
}
},
"type": "Transceiver"
},
{
"uid": "roadm Lannion_CAS",
"metadata": {
"location": {
"city": "Lannion_CAS",
"region": "RLD",
"latitude": 2.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "roadm Lorient_KMA",
"metadata": {
"location": {
"city": "Lorient_KMA",
"region": "RLD",
"latitude": 2.0,
"longitude": 3.0
}
},
"type": "Roadm"
},
{
"uid": "roadm Vannes_KBE",
"metadata": {
"location": {
"city": "Vannes_KBE",
"region": "RLD",
"latitude": 2.0,
"longitude": 4.0
}
},
"type": "Roadm"
},
{
"uid": "roadm Rennes_STA",
"metadata": {
"location": {
"city": "Rennes_STA",
"region": "RLD",
"latitude": 0.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "roadm Brest_KLA",
"metadata": {
"location": {
"city": "Brest_KLA",
"region": "RLD",
"latitude": 4.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "roadm toto",
"metadata": {
"location": {
"city": "toto",
"region": "",
"latitude": 6.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "roadm tata",
"metadata": {
"location": {
"city": "tata",
"region": "",
"latitude": 7.0,
"longitude": 0.0
}
},
"type": "Roadm"
},
{
"uid": "ingress fused spans in Corlay",
"metadata": {
"location": {
"city": "Corlay",
"region": "RLD",
"latitude": 2.0,
"longitude": 1.0
}
},
"type": "Fused"
},
{
"uid": "ingress fused spans in Loudeac",
"metadata": {
"location": {
"city": "Loudeac",
"region": "RLD",
"latitude": 2.0,
"longitude": 2.0
}
},
"type": "Fused"
},
{
"uid": "ingress fused spans in Morlaix",
"metadata": {
"location": {
"city": "Morlaix",
"region": "RLD",
"latitude": 3.0,
"longitude": 0.0
}
},
"type": "Fused"
},
{
"uid": "egress fused spans in Corlay",
"metadata": {
"location": {
"city": "Corlay",
"region": "RLD",
"latitude": 2.0,
"longitude": 1.0
}
},
"type": "Fused"
},
{
"uid": "egress fused spans in Loudeac",
"metadata": {
"location": {
"city": "Loudeac",
"region": "RLD",
"latitude": 2.0,
"longitude": 2.0
}
},
"type": "Fused"
},
{
"uid": "egress fused spans in Morlaix",
"metadata": {
"location": {
"city": "Morlaix",
"region": "RLD",
"latitude": 3.0,
"longitude": 0.0
}
},
"type": "Fused"
},
{
"uid": "fiber (Lannion_CAS \u2192 Corlay)-F061",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 0.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 20.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Corlay \u2192 Loudeac)-F010",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 1.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 50.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 2.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 3.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 10.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
"metadata": {
"location": {
"latitude": 1.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 65.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
"metadata": {
"location": {
"latitude": 2.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 40.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Morlaix \u2192 Brest_KLA)-F060",
"metadata": {
"location": {
"latitude": 3.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 35.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (toto \u2192 tata)-",
"metadata": {
"location": {
"latitude": 6.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 80.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Corlay \u2192 Lannion_CAS)-F061",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 0.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 20.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Loudeac \u2192 Corlay)-F010",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 1.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 50.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 2.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 3.5
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 10.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
"metadata": {
"location": {
"latitude": 1.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 60.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
"metadata": {
"location": {
"latitude": 0.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 65.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
"metadata": {
"location": {
"latitude": 2.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 40.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (Brest_KLA \u2192 Morlaix)-F060",
"metadata": {
"location": {
"latitude": 3.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 35.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
},
{
"uid": "fiber (tata \u2192 toto)-",
"metadata": {
"location": {
"latitude": 6.5,
"longitude": 0.0
}
},
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"length": 80.0,
"length_units": "km",
"loss_coef": 0.2,
"con_in": null,
"con_out": null
}
}
],
"connections": [
{
"from_node": "roadm Lannion_CAS",
"to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061"
},
{
"from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "roadm Lannion_CAS",
"to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056"
},
{
"from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "roadm Lannion_CAS",
"to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059"
},
{
"from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061",
"to_node": "ingress fused spans in Corlay"
},
{
"from_node": "ingress fused spans in Corlay",
"to_node": "fiber (Corlay \u2192 Loudeac)-F010"
},
{
"from_node": "fiber (Loudeac \u2192 Corlay)-F010",
"to_node": "egress fused spans in Corlay"
},
{
"from_node": "egress fused spans in Corlay",
"to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061"
},
{
"from_node": "fiber (Corlay \u2192 Loudeac)-F010",
"to_node": "ingress fused spans in Loudeac"
},
{
"from_node": "ingress fused spans in Loudeac",
"to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054"
},
{
"from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054",
"to_node": "egress fused spans in Loudeac"
},
{
"from_node": "egress fused spans in Loudeac",
"to_node": "fiber (Loudeac \u2192 Corlay)-F010"
},
{
"from_node": "roadm Lorient_KMA",
"to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054"
},
{
"from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054",
"to_node": "roadm Lorient_KMA"
},
{
"from_node": "roadm Lorient_KMA",
"to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055"
},
{
"from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055",
"to_node": "roadm Lorient_KMA"
},
{
"from_node": "roadm Vannes_KBE",
"to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055"
},
{
"from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055",
"to_node": "roadm Vannes_KBE"
},
{
"from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056",
"to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057"
},
{
"from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057",
"to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056"
},
{
"from_node": "roadm Rennes_STA",
"to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057"
},
{
"from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057",
"to_node": "roadm Rennes_STA"
},
{
"from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059",
"to_node": "ingress fused spans in Morlaix"
},
{
"from_node": "ingress fused spans in Morlaix",
"to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060"
},
{
"from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060",
"to_node": "egress fused spans in Morlaix"
},
{
"from_node": "egress fused spans in Morlaix",
"to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059"
},
{
"from_node": "roadm Brest_KLA",
"to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060"
},
{
"from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060",
"to_node": "roadm Brest_KLA"
},
{
"from_node": "roadm toto",
"to_node": "fiber (toto \u2192 tata)-"
},
{
"from_node": "fiber (tata \u2192 toto)-",
"to_node": "roadm toto"
},
{
"from_node": "roadm tata",
"to_node": "fiber (tata \u2192 toto)-"
},
{
"from_node": "fiber (toto \u2192 tata)-",
"to_node": "roadm tata"
},
{
"from_node": "trx Lannion_CAS",
"to_node": "roadm Lannion_CAS"
},
{
"from_node": "roadm Lannion_CAS",
"to_node": "trx Lannion_CAS"
},
{
"from_node": "trx Lorient_KMA",
"to_node": "roadm Lorient_KMA"
},
{
"from_node": "roadm Lorient_KMA",
"to_node": "trx Lorient_KMA"
},
{
"from_node": "trx Vannes_KBE",
"to_node": "roadm Vannes_KBE"
},
{
"from_node": "roadm Vannes_KBE",
"to_node": "trx Vannes_KBE"
},
{
"from_node": "trx Rennes_STA",
"to_node": "roadm Rennes_STA"
},
{
"from_node": "roadm Rennes_STA",
"to_node": "trx Rennes_STA"
},
{
"from_node": "trx Brest_KLA",
"to_node": "roadm Brest_KLA"
},
{
"from_node": "roadm Brest_KLA",
"to_node": "trx Brest_KLA"
},
{
"from_node": "trx toto",
"to_node": "roadm toto"
},
{
"from_node": "roadm toto",
"to_node": "trx toto"
},
{
"from_node": "trx tata",
"to_node": "roadm tata"
},
{
"from_node": "roadm tata",
"to_node": "trx tata"
}
]
}

View File

@@ -1,219 +0,0 @@
{
"path-request": [
{
"request-id": "0",
"source": "Lorient_KMA",
"destination": "Vannes_KBE",
"src-tp-id": "trx Lorient_KMA",
"dst-tp-id": "trx Vannes_KBE",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
}
},
"optimizations": {
"explicit-route-include-objects": []
}
},
{
"request-id": "1",
"source": "Brest_KLA",
"destination": "Vannes_KBE",
"src-tp-id": "trx Brest_KLA",
"dst-tp-id": "trx Vannes_KBE",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
}
},
"optimizations": {
"explicit-route-include-objects": [
{
"index": 0,
"unnumbered-hop": {
"node-id": "Lannion_CAS",
"link-tp-id": "link-tp-id is not used",
"hop-type": "loose",
"direction": "direction is not used"
},
"label-hop": {
"te-label": {
"generic": "generic is not used",
"direction": "direction is not used"
}
}
},
{
"index": 1,
"unnumbered-hop": {
"node-id": "Lorient_KMA",
"link-tp-id": "link-tp-id is not used",
"hop-type": "loose",
"direction": "direction is not used"
},
"label-hop": {
"te-label": {
"generic": "generic is not used",
"direction": "direction is not used"
}
}
}
]
}
},
{
"request-id": "3",
"source": "Lannion_CAS",
"destination": "Rennes_STA",
"src-tp-id": "trx Lannion_CAS",
"dst-tp-id": "trx Rennes_STA",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
}
},
"optimizations": {
"explicit-route-include-objects": []
}
},
{
"request-id": "4",
"source": "Rennes_STA",
"destination": "Lannion_CAS",
"src-tp-id": "trx Rennes_STA",
"dst-tp-id": "trx Lannion_CAS",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_2",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": 64,
"output-power": 0.0019952623149688794
}
},
"optimizations": {
"explicit-route-include-objects": []
}
},
{
"request-id": "5",
"source": "Lorient_KMA",
"destination": "Lannion_CAS",
"src-tp-id": "trx Lorient_KMA",
"dst-tp-id": "trx Lannion_CAS",
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673
}
},
"optimizations": {
"explicit-route-include-objects": [
{
"index": 0,
"unnumbered-hop": {
"node-id": "toto",
"link-tp-id": "link-tp-id is not used",
"hop-type": "loose",
"direction": "direction is not used"
},
"label-hop": {
"te-label": {
"generic": "generic is not used",
"direction": "direction is not used"
}
}
}
]
}
}
],
"synchronisation": [
{
"synchonization-id": "0",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"request-id-number": [
"0",
"0"
]
}
},
{
"synchonization-id": "3",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"request-id-number": [
"3",
"4"
]
}
},
{
"synchonization-id": "5",
"svec": {
"relaxable": "False",
"link-diverse": "True",
"node-diverse": "True",
"request-id-number": [
"5",
"0"
]
}
}
]
}

View File

@@ -0,0 +1,223 @@
{
"uid": "Span1",
"params": {
"length": 80,
"loss_coef": 0.2,
"length_units": "km",
"att_in": 0,
"con_in": 0.5,
"con_out": 0.5,
"type_variety": "SSMF",
"dispersion": 0.0000167,
"gamma": 0.00127,
"raman_efficiency": {
"cr": [
0,
0.0000094,
0.0000292,
0.0000488,
0.0000682,
0.0000831,
0.000094,
0.0001014,
0.0001069,
0.0001119,
0.0001217,
0.0001268,
0.0001365,
0.000149,
0.000165,
0.000181,
0.0001977,
0.0002192,
0.0002469,
0.0002749,
0.0002999,
0.0003206,
0.0003405,
0.0003592,
0.000374,
0.0003826,
0.0003841,
0.0003826,
0.0003802,
0.0003756,
0.0003549,
0.0003795,
0.000344,
0.0002933,
0.0002024,
0.0001158,
0.0000846,
0.0000714,
0.0000686,
0.000085,
0.0000893,
0.0000901,
0.0000815,
0.0000667,
0.0000437,
0.0000328,
0.0000296,
0.0000265,
0.0000257,
0.0000281,
0.0000308,
0.0000367,
0.0000585,
0.0000663,
0.0000636,
0.000055,
0.0000406,
0.0000277,
0.0000242,
0.0000187,
0.000016,
0.000014,
0.0000113,
0.0000105,
0.0000098,
0.0000098,
0.0000113,
0.0000164,
0.0000195,
0.0000238,
0.0000226,
0.0000203,
0.0000148,
0.0000109,
0.0000098,
0.0000105,
0.0000117,
0.0000125,
0.0000121,
0.0000109,
0.0000098,
0.0000082,
0.0000066,
0.0000047,
0.0000027,
0.0000019,
0.0000012,
4e-7,
2e-7,
1e-7
],
"frequency_offset": [
0,
500000000000,
1000000000000,
1500000000000,
2000000000000,
2500000000000,
3000000000000,
3500000000000,
4000000000000,
4500000000000,
5000000000000,
5500000000000,
6000000000000,
6500000000000,
7000000000000,
7500000000000,
8000000000000,
8500000000000,
9000000000000,
9500000000000,
10000000000000,
10500000000000,
11000000000000,
11500000000000,
12000000000000,
12500000000000,
12750000000000,
13000000000000,
13250000000000,
13500000000000,
14000000000000,
14500000000000,
14750000000000,
15000000000000,
15500000000000,
16000000000000,
16500000000000,
17000000000000,
17500000000000,
18000000000000,
18250000000000,
18500000000000,
18750000000000,
19000000000000,
19500000000000,
20000000000000,
20500000000000,
21000000000000,
21500000000000,
22000000000000,
22500000000000,
23000000000000,
23500000000000,
24000000000000,
24500000000000,
25000000000000,
25500000000000,
26000000000000,
26500000000000,
27000000000000,
27500000000000,
28000000000000,
28500000000000,
29000000000000,
29500000000000,
30000000000000,
30500000000000,
31000000000000,
31500000000000,
32000000000000,
32500000000000,
33000000000000,
33500000000000,
34000000000000,
34500000000000,
35000000000000,
35500000000000,
36000000000000,
36500000000000,
37000000000000,
37500000000000,
38000000000000,
38500000000000,
39000000000000,
39500000000000,
40000000000000,
40500000000000,
41000000000000,
41500000000000,
42000000000000
]
}
},
"operational": {
"temperature": 283,
"raman_pumps": [
{
"power": 0.2,
"frequency": 205000000000000,
"propagation_direction": "counterprop"
},
{
"power": 0.206,
"frequency": 201000000000000,
"propagation_direction": "counterprop"
}
]
},
"metadata": {
"location": {
"latitude": 1,
"longitude": 0,
"city": null,
"region": ""
}
}
}

View File

@@ -0,0 +1,14 @@
{
"raman_computed_channels": [1, 18, 37, 56, 75],
"raman_parameters": {
"flag_raman": true,
"space_resolution": 10e3,
"tolerance": 1e-8
},
"nli_parameters": {
"nli_method_name": "ggn_spectrally_separated",
"wdm_grid_size": 50e9,
"dispersion_tolerance": 1,
"phase_shift_tollerance": 0.1
}
}

BIN
tests/data/testService.xls Normal file

Binary file not shown.

View File

@@ -0,0 +1,79 @@
{
"path-request": [
{
"request-id": "0",
"source": "trx Lorient_KMA",
"destination": "trx Vannes_KBE",
"src-tp-id": "trx Lorient_KMA",
"dst-tp-id": "trx Vannes_KBE",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": null,
"path_bandwidth": 100000000000.0
}
}
},
{
"request-id": "1",
"source": "trx Brest_KLA",
"destination": "trx Vannes_KBE",
"src-tp-id": "trx Brest_KLA",
"dst-tp-id": "trx Vannes_KBE",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": 0.0012589254117941673,
"path_bandwidth": 10000000000.0
}
}
},
{
"request-id": "3",
"source": "trx Lannion_CAS",
"destination": "trx Rennes_STA",
"src-tp-id": "trx Lannion_CAS",
"dst-tp-id": "trx Rennes_STA",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673,
"path_bandwidth": 60000000000.0
}
}
}
]
}

BIN
tests/data/testTopology.xls Normal file

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,OSNR-0.1nm,SNR-0.1nm,SNR-bandwidth,baud rate (Gbaud),input power (dBm),path,"spectrum (N,M)",reversed path OSNR-0.1nm,reversed path SNR-0.1nm,reversed path SNR-bandwidth
0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,30.84,30.84,26.75,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE,"-284, 4"
1,trx Brest_KLA,trx Vannes_KBE,10.0,True,1,1,Voyager,mode 1,22.65,22.11,18.03,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | Edfa0_roadm Brest_KLA | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | Edfa0_fiber (Loudeac → Lorient_KMA)-F054 | roadm Lorient_KMA | Edfa1_roadm Lorient_KMA | fiber (Lorient_KMA → Vannes_KBE)-F055 | Edfa0_fiber (Lorient_KMA → Vannes_KBE)-F055 | roadm Vannes_KBE | trx Vannes_KBE,"-276, 4"
3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,28.29,25.85,21.77,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | Edfa0_fiber (Stbrieuc → Rennes_STA)-F057 | roadm Rennes_STA | trx Rennes_STA,"-284, 4"
4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,22.27,22.15,15.05,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | Edfa1_roadm Rennes_STA | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | Edfa0_fiber (Ploermel → Vannes_KBE)- | roadm Vannes_KBE | Edfa0_roadm Vannes_KBE | fiber (Vannes_KBE → Lorient_KMA)-F055 | Edfa0_fiber (Vannes_KBE → Lorient_KMA)-F055 | roadm Lorient_KMA | Edfa0_roadm Lorient_KMA | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS,"-266, 6"
5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,30.79,28.77,21.68,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | Edfa0_roadm Rennes_STA | fiber (Rennes_STA → Stbrieuc)-F057 | Edfa0_fiber (Rennes_STA → Stbrieuc)-F057 | fiber (Stbrieuc → Lannion_CAS)-F056 | Edfa0_fiber (Stbrieuc → Lannion_CAS)-F056 | roadm Lannion_CAS | trx Lannion_CAS,"-274, 6"
6,,,,NO_PATH,,,,,,,,,,,
Can't render this file because it has a wrong number of fields in line 2.

View File

@@ -0,0 +1,218 @@
{
"path-request": [
{
"request-id": "0",
"source": "trx Lorient_KMA",
"destination": "trx Vannes_KBE",
"src-tp-id": "trx Lorient_KMA",
"dst-tp-id": "trx Vannes_KBE",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": null,
"path_bandwidth": 100000000000.0
}
}
},
{
"request-id": "1",
"source": "trx Brest_KLA",
"destination": "trx Vannes_KBE",
"src-tp-id": "trx Brest_KLA",
"dst-tp-id": "trx Vannes_KBE",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": 0.0012589254117941673,
"path_bandwidth": 10000000000.0
}
},
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": "route-include-ero",
"index": 0,
"num-unnum-hop": {
"node-id": "roadm Brest_KLA",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
},
{
"explicit-route-usage": "route-include-ero",
"index": 1,
"num-unnum-hop": {
"node-id": "roadm Lannion_CAS",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
},
{
"explicit-route-usage": "route-include-ero",
"index": 2,
"num-unnum-hop": {
"node-id": "roadm Lorient_KMA",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
},
{
"explicit-route-usage": "route-include-ero",
"index": 3,
"num-unnum-hop": {
"node-id": "roadm Vannes_KBE",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
}
]
}
},
{
"request-id": "3",
"source": "trx Lannion_CAS",
"destination": "trx Rennes_STA",
"src-tp-id": "trx Lannion_CAS",
"dst-tp-id": "trx Rennes_STA",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.0012589254117941673,
"path_bandwidth": 60000000000.0
}
}
},
{
"request-id": "4",
"source": "trx Rennes_STA",
"destination": "trx Lannion_CAS",
"src-tp-id": "trx Rennes_STA",
"dst-tp-id": "trx Lannion_CAS",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "mode 2",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 150000000000.0
}
}
},
{
"request-id": "5",
"source": "trx Rennes_STA",
"destination": "trx Lannion_CAS",
"src-tp-id": "trx Rennes_STA",
"dst-tp-id": "trx Lannion_CAS",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "mode 2",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": 63,
"output-power": 0.0019952623149688794,
"path_bandwidth": 20000000000.0
}
}
},
{
"request-id": "6",
"source": "trx Lannion_CAS",
"destination": "trx a",
"src-tp-id": "trx Lannion_CAS",
"dst-tp-id": "trx a",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "mode 2",
"effective-freq-slot": [
{
"N": "null",
"M": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 100000000000.0
}
}
}
],
"synchronization": [
{
"synchronization-id": "3",
"svec": {
"relaxable": "false",
"disjointness": "node link",
"request-id-number": [
"3",
"1"
]
}
},
{
"synchronization-id": "4",
"svec": {
"relaxable": "false",
"disjointness": "node link",
"request-id-number": [
"4",
"5"
]
}
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,614 @@
{
"path-request": [
{
"request-id": "1",
"source": "trx a",
"destination": "trx g",
"src-tp-id": "trx a",
"dst-tp-id": "trx g",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "2a",
"source": "trx a",
"destination": "trx h",
"src-tp-id": "trx a",
"dst-tp-id": "trx h",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "3",
"source": "trx f",
"destination": "trx b",
"src-tp-id": "trx f",
"dst-tp-id": "trx b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "ee",
"source": "trx c",
"destination": "trx f",
"src-tp-id": "trx c",
"dst-tp-id": "trx f",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
},
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": "route-include-ero",
"index": 0,
"num-unnum-hop": {
"node-id": "roadm e",
"hop-type": "LOOSE"
}
},
{
"explicit-route-usage": "route-include-ero",
"index": 1,
"num-unnum-hop": {
"node-id": "roadm g",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
}
]
}
},
{
"request-id": "ff",
"source": "trx c",
"destination": "trx f",
"src-tp-id": "trx c",
"dst-tp-id": "trx f",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 2 - fake",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": 63,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "10",
"source": "trx a",
"destination": "trx g",
"src-tp-id": "trx a",
"dst-tp-id": "trx g",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 2",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": 63,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "11",
"source": "trx a",
"destination": "trx h",
"src-tp-id": "trx a",
"dst-tp-id": "trx h",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
},
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": "route-include-ero",
"index": 0,
"num-unnum-hop": {
"node-id": "bb",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
}
]
}
},
{
"request-id": "12",
"source": "trx f",
"destination": "trx b",
"src-tp-id": "trx f",
"dst-tp-id": "trx b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 3",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 62500000000.0,
"max-nb-of-channel": 76,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
},
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": "route-include-ero",
"index": 0,
"num-unnum-hop": {
"node-id": "trx b",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
}
]
}
},
{
"request-id": "13",
"source": "trx c",
"destination": "trx f",
"src-tp-id": "trx c",
"dst-tp-id": "trx f",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "14",
"source": "trx c",
"destination": "trx f",
"src-tp-id": "trx c",
"dst-tp-id": "trx f",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
},
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": "route-include-ero",
"index": 0,
"num-unnum-hop": {
"node-id": "roadm e",
"hop-type": "LOOSE"
}
},
{
"explicit-route-usage": "route-include-ero",
"index": 1,
"num-unnum-hop": {
"node-id": "roadm g",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
}
]
}
},
{
"request-id": "e:1# /",
"source": "trx a",
"destination": "trx g",
"src-tp-id": "trx a",
"dst-tp-id": "trx g",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager_16QAM",
"trx_mode": "16QAM",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": null,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "b-2a",
"source": "trx a",
"destination": "trx h",
"src-tp-id": "trx a",
"dst-tp-id": "trx h",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": "mode 1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "3a;?",
"source": "trx f",
"destination": "trx b",
"src-tp-id": "trx f",
"dst-tp-id": "trx b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": "PS_SP64_1",
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "ee-s",
"source": "trx c",
"destination": "trx f",
"src-tp-id": "trx c",
"dst-tp-id": "trx f",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "vendorA_trx-type1",
"trx_mode": null,
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": 80,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
},
"explicit-route-objects": {
"route-object-include-exclude": [
{
"explicit-route-usage": "route-include-ero",
"index": 0,
"num-unnum-hop": {
"node-id": "roadm e",
"hop-type": "LOOSE"
}
},
{
"explicit-route-usage": "route-include-ero",
"index": 1,
"num-unnum-hop": {
"node-id": "roadm g",
"link-tp-id": "link-tp-id is not used",
"hop-type": "LOOSE"
}
}
]
}
},
{
"request-id": "ff-b",
"source": "trx c",
"destination": "trx f",
"src-tp-id": "trx c",
"dst-tp-id": "trx f",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": null,
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": 0.001,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "10-z",
"source": "trx a",
"destination": "trx g",
"src-tp-id": "trx a",
"dst-tp-id": "trx g",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": null,
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": 63,
"output-power": null,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "11 g",
"source": "trx a",
"destination": "trx h",
"src-tp-id": "trx a",
"dst-tp-id": "trx h",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": null,
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 50000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": 300000000000.0
}
}
},
{
"request-id": "12<",
"source": "trx f",
"destination": "trx b",
"src-tp-id": "trx f",
"dst-tp-id": "trx b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": null,
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 75000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": null
}
}
},
{
"request-id": "12>",
"source": "trx f",
"destination": "trx b",
"src-tp-id": "trx f",
"dst-tp-id": "trx b",
"bidirectional": false,
"path-constraints": {
"te-bandwidth": {
"technology": "flexi-grid",
"trx_type": "Voyager",
"trx_mode": null,
"effective-freq-slot": [
{
"n": "null",
"m": "null"
}
],
"spacing": 30000000000.0,
"max-nb-of-channel": null,
"output-power": null,
"path_bandwidth": null
}
}
}
],
"synchronization": [
{
"synchronization-id": "1",
"svec": {
"relaxable": "False",
"disjointness": "node link",
"request-id-number": [
"1",
"2a"
]
}
},
{
"synchronization-id": "3",
"svec": {
"relaxable": "False",
"disjointness": "node link",
"request-id-number": [
"3",
"1"
]
}
},
{
"synchronization-id": "ff",
"svec": {
"relaxable": "False",
"disjointness": "node link",
"request-id-number": [
"ff",
"13"
]
}
},
{
"synchronization-id": "13",
"svec": {
"relaxable": "False",
"disjointness": "node link",
"request-id-number": [
"13",
"14"
]
}
}
]
}

View File

@@ -77,7 +77,22 @@
"longitude": 0
}
}
},
},
{
"uid": "Att_B",
"type": "Fused",
"params":{
"loss":16
},
"metadata": {
"location": {
"latitude": 2.0,
"longitude": 1.0,
"city": "Corlay",
"region": "RLD"
}
}
},
{
"uid": "Site_B",
"type": "Transceiver",
@@ -110,6 +125,10 @@
},
{
"from_node": "Edfa2",
"to_node": "Att_B"
},
{
"from_node": "Att_B",
"to_node": "Site_B"
}

View File

@@ -5,12 +5,12 @@
from gnpy.core.elements import Edfa
from numpy import zeros, array
from json import load, dumps
from json import load
from gnpy.core.elements import Transceiver, Fiber, Edfa
from gnpy.core.utils import lin2db, db2lin
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
from gnpy.core.equipment import load_equipment
from gnpy.core.network import build_network, load_network, set_roadm_loss
from gnpy.core.equipment import load_equipment, automatic_fmax, automatic_nch
from gnpy.core.network import build_network, load_network
from pathlib import Path
import pytest
@@ -66,7 +66,9 @@ def setup_trx():
def si(nch_and_spacing, bw):
"""parametrize a channel comb with nb_channel, spacing and signal bw"""
nb_channel, spacing = nch_and_spacing
return create_input_spectral_information(191.3e12, 0.15, bw, 1e-3, spacing, nb_channel)
f_min = 191.3e12
f_max = automatic_fmax(f_min, spacing, nb_channel)
return create_input_spectral_information(f_min, f_max, 0.15, bw, 1e-3, spacing)
@pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)])
def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si):
@@ -77,7 +79,7 @@ def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si):
pin = pin/db2lin(gain)
baud_rates = array([c.baud_rate for c in si.carriers])
edfa.operational.gain_target = gain
pref = Pref(0, -gain)
pref = Pref(0, -gain, lin2db(len(frequencies)))
edfa.interpol_params(frequencies, pin, baud_rates, pref)
result = edfa.nf
assert pytest.approx(nf_expected, abs=0.01) == result[0]
@@ -91,7 +93,7 @@ def test_fixed_gain_nf(gain, nf_expected, setup_edfa_fixed_gain, si):
pin = pin/db2lin(gain)
baud_rates = array([c.baud_rate for c in si.carriers])
edfa.operational.gain_target = gain
pref = Pref(0, -gain)
pref = Pref(0, -gain, lin2db(len(frequencies)))
edfa.interpol_params(frequencies, pin, baud_rates, pref)
assert pytest.approx(nf_expected, abs=0.01) == edfa.nf[0]
@@ -116,7 +118,7 @@ def test_compare_nf_models(gain, setup_edfa_variable_gain, si):
pin = pin/db2lin(gain)
baud_rates = array([c.baud_rate for c in si.carriers])
edfa.operational.gain_target = gain
pref = Pref(0, -gain)
pref = Pref(0, -gain, lin2db(len(frequencies)))
edfa.interpol_params(frequencies, pin, baud_rates, pref)
nf_model = edfa.nf[0]
edfa.interpol_params(frequencies, pin, baud_rates, pref)
@@ -135,7 +137,7 @@ def test_ase_noise(gain, si, setup_edfa_variable_gain, setup_trx, bw):
pin = array([c.power.signal+c.power.nli+c.power.ase for c in si.carriers])
baud_rates = array([c.baud_rate for c in si.carriers])
edfa.operational.gain_target = gain
pref = Pref(0, 0)
pref = Pref(0, 0, lin2db(len(frequencies)))
edfa.interpol_params(frequencies, pin, baud_rates, pref)
nf = edfa.nf
pin = lin2db(pin[0]*1e3)

View File

@@ -0,0 +1,86 @@
#!/usr/bin/env python3
# TelecomInfraProject/gnpy/examples
# Module name : test_automaticmodefeature.py
# Version :
# License : BSD 3-Clause Licence
# Copyright (c) 2018, Telecom Infra Project
"""
@author: esther.lerouzic
checks that empty info on mode, power, nbchannel in service file are supported
uses combination of [mode, pow, nb channel] filled or empty defined in meshTopologyToy_services.json
leading to feasible path or not, and check the propagate and propagate_and_optimize_mode
return the expected based on a reference toy example
"""
from pathlib import Path
import pytest
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch
from gnpy.core.network import load_network, build_network
from examples.path_requests_run import requests_from_json, correct_route_list, load_requests
from gnpy.core.request import compute_path_dsjctn, propagate, propagate_and_optimize_mode
from gnpy.core.utils import db2lin, lin2db
from gnpy.core.elements import Roadm
network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json'
service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json'
result_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testresults.json'
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
@pytest.mark.parametrize("net",[network_file_name])
@pytest.mark.parametrize("eqpt", [eqpt_library_name])
@pytest.mark.parametrize("serv",[service_file_name])
@pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 2', 'mode 1', 'mode 2', 'nok']])
def test_automaticmodefeature(net,eqpt,serv,expected_mode):
data = load_requests(serv, eqpt, bidir=False)
equipment = load_equipment(eqpt)
network = load_network(net,equipment)
# Build the network once using the default power defined in SI in eqpt config
# power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
# spacing, f_min and f_max
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)
rqs = requests_from_json(data, equipment)
rqs = correct_route_list(network, rqs)
dsjn = []
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
path_res_list = []
for i,pathreq in enumerate(rqs):
# use the power specified in requests but might be different from the one specified for design
# the power is an optional parameter for requests definition
# if optional, use the one defines in eqt_config.json
p_db = lin2db(pathreq.power*1e3)
p_total_db = p_db + lin2db(pathreq.nb_channel)
print(f'request {pathreq.request_id}')
print(f'Computing path from {pathreq.source} to {pathreq.destination}')
print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output
total_path = pths[i]
print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}\n')
# for debug
# print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}')
if pathreq.baud_rate is not None:
print(pathreq.format)
path_res_list.append(pathreq.format)
total_path = propagate(total_path,pathreq,equipment)
else:
total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment)
# if no baudrate satisfies spacing, no mode is returned and an empty path is returned
# a warning is shown in the propagate_and_optimize_mode
if mode is not None :
print (mode['format'])
path_res_list.append(mode['format'])
else :
print('nok')
path_res_list.append('nok')
print(path_res_list)
assert path_res_list==expected_mode

109
tests/test_disjunction.py Normal file
View File

@@ -0,0 +1,109 @@
#!/usr/bin/env python3
# TelecomInfraProject/gnpy/examples
# Module name : test_disjunction.py
# Version :
# License : BSD 3-Clause Licence
# Copyright (c) 2018, Telecom Infra Project
"""
@author: esther.lerouzic
checks that computed paths are disjoint as specified in the json service file
"""
from pathlib import Path
import pytest
from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch
from gnpy.core.network import load_network, build_network
from examples.path_requests_run import (requests_from_json , correct_route_list ,
load_requests , disjunctions_from_json)
from gnpy.core.request import compute_path_dsjctn, isdisjoint , find_reversed_path
from gnpy.core.utils import db2lin, lin2db
from gnpy.core.elements import Roadm
from gnpy.core.spectrum_assignment import build_oms_list
network_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_expected.json'
service_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testservices.json'
result_file_name = Path(__file__).parent.parent / 'tests/data/testTopology_testresults.json'
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
@pytest.mark.parametrize("net",[network_file_name])
@pytest.mark.parametrize("eqpt", [eqpt_library_name])
@pytest.mark.parametrize("serv",[service_file_name])
def test_disjunction(net,eqpt,serv):
data = load_requests(serv, eqpt, bidir=False)
equipment = load_equipment(eqpt)
network = load_network(net,equipment)
# Build the network once using the default power defined in SI in eqpt config
# power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
# spacing, f_min and f_max
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)
build_oms_list(network, equipment)
rqs = requests_from_json(data, equipment)
rqs = correct_route_list(network, rqs)
dsjn = disjunctions_from_json(data)
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
print(dsjn)
dsjn_list = [d.disjunctions_req for d in dsjn ]
# assumes only pairs in dsjn list
test = True
for e in dsjn_list:
rqs_id_list = [r.request_id for r in rqs]
p1 = pths[rqs_id_list.index(e[0])][1:-1]
p2 = pths[rqs_id_list.index(e[1])][1:-1]
if isdisjoint(p1, p2) + isdisjoint(p1, find_reversed_path(p2)) > 0:
test = False
print(f'Computed path (roadms):{[e.uid for e in p1 if isinstance(e, Roadm)]}\n')
print(f'Computed path (roadms):{[e.uid for e in p2 if isinstance(e, Roadm)]}\n')
break
print(dsjn_list)
assert test
@pytest.mark.parametrize("net",[network_file_name])
@pytest.mark.parametrize("eqpt", [eqpt_library_name])
@pytest.mark.parametrize("serv",[service_file_name])
def test_does_not_loop_back(net,eqpt,serv):
data = load_requests(serv, eqpt, bidir=False)
equipment = load_equipment(eqpt)
network = load_network(net,equipment)
# Build the network once using the default power defined in SI in eqpt config
# power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
# spacing, f_min and f_max
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)
build_oms_list(network, equipment)
rqs = requests_from_json(data, equipment)
rqs = correct_route_list(network, rqs)
dsjn = disjunctions_from_json(data)
pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
# check that computed paths do not loop back ie each element appears only once
test = True
for p in pths :
for el in p:
p.remove(el)
a = [e for e in p if e.uid == el.uid]
if a :
test = False
break
assert test
# TODO : test that identical requests are correctly agregated
# and reproduce disjunction vector as well as route constraints
# check that requests with different parameters are not aggregated
# check that the total agregated bandwidth is the same after aggregation
#

Some files were not shown because too many files have changed in this diff Show More