001package com.net2plan.examples.general.reports;
002
003import java.text.DecimalFormat;
004import java.util.Arrays;
005import java.util.HashMap;
006import java.util.Iterator;
007import java.util.LinkedHashMap;
008import java.util.LinkedList;
009import java.util.List;
010import java.util.Map;
011
012import org.apache.commons.collections15.ListUtils;
013import org.apache.commons.lang.ArrayUtils;
014
015import com.net2plan.interfaces.networkDesign.IReport;
016import com.net2plan.interfaces.networkDesign.Link;
017import com.net2plan.interfaces.networkDesign.Net2PlanException;
018import com.net2plan.interfaces.networkDesign.NetPlan;
019import com.net2plan.interfaces.networkDesign.Route;
020import com.net2plan.libraries.OpticalImpairmentUtils;
021import com.net2plan.utils.Constants.OrderingType;
022import com.net2plan.utils.DoubleUtils;
023import com.net2plan.utils.InputParameter;
024import com.net2plan.utils.Quadruple;
025import com.net2plan.utils.StringUtils;
026import com.net2plan.utils.Triple;
027
028/**
029 * <p>
030 * This report shows line engineering information for WDM links in a multilayer optical network. The impairment calculations are based on the Gaussian Noise Model developed by Politecnico di Torino and inspired in the procedures described in the 2009
031 * ITU-T WDM manual "Optical fibres, cabbles and systems".
032 * </p>
033 * <p>
034 * The report assumes that the WDM network follows the scheme:
035 * </p>
036 * <ul>
037 * <li>In the net2plan object, nodes are OADMs, links are fiber links, and routes are lightpaths: WDM channels optically switched at intermediate nodes.</li>
038 * <li>Nodes are connected by unidirectional fiber links. Fiber link distance is given by the link length. Other specifications are given by fibers_XXX input parameters, each one describing the parameter for the fiber types specified in fibers_types,
039 * in the same order and separated by spaces. The fiber can be split into spans if optical amplifers (EDFAs) and/or passive components (PCs) are placed along the fiber. These spans can be of different fiber types as long as they are described in a
040 * link attribute called "fiberTypes". Must be separated by spaces and, in case that there were more spans than elements of the attribute, the default type given in "fiber_default_type" would be used.</li>
041 * <li>Optical line amplifiers (EDFAs) can be located in none, one or more positions in the fiber link, separating them in different spans. EDFAs are supposed to operate in the automatic gain control mode. Thus, the gain is the same, whatever the
042 * number of input WDM channels. EDFA positions (as distance" in km from the link start to the EDFA location), EDFA gains (assumed in dB) and EDFA noise figures (in dB) are read from the "edfaPositions_km", "edfaGains_dB" and "edfaNoiseFigures_dB"
043 * attributes of the links. The format of all attributes are the same: a string of numbers separated by spaces. The <i>i</i>-th number corresponding to the position/gain of the <i>i</i>-th EDFA. If the attributes do not exist, it is assumed that no
044 * EDFAs are placed in this link. EDFA specifications are given by "edfa_XXX" parameters</li>
045 * <li>There are not Dispersion compensating modules (DCMs) in the topoology, since the Gaussian Noise Model is used.</li>
046 * <li>Passive components are described by the link attributes "pcPositions_km" and "pcLosses_dB". The <i>i</i>-th number corresponding to the position/loss of the <i>i</i>-th PC. If the attributes do not exist, it is assumed that no PCs are placed
047 * in this link. Other specifications for PC will be described in teh pc_XXX input parameters.</li>
048 * <li>Fiber links start and end in OADM modules, that permit adding, dropping and optically switch individual WDM channels. OADMs have a pre-amplifier (traversed by drop and express channels) and a boost amplifier (traversed by add and express
049 * channels). They are supposed to equalize the channel power at their outputs, to a fixed value (added and express channels will thus have the same power in the fibers). Also, OADMs attenuate appropriately the optical signal coming from the
050 * pre-amplifier, in the drop channels, so that they fall within the receiver sensitivity range. OADM noise figures for add, drop and express channels are given as input parameters. PMD values for add, drop and express channels are computed assumming
051 * that: (i) add channel traverse a multiplexer and the booster, (ii) drop channels travese the pre-amplifier and a demultiplexer, (iii) express channels traverse the two amplifiers. The required parameters are provided in oadm_XXX parameters.</li>
052 * <li>Each channel ends in a receiver, with specifications given by "tp_XXX" parameters.</li>
053 * </ul>
054 * <p>
055 * The basic checks performed are:
056 * </p>
057 * <ul>
058 * <li>For each link, signal power levels are within operating ranges at the oadm/edfas, both when the link has one single active channel, or when all the "gn_spec_nCh" are active</li>
059 * <li>For each route (lightpath), OSNR (Optical Signal to Noise Ration) is within the operating range at the receiver. A set of margins are considered to account to several not directly considered impairments.</li>
060 * <li>For each route (lightpath), PMD (Polarization mode dispersion) is within the operating range at the receiver</li>
061 * </ul>
062 * 
063 * @net2plan.keywords WDM, Multilayer
064 * @author Pablo Pavon-Marino, Elena Martin-Seoane
065 * @version 1.3, November 2017 */
066public class Report_WDM_lineEngineering_GNModel implements IReport
067{
068        /* Constants */
069        private final static double     delta_f                                 = 6.25E-3;      /* GHz of the slots in the grid */
070        private final static double     infinityThreshold_dB    = 300;          /* starting value to consider the OSNR perfect */
071
072        /* Input parameters */
073        private NetPlan                         netPlan;
074        private Map<String, String>     reportParameters;
075
076        /* GN General parameters */
077        private final InputParameter    gn_gen_f0_THz   = new InputParameter("gn_gen_f0_THz", (double) 192.075, "Starting frequency of the laser grid used to describe the WDM system [THz]");
078        private final InputParameter    gn_gen_ns               = new InputParameter("gn_gen_ns", (int) 800, "Number of 6.25 GHz slots in the grid");
079
080        /* Usable wavelengths */
081        private double  channels_minChannelLambda_nm;
082        private double  channels_maxChannelLambda_nm;
083
084        /* GN spectrum description */
085        private final InputParameter    gn_spec_nCh                             = new InputParameter("gn_spec_nCh", (int) 16, "Number of used channels defined in the spectrum.");
086        private final InputParameter    gn_spec_laserPosition   = new InputParameter("gn_spec_laserPosition", "false false true false false false", "A list of booleans indicating whether a laser is turned on or not (per each channel)");
087        private final InputParameter    gn_spec_bandwidthCh_THz = new InputParameter("gn_spec_bandwidthCh_THz", (double) 0.032, "The -3 dB WDM channel bandwidth (for a root raised cosine, it is equal to the symbol rate)");
088
089        /* Fiber specifications */
090        private final InputParameter    fiber_PMD_ps_per_sqroot_km                      = new InputParameter("fiber_PMD_ps_per_sqroot_km", (double) 0.4, "Polarization mode dispersion per km^0.5 of fiber (PMD_Q link factor)");
091        private final InputParameter    fiber_default_type                                      = new InputParameter("fiber_default_type", "SMF",
092                        "A string calling the type of fiber described (can be override by the 'fiberTypes' Net2Plan attribute). Must be a value from 'fibers_types'.");
093        /* GN Fiber parameters */
094        private final InputParameter    fibers_alpha_dB_per_km                          = new InputParameter("fibers_alpha_dB_per_km", "0.2 0.22", "The attenuation coefficient for each fiber type [dB/km]");
095        private final InputParameter    fibers_alpha1st_dB_per_km_per_THz       = new InputParameter("fibers_alpha1st_dB_per_km_per_THz", "0 0",
096                        "The first derivative of alpha indicating the alpha slope for each fiber type [dB/km/THz]. Should be zero if you assume a flat attenuation with respect to the frequency");
097        private final InputParameter    fibers_beta2_ps2_per_km                         = new InputParameter("fibers_beta2_ps2_per_km", "21.27 21", "The dispersion coefficient for each fiber type [ps^2/km]");
098        private final InputParameter    fibers_n2_m2_per_W                                      = new InputParameter("fibers_n2_m2_per_W", "2.5E-20 2.5E-20", "Second-order nonlinear refractive index for each fiber type [m^2/W]. A typical value is 2.5E-20 m^2/W");
099        private final InputParameter    fibers_Aeff_um2                                         = new InputParameter("fibers_Aeff_um2", "77.77 70", "The effective area for each fiber type [um^2]");
100        private final InputParameter    fibers_types                                            = new InputParameter("fibers_types", "SMF NZDF", "The names of the fiber types described in the other fibers_XXX parameters. They MUST BE ordered.");
101        private final InputParameter    fibers_numberOfFiberTypes                       = new InputParameter("fibers_numberOfFiberTypes", (int) 2, "The number of different fiber types described. Must be equal to the length of the others fibers_XXX parameters.");
102
103        /* Transponder specifications */
104        private final InputParameter    tp_minOSNR_dB                                   = new InputParameter("tp_minOSNR_dB", (double) 7, "Minimum OSNR needed at the receiver");
105        private final InputParameter    tp_minWavelength_nm                             = new InputParameter("tp_minWavelength_nm", (double) 1529.55, "Minimum wavelength usable by the transponder");
106        private final InputParameter    tp_maxWavelength_nm                             = new InputParameter("tp_maxWavelength_nm", (double) 1561.84, "Maximum wavelength usable by the transponder");
107        private final InputParameter    tp_pmdTolerance_ps                              = new InputParameter("tp_pmdTolerance_ps", (double) 10, "Maximum tolarance of polarizarion mode dispersion (mean of differential group delay) in ps at the receiver");
108        private final InputParameter    tp_inputPowerSensitivityMin_dBm = new InputParameter("tp_inputPowerSensitivityMin_dBm", (double) -20, "Minimum input power at the receiver in dBm");
109        private final InputParameter    tp_inputPowerSensitivityMax_dBm = new InputParameter("tp_inputPowerSensitivityMax_dBm", (double) -8, "Maximum input power at the receiver in dBm");
110
111        /* OADM specs */
112        private final InputParameter    oadm_outputPowerPerChannel_W            = new InputParameter("oadm_outputPowerPerChannel_W", (double) 1E-3, "The WDM channel power at the output of the OADM [W]");
113        private final InputParameter    oadm_perChannelMinInputPower_dBm        = new InputParameter("oadm_perChannelMinInputPower_dBm", (double) -19, "Minimum power needed at the OADM input");
114        private final InputParameter    oadm_perChannelMaxInputPower_dBm        = new InputParameter("oadm_perChannelMaxInputPower_dBm", (double) 1000, "Maximum power admitted at the OADM input");
115        private final InputParameter    oadm_muxDemuxPMD_ps                                     = new InputParameter("oadm_muxDemuxPMD_ps", (double) 0.5, "PMD of the mux/demux inside the OADMs. Does not affect express lightpaths");
116        private final InputParameter    oadm_preAmplifierPMD_ps                         = new InputParameter("oadm_preAmplifierPMD_ps", (double) 0.5, "PMD off OADM preamplifier");
117        private final InputParameter    oadm_boosterPMD_ps                                      = new InputParameter("oadm_boosterPMD_ps", (double) 0.5, "PMD off OADM booster amplifier");
118        private final InputParameter    oadm_addChannelNoiseFactor_dB           = new InputParameter("oadm_addChannelNoiseFactor_dB", (double) 6, "Noise factor observed by add channels");
119        private final InputParameter    oadm_dropChannelNoiseFactor_dB          = new InputParameter("oadm_dropChannelNoiseFactor_dB", (double) 6, "Noise factor observed by drop channels");
120        private final InputParameter    oadm_expressChannelNoiseFactor_dB       = new InputParameter("oadm_expressChannelNoiseFactor_dB", (double) 10, "Noise factor observed by express channels");
121
122        /* Optical line amplifier specifications */
123        private final InputParameter    edfa_minWavelength_nm                   = new InputParameter("edfa_minWavelength_nm", (double) 1530, "Minimum wavelength usable by the EDFA");
124        private final InputParameter    edfa_maxWavelength_nm                   = new InputParameter("edfa_maxWavelength_nm", (double) 1563, "Maximum wavelength usable by the EDFA");
125        private final InputParameter    edfa_minInputPower_dBm                  = new InputParameter("edfa_minInputPower_dBm", (double) -29, "Minimum input power at the EDFA");
126        private final InputParameter    edfa_maxInputPower_dBm                  = new InputParameter("edfa_maxInputPower_dBm", (double) 2, "Maximum input power at the EDFA");
127        private final InputParameter    edfa_minOutputPower_dBm                 = new InputParameter("edfa_minOutputPower_dBm", (double) -6, "Minimum output power at the EDFA");
128        private final InputParameter    edfa_maxOutputPower_dBm                 = new InputParameter("edfa_maxOutputPower_dBm", (double) 19, "Maximum output power at the EDFA");
129        private final InputParameter    edfa_minGain_dB                                 = new InputParameter("edfa_minGain_dB", (double) 17, "Minimum gain at the EDFA");
130        private final InputParameter    edfa_maxGain_dB                                 = new InputParameter("edfa_maxGain_dB", (double) 23, "Maximum gain at the EDFA");
131        private final InputParameter    edfa_PMD_ps                                             = new InputParameter("edfa_PMD_ps", (double) 0.5, "Polarization mode dispersion in ps added by the EDFA");
132        private final InputParameter    edfa_default_noiseFactor_dB             = new InputParameter("edfa_default_noiseFactor_dB", (double) 3, "Default noise factor used when the link does not have the attribute");
133        private final InputParameter    edfa_noiseFactorMaximumGain_dB  = new InputParameter("edfa_noiseFactorMaximumGain_dB", (double) 5,
134                        "Noise factor at the EDFA when the gain is in its upper limit (linear interpolation is used to estimate the noise figure at other gains)");
135        private final InputParameter    edfa_noiseFactorMinimumGain_dB  = new InputParameter("edfa_noiseFactorMinimumGain_dB", (double) 5,
136                        "Noise factor at the EDFA when the gain is in its lower limit (linear interpolation is used to estimate the noise figure at other gains)");
137
138        /* PC specs */
139        private final InputParameter pc_PMD_ps = new InputParameter("pc_PMD_ps", (double) 0.5, "Polarization mode dispersion in ps added by the PC");
140
141        /* OSNR penalties */
142        private final InputParameter    osnrPenalty_nonLinear_dB                = new InputParameter("osnrPenalty_nonLinear_dB", (double) 2, "OSNR penalty caused by the non-linear effects SPM, XPM, FWM and Brillouin / Raman scattering");
143        private final InputParameter    osnrPenalty_PMD_dB                              = new InputParameter("osnrPenalty_PMD_dB", (double) 0.5, "OSNR penalty caused by the polarization mode dispersion (assumed within limits)");
144        private final InputParameter    osnrPenalty_PDL_dB                              = new InputParameter("osnrPenalty_PDL_dB", (double) 0.3, "OSNR penalty caused by polarization dispersion losses");
145        private final InputParameter    osnrPenalty_transmitterChirp_dB = new InputParameter("osnrPenalty_transmitterChirp_dB", (double) 0.5, "OSNR penalty caused by transmitter chirp ");
146        private final InputParameter    osnrPenalty_OADMCrosstalk_dB    = new InputParameter("osnrPenalty_OADMCrosstalk_dB", (double) 0.8, "OSNR penalty caused by the crosstalk at the OADMs");
147        private final InputParameter    osnrPenalty_unassignedMargin_dB = new InputParameter("osnrPenalty_unassignedMargin_dB", (double) 3, "OSNR penalty caused by not assigned margins (e.g. random effects, aging, ...)");
148        private double                                  osnrPenalty_SUM_dB;
149
150        /* Global parameters */
151        private Map<String, double[]>           spectrumParameters;
152        Map<String, Map<String, Double>>        fiberParameters;
153        private double                                          centralFreq_THz;
154        private double[]                                        frequenciesPerChannel_THz;
155
156        @Override
157        public String executeReport(NetPlan netPlan, Map<String, String> reportParameters, Map<String, String> net2planParameters)
158        {
159                /* Input parameters */
160                this.netPlan = netPlan;
161                this.reportParameters = reportParameters;
162
163                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
164                InputParameter.initializeAllInputParameterFieldsOfObject(this, reportParameters);
165                fiberParameters = getFiberSpecsMap();
166
167                /* Initialize GN Parameters */
168                centralFreq_THz = gn_gen_f0_THz.getDouble() + (Math.floor(gn_gen_ns.getInt() / 2.0) * delta_f);
169                final Boolean[] laser_position = getLaserPositions(StringUtils.toBooleanArray(StringUtils.split(gn_spec_laserPosition.getString())));
170                frequenciesPerChannel_THz = getBasebandFrequency(laser_position);
171
172                /* Usable wavelengths */
173                channels_minChannelLambda_nm = OpticalImpairmentUtils.constant_c / ((gn_gen_f0_THz.getDouble() * 1e3) + gn_gen_ns.getInt() * delta_f);
174                channels_maxChannelLambda_nm = (OpticalImpairmentUtils.constant_c / (gn_gen_f0_THz.getDouble() * 1e12)) * 1e9;
175
176                /* OSNR penalties */
177                osnrPenalty_SUM_dB = osnrPenalty_nonLinear_dB.getDouble() + osnrPenalty_PMD_dB.getDouble() + osnrPenalty_PDL_dB.getDouble() + osnrPenalty_transmitterChirp_dB.getDouble() + osnrPenalty_OADMCrosstalk_dB.getDouble()
178                                + osnrPenalty_unassignedMargin_dB.getDouble();
179
180                final Map<Link, List<Quadruple<Double, String, Double, String>>> elements_e = new LinkedHashMap<Link, List<Quadruple<Double, String, Double, String>>>();
181                final Map<Link, List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>>> impairments_e = new LinkedHashMap<Link, List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>>>();
182                final Map<Link, List<String>> warnings_e = new LinkedHashMap<Link, List<String>>();
183
184                for (Link link : netPlan.getLinks())
185                {
186                        final List<Link> seqLinks = new LinkedList<Link>();
187                        seqLinks.add(link);
188                        final List<Quadruple<Double, String, Double, String>> elementPositions = getElementPositionsListPerLightpath(seqLinks);
189
190                        spectrumParameters = initializeSpectrum();
191
192                        final List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> impairmentsAtInputAndOutputs = OpticalImpairmentUtils.computeImpairments(elementPositions, spectrumParameters, fiberParameters,
193                                        oadm_outputPowerPerChannel_W.getDouble(), fiber_PMD_ps_per_sqroot_km.getDouble(), edfa_PMD_ps.getDouble(), pc_PMD_ps.getDouble(), oadm_muxDemuxPMD_ps.getDouble(), oadm_preAmplifierPMD_ps.getDouble(),
194                                        oadm_boosterPMD_ps.getDouble(), frequenciesPerChannel_THz, centralFreq_THz, tp_inputPowerSensitivityMin_dBm.getDouble(), tp_inputPowerSensitivityMax_dBm.getDouble());
195                        final List<String> warningMessages = computeWarningMessages(elementPositions, impairmentsAtInputAndOutputs);
196
197                        elements_e.put(link, elementPositions);
198                        impairments_e.put(link, impairmentsAtInputAndOutputs);
199                        warnings_e.put(link, warningMessages);
200                }
201
202                final Map<Route, List<Quadruple<Double, String, Double, String>>> elements_r = new LinkedHashMap<Route, List<Quadruple<Double, String, Double, String>>>();
203                final Map<Route, List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>>> impairments_r = new LinkedHashMap<Route, List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>>>();
204                final Map<Route, List<String>> warnings_r = new LinkedHashMap<Route, List<String>>();
205                for (Route r : netPlan.getRoutes())
206                {
207                        final List<Link> seqLinks = r.getSeqLinks();
208                        final List<Quadruple<Double, String, Double, String>> elementPositions = getElementPositionsListPerLightpath(seqLinks);
209
210                        spectrumParameters = initializeSpectrum();
211
212                        final List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> impairmentsAtInputAndOutputs = OpticalImpairmentUtils.computeImpairments(elementPositions, spectrumParameters, fiberParameters,
213                                        oadm_outputPowerPerChannel_W.getDouble(), fiber_PMD_ps_per_sqroot_km.getDouble(), edfa_PMD_ps.getDouble(), pc_PMD_ps.getDouble(), oadm_muxDemuxPMD_ps.getDouble(), oadm_preAmplifierPMD_ps.getDouble(),
214                                        oadm_boosterPMD_ps.getDouble(), frequenciesPerChannel_THz, centralFreq_THz, tp_inputPowerSensitivityMin_dBm.getDouble(), tp_inputPowerSensitivityMax_dBm.getDouble());
215                        final List<String> warningMessages = computeWarningMessages(elementPositions, impairmentsAtInputAndOutputs);
216
217                        elements_r.put(r, elementPositions);
218                        impairments_r.put(r, impairmentsAtInputAndOutputs);
219                        warnings_r.put(r, warningMessages);
220                }
221
222                return printReport(elements_e, impairments_e, warnings_e, elements_r, impairments_r, warnings_r);
223        }
224
225        @Override
226        public String getDescription()
227        {
228                return "This report shows line engineering information for WDM links in the network. " + " The report assumes that the WDM network follows the scheme:\n"
229                                + " * In the net2plan object, nodes are OADMs, links are fiber links  and routes are lightpaths:\n" + "WDM channels optically switched at intermediate nodes.\n"
230                                + " * Nodes are connected by unidirectional fiber links. Fiber link distance is" + " given by the link length. Other specifications are given by fibers_XXX input parameters, each one describing the"
231                                + "parameter for the fiber types specified in fibers_types, in the same order and separated by" + "spaces. The fiber can be split into spans if optical amplifers (EDFAs) and/or passive components"
232                                + "(PCs) are placed along the fiber. These spans can be of different fiber types as long as they are" + "described in a link attribute called \"fiberTypes\". Must be separated by spaces and, in case that"
233                                + "there were more spans than elements of the attribute, the default type given in" + "\"fiber_default_type\" would be used." + " * Optical line amplifiers (EDFAs) can be located in none, one or more"
234                                + " positions in the fiber link, separating them into different spans. EDFAs are" + " supposed to operate in the automatic gain control mode. Thus, the gain is the"
235                                + " same, whatever the number of input WDM channels. EDFA positions (as distance" + " in km from the link start to the EDFA location), EDFA gains (assumed in"
236                                + " dB) and EDFA noise figures (in dB) are read from the \"edfaPositions_km\", \"edfaGains_dB\" and \"edfaNoiseFigures_dB\"" + " attributes of the links. The format of all attributes will be the same: a string of numbers"
237                                + " separated by spaces. The i-th number corresponding to the position/gain of the" + " i-th EDFA. If the attributes do not exist, it is assumed that no EDFAs"
238                                + " are placed in this link. EDFA specifications are given by \"edfa_XXX\" parameters.\n" + " * Passive components are described by the link attributes \"pcPositions_km\" and \"pcLosses_dB\".\n"
239                                + " The i-th number corresponding to the position/loss of the i-th PC.\n" + " If the attributes do not exist, it is assumed that no PCs are placed in this link. \n" + " Further description in the HTML generated.";
240        }
241
242        @Override
243        public List<Triple<String, String, String>> getParameters()
244        {
245                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
246                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
247        }
248
249        @Override
250        public String getTitle()
251        {
252                return "WDM line engineering with GN calculations";
253        }
254
255        /** Checks if the number of elements in the fiber input parameters is always the same. If it is, returns a Map for each type of fiber with its parameters
256         * 
257         * @return Map("FiberTypeName", Map("param", value)) */
258        private Map<String, Map<String, Double>> getFiberSpecsMap()
259        {
260                final Map<String, Map<String, Double>> fiberSpecs = new HashMap<String, Map<String, Double>>();
261
262                final String[] fiberTypes = StringUtils.split(fibers_types.getString());
263                final double[] fiberAlphas = StringUtils.toDoubleArray(StringUtils.split(fibers_alpha_dB_per_km.getString()));
264                final double[] fiberAlpha1sts = StringUtils.toDoubleArray(StringUtils.split(fibers_alpha1st_dB_per_km_per_THz.getString()));
265                final double[] fiberBeta2s = StringUtils.toDoubleArray(StringUtils.split(fibers_beta2_ps2_per_km.getString()));
266                final double[] fiberAeffs = StringUtils.toDoubleArray(StringUtils.split(fibers_Aeff_um2.getString()));
267                final double[] fiberN2s = StringUtils.toDoubleArray(StringUtils.split(fibers_n2_m2_per_W.getString()));
268                final int numFiberTypes = fibers_numberOfFiberTypes.getInt();
269
270                if (numFiberTypes != fiberTypes.length || numFiberTypes != fiberAlphas.length || numFiberTypes != fiberAlpha1sts.length || numFiberTypes != fiberBeta2s.length || numFiberTypes != fiberAeffs.length || numFiberTypes != fiberN2s.length)
271                        throw new Net2PlanException("Incorrect number of fiber parameters.");
272
273                boolean containsDefaultType = false;
274                for (String string : fiberTypes)
275                        if (string.equalsIgnoreCase(fiber_default_type.getString()))
276                                containsDefaultType = true;
277
278                if (!containsDefaultType)
279                        throw new Net2PlanException("fiber_default_type is not contained in the fibers_types list.");
280
281                for (int i = 0; i < fiberTypes.length; i++)
282                {
283                        final String fiberType = fiberTypes[i];
284
285                        final Map<String, Double> specs_thisType = new HashMap<String, Double>();
286                        specs_thisType.put(OpticalImpairmentUtils.stFiber_alpha_dB_per_km, fiberAlphas[i]);
287                        specs_thisType.put(OpticalImpairmentUtils.stFiber_alpha1st_dB_per_km_per_THz, fiberAlpha1sts[i]);
288                        specs_thisType.put(OpticalImpairmentUtils.stFiber_beta2_ps2_per_km, fiberBeta2s[i]);
289                        specs_thisType.put(OpticalImpairmentUtils.stFiber_effectiveArea_um2, fiberAeffs[i]);
290                        specs_thisType.put(OpticalImpairmentUtils.stFiber_n2Coeff_m2_per_W, fiberN2s[i]);
291
292                        fiberSpecs.put(fiberType, specs_thisType);
293                }
294
295                return fiberSpecs;
296        }
297
298        /** Initializes all spectrum parameters with the given input parameters
299         * 
300         * @return initial spectrum */
301        private Map<String, double[]> initializeSpectrum()
302        {
303                final Map<String, double[]> spectrumParameters = new HashMap<>();
304                final int numChannels = gn_spec_nCh.getInt();
305
306                final double[] bandwidthPerChannel_THz = new double[numChannels];
307                final double[] powerPerChannel_W = new double[numChannels];
308                final double[] aseNoisePower_W = new double[numChannels];
309                final double[] nliNoisePower_W = new double[numChannels];
310
311                Arrays.fill(bandwidthPerChannel_THz, gn_spec_bandwidthCh_THz.getDouble());
312                Arrays.fill(powerPerChannel_W, oadm_outputPowerPerChannel_W.getDouble());
313                Arrays.fill(aseNoisePower_W, 0);
314                Arrays.fill(nliNoisePower_W, 0);
315
316                spectrumParameters.put(OpticalImpairmentUtils.stSpectrum_bandwidthPerChannel_THz, bandwidthPerChannel_THz);
317                spectrumParameters.put(OpticalImpairmentUtils.stSpectrum_powerPerChannel_W, powerPerChannel_W);
318                spectrumParameters.put(OpticalImpairmentUtils.stSpectrum_aseNoisePower_W, aseNoisePower_W);
319                spectrumParameters.put(OpticalImpairmentUtils.stSpectrum_nliNoisePowerg_W, nliNoisePower_W);
320
321                return spectrumParameters;
322        }
323
324        /** Gets an array of booleans with the status of the lasers for all channels
325         * 
326         * @param lp laser positions per channel
327         * @return extended array with the lp of every channel */
328        private Boolean[] getLaserPositions(boolean[] lp)
329        {
330
331                List<Boolean> lasers = new LinkedList<>();
332                final List<Boolean> lps = Arrays.asList(ArrayUtils.toObject(lp));
333
334                for (int i = 0; i < gn_spec_nCh.getInt(); i++)
335                        lasers = ListUtils.union(lasers, lps);
336
337                return lasers.toArray(new Boolean[lasers.size()]);
338
339        }
340
341        /** Initializes frequencies for each channel
342         * 
343         * @param laser_position boolean whether a laser is turn on or not
344         * @return frequencies per channel */
345        private double[] getBasebandFrequency(Boolean[] laser_position)
346        {
347                double[] frequenciesPerChannel_THz = DoubleUtils.zeros(gn_spec_nCh.getInt());
348
349                int count = 0;
350                for (int i = 0; i < laser_position.length; i++)
351                        if (laser_position[i])
352                                frequenciesPerChannel_THz[count++] = (gn_gen_f0_THz.getDouble() - centralFreq_THz) + delta_f * i;
353
354                return frequenciesPerChannel_THz;
355        }
356
357        /** Gets the network warnings for the elements and impairments given
358         * 
359         * @param elementPositions List of Quadruple of(position [km], type, 3rd: data; 4th: auxData)
360         * @param impairmentsAtInputAndOutputs List of Quadruple of (before element Map(paramName, value), before element PMD, after element Map(paramName, value), after element PMD)
361         * @return warnings */
362        private List<String> computeWarningMessages(List<Quadruple<Double, String, Double, String>> elementPositions, List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> impairmentsAtInputAndOutputs)
363        {
364                final double numChannels_dB = OpticalImpairmentUtils.linear2dB(gn_spec_nCh.getInt());
365                final int centralChannel = Math.floorDiv(gn_spec_nCh.getInt(), 2);
366                final List<String> res = new LinkedList<String>();
367
368                final Iterator<Quadruple<Double, String, Double, String>> it_elementPositions = elementPositions.iterator();
369                final Iterator<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> it_impairments = impairmentsAtInputAndOutputs.iterator();
370
371                while (it_elementPositions.hasNext())
372                {
373                        String st = "";
374
375                        final Quadruple<Double, String, Double, String> thisElement = it_elementPositions.next();
376                        final Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double> thisImpairments = it_impairments.next();
377
378                        final double initialPosition_km = thisElement.getFirst();
379                        final String name = thisElement.getSecond();
380                        final double elementData = thisElement.getThird();
381
382                        final Map<String, double[]> preImpSpectrum = thisImpairments.getFirst();
383                        final double pre_powerPerChannel_dBm = OpticalImpairmentUtils.linear2dB(preImpSpectrum.get(OpticalImpairmentUtils.stSpectrum_powerPerChannel_W)[centralChannel] * 1e3);
384
385                        final Map<String, double[]> postImpSpectrum = thisImpairments.getThird();
386                        final double post_powerPerChannel_dBm = OpticalImpairmentUtils.linear2dB(postImpSpectrum.get(OpticalImpairmentUtils.stSpectrum_powerPerChannel_W)[centralChannel] * 1e3);
387
388                        if (name.equalsIgnoreCase("OADM-ADD"))
389                        {
390                                /* Wavelengths in use within transponder range */
391                                if (channels_minChannelLambda_nm < tp_minWavelength_nm.getDouble())
392                                        st += "Wavelength " + channels_minChannelLambda_nm + " nm is outside the transponder range [" + tp_minWavelength_nm.getDouble() + " nm, " + tp_maxWavelength_nm.getDouble() + " nm]";
393                                if (channels_maxChannelLambda_nm > tp_maxWavelength_nm.getDouble())
394                                        st += "Wavelength " + channels_maxChannelLambda_nm + " nm is outside the transponder range [" + tp_minWavelength_nm.getDouble() + " nm, " + tp_maxWavelength_nm.getDouble() + " nm]";
395
396                                /* Output power within limits */
397                                if (Math.abs(post_powerPerChannel_dBm - OpticalImpairmentUtils.linear2dB(oadm_outputPowerPerChannel_W.getDouble() * 1e3)) > 1E-3)
398                                        st += "At " + initialPosition_km + "km: Power at the OADM-ADD output is " + post_powerPerChannel_dBm + " dBm. It should be: " + OpticalImpairmentUtils.linear2dB(oadm_outputPowerPerChannel_W.getDouble() * 1e3);
399
400                        } else if (name.equalsIgnoreCase("OADM-EXPRESS"))
401                        {
402                                /* Input power within limits */
403                                if (pre_powerPerChannel_dBm < oadm_perChannelMinInputPower_dBm.getDouble() - 1E-3)
404                                        st += "At " + initialPosition_km + "km: Power at the OADM-EXPRESS input is " + pre_powerPerChannel_dBm + " dBm. It should be between [" + oadm_perChannelMinInputPower_dBm.getDouble() + ", "
405                                                        + oadm_perChannelMaxInputPower_dBm.getDouble() + "] dBm";
406                                if (pre_powerPerChannel_dBm > oadm_perChannelMaxInputPower_dBm.getDouble() + 1E-3)
407                                        st += "At " + initialPosition_km + "km: Power at the OADM-EXPRESS input is " + pre_powerPerChannel_dBm + " dBm. It should be between [" + oadm_perChannelMinInputPower_dBm.getDouble() + ", "
408                                                        + oadm_perChannelMaxInputPower_dBm.getDouble() + "] dBm";
409
410                                /* Output power within limits */
411                                if (Math.abs(post_powerPerChannel_dBm - OpticalImpairmentUtils.linear2dB(oadm_outputPowerPerChannel_W.getDouble() * 1e3)) > 1E-3)
412                                        st += "At " + initialPosition_km + "km: Power at the OADM-EXPRESS output is " + post_powerPerChannel_dBm + " dBm. It should be: " + OpticalImpairmentUtils.linear2dB(oadm_outputPowerPerChannel_W.getDouble() * 1e3);
413
414                        } else if (name.equalsIgnoreCase("OADM-DROP"))
415                        {
416                                final double post_PMDSquared_ps2 = thisImpairments.getFourth();
417                                final Triple<double[], double[], double[]> post_OSNR_linear = OpticalImpairmentUtils.getOSNR(postImpSpectrum);
418
419                                /* Input power within limits */
420                                if (pre_powerPerChannel_dBm < oadm_perChannelMinInputPower_dBm.getDouble() - 1E-3)
421                                        st += "At " + initialPosition_km + "km: Power at the OADM-DROP input is " + pre_powerPerChannel_dBm + " dBm. It should be between [" + oadm_perChannelMinInputPower_dBm.getDouble() + ","
422                                                        + oadm_perChannelMaxInputPower_dBm.getDouble() + "] dBm";
423                                if (pre_powerPerChannel_dBm > oadm_perChannelMaxInputPower_dBm.getDouble() + 1E-3)
424                                        st += "At " + initialPosition_km + "km: Power at the OADM-DROP input is " + pre_powerPerChannel_dBm + " dBm. It should be between [" + oadm_perChannelMinInputPower_dBm.getDouble() + ","
425                                                        + oadm_perChannelMaxInputPower_dBm.getDouble() + "] dBm";
426
427                                /* Output power within limits */
428                                if (post_powerPerChannel_dBm < tp_inputPowerSensitivityMin_dBm.getDouble() - 1E-3)
429                                        st += "At " + initialPosition_km + "km: Power at the OADM-DROP output is " + post_powerPerChannel_dBm + ". It should be between [" + tp_inputPowerSensitivityMin_dBm.getDouble() + "," + tp_inputPowerSensitivityMax_dBm.getDouble()
430                                                        + "] dBm";
431                                if (post_powerPerChannel_dBm > tp_inputPowerSensitivityMax_dBm.getDouble() + 1E-3)
432                                        st += "At " + initialPosition_km + "km: Power at the OADM-DROP output is " + post_powerPerChannel_dBm + ". It should be between [" + tp_inputPowerSensitivityMin_dBm.getDouble() + "," + tp_inputPowerSensitivityMax_dBm.getDouble()
433                                                        + "] dBm";
434
435                                /* OSNR within limits */
436                                if (OpticalImpairmentUtils.linear2dB(post_OSNR_linear.getThird()[centralChannel]) < tp_minOSNR_dB.getDouble() + osnrPenalty_SUM_dB)
437                                        st += "At " + initialPosition_km + "km: OSNR at the RECEIVER is " + OpticalImpairmentUtils.linear2dB(post_OSNR_linear.getThird()[centralChannel]) + " dB. It is below the tolerance plus margin " + tp_minOSNR_dB.getDouble()
438                                                        + " dB + penalties " + osnrPenalty_SUM_dB + " dB = " + (tp_minOSNR_dB.getDouble() + osnrPenalty_SUM_dB) + " dB)";
439
440                                /* PMD tolerance at the receiver */
441                                final double pmdAtReceiver = Math.sqrt(post_PMDSquared_ps2);
442                                if (pmdAtReceiver > tp_pmdTolerance_ps.getDouble())
443                                        st += "At " + initialPosition_km + "km: PMD at the RECEIVER is " + pmdAtReceiver + " ps. It is above the maximum PMD tolerance (" + tp_pmdTolerance_ps.getDouble() + " ps)";
444
445                        } else if (name.equalsIgnoreCase("SPAN"))
446                        {} else if (name.equalsIgnoreCase("EDFA"))
447                        {
448                                final double edfaGain_dB = elementData;
449
450                                /* Wavelengths within limits */
451                                if (channels_minChannelLambda_nm < edfa_minWavelength_nm.getDouble())
452                                        st += "Wavelength " + channels_minChannelLambda_nm + " nm is outside the transponder range [" + edfa_minWavelength_nm.getDouble() + " nm, " + edfa_maxWavelength_nm.getDouble() + " nm]";
453                                if (channels_maxChannelLambda_nm > edfa_maxWavelength_nm.getDouble())
454                                        st += "Wavelength " + channels_maxChannelLambda_nm + " nm is outside the transponder range [" + edfa_minWavelength_nm.getDouble() + " nm, " + edfa_maxWavelength_nm.getDouble() + " nm]";
455
456                                /* Gain within limits */
457                                if (edfaGain_dB < edfa_minGain_dB.getDouble() - 1E-3)
458                                        st += "At " + initialPosition_km + "km: EDFA gain is " + edfaGain_dB + " dB. It should be between [" + edfa_minGain_dB.getDouble() + ", " + edfa_maxGain_dB.getDouble() + "] dB";
459                                if (edfaGain_dB > edfa_maxGain_dB.getDouble() + 1E-3)
460                                        st += "At " + initialPosition_km + "km: EDFA gain is " + edfaGain_dB + " dB. It should be between [" + edfa_minGain_dB.getDouble() + ", " + edfa_maxGain_dB.getDouble() + "] dB";
461
462                                /* Input power within limits */
463                                if (pre_powerPerChannel_dBm < edfa_minInputPower_dBm.getDouble() - 1E-3)
464                                        st += "At " + initialPosition_km + "km: Power at the EDFA input is (is one WDM channel) " + pre_powerPerChannel_dBm + " dBm. It should be between [" + edfa_minInputPower_dBm.getDouble() + ", " + edfa_maxInputPower_dBm.getDouble()
465                                                        + "] dBm";
466                                if (pre_powerPerChannel_dBm + numChannels_dB > edfa_maxInputPower_dBm.getDouble() + 1E-3)
467                                        st += "At " + initialPosition_km + "km: Power at the EDFA input is (if all WDM channels were active) " + (pre_powerPerChannel_dBm + numChannels_dB) + " dBm. It should be between [" + edfa_minInputPower_dBm.getDouble() + ","
468                                                        + edfa_maxInputPower_dBm.getDouble() + "] dBm";
469
470                                /* Output power within limits */
471                                if (post_powerPerChannel_dBm < edfa_minOutputPower_dBm.getDouble() - 1E-3)
472                                        st += "At " + initialPosition_km + "km: Power at the EDFA output is (is one WDM channel) " + post_powerPerChannel_dBm + " dBm. It should be between [" + edfa_minOutputPower_dBm.getDouble() + ", "
473                                                        + edfa_maxOutputPower_dBm.getDouble() + "] dBm";
474                                if (post_powerPerChannel_dBm + numChannels_dB > edfa_maxOutputPower_dBm.getDouble() + 1E-3)
475                                        st += "At " + initialPosition_km + "km: Power at the EDFA output is (if all WDM channels were active) " + (post_powerPerChannel_dBm + numChannels_dB) + " dBm. It should be between [" + edfa_minOutputPower_dBm.getDouble() + ", "
476                                                        + edfa_maxOutputPower_dBm.getDouble() + "] dBm";
477                        } else if (name.equalsIgnoreCase("PC"))
478                        {} else
479                        {
480                                throw new RuntimeException("Unknown element type");
481                        }
482
483                        res.add(st);
484                }
485
486                return res;
487        }
488
489        /** Gets all the elements in the given link or lightpath
490         * 
491         * @param seqLinks list of links
492         * @return List of elements as a Quadruple object where: first is the position of the element (km), second the type of element, third the main parameter (i.e. OA=gain, PC=loss, SPAN=length, OADM=nodeID), fourth other information (i.e.
493         *         OA=noiseFigure, SPAN=fiberType, OADM=noiseFigure). */
494        private List<Quadruple<Double, String, Double, String>> getElementPositionsListPerLightpath(List<Link> seqLinks)
495        {
496                final List<Quadruple<Double, String, Double, String>> res = new LinkedList<Quadruple<Double, String, Double, String>>();
497                double currentDistanceFromRouteInit_km = 0;
498
499                for (int index = 0; index < seqLinks.size(); index++)
500                {
501                        final Link e = seqLinks.get(index);
502                        int oadmCounter = 0;
503
504                        final double d_e = e.getLengthInKm();
505                        final String st_edfaPositions_km = e.getAttribute("edfaPositions_km") == null ? "" : e.getAttribute("edfaPositions_km");
506                        final String st_edfaGains_dB = e.getAttribute("edfaGains_dB") == null ? "" : e.getAttribute("edfaGains_dB");
507                        final String st_edfaNoiseFigures_dB = e.getAttribute("edfaNoiseFigures_dB") == null ? "" : e.getAttribute("edfaNoiseFigures_dB");
508                        final String st_pcPositions_km = e.getAttribute("pcPositions_km") == null ? "" : e.getAttribute("pcPositions_km");
509                        final String st_pcLosses_dB = e.getAttribute("pcLosses_dB") == null ? "" : e.getAttribute("pcLosses_dB");
510                        final String st_oadmNoiseFigures_dB = e.getAttribute("oadmNoiseFigures_dB") == null ? "" : e.getAttribute("oadmNoiseFigures_dB");
511                        final String st_fiberTypes = e.getAttribute("fiberTypes") == null ? "" : e.getAttribute("fiberTypes");
512
513                        final double[] edfaPositions_km = StringUtils.toDoubleArray(StringUtils.split(st_edfaPositions_km));
514                        final double[] edfaGains_dB = StringUtils.toDoubleArray(StringUtils.split(st_edfaGains_dB));
515                        final double[] edfaNoiseFigures_dB = StringUtils.toDoubleArray(StringUtils.split(st_edfaNoiseFigures_dB));
516                        final double[] pcPositions_km = StringUtils.toDoubleArray(StringUtils.split(st_pcPositions_km));
517                        final double[] pcLosses_dB = StringUtils.toDoubleArray(StringUtils.split(st_pcLosses_dB));
518                        final double[] oadmNoiseFigures_dB = StringUtils.toDoubleArray(StringUtils.split(st_oadmNoiseFigures_dB));
519                        final String[] fiberTypes = StringUtils.split(st_fiberTypes);
520
521                        /* Basic checks */
522                        if (edfaPositions_km.length != edfaGains_dB.length)
523                                throw new Net2PlanException("Link: " + e + ". Number of elements in edfaPositions_km is not equal to the number of elements in edfaGains_dB");
524
525                        if (pcPositions_km.length != pcLosses_dB.length)
526                                throw new Net2PlanException("Link: " + e + ". Number of elements in pcPositions_km is not equal to the number of elements in pcLosses_dB");
527
528                        for (double edfaPosition : edfaPositions_km)
529                                if ((edfaPosition < 0) || (edfaPosition > d_e))
530                                        throw new Net2PlanException("Link: " + e + ". Wrong OA position: " + edfaPosition + ", link length = " + d_e);
531
532                        for (double pcPosition : pcPositions_km)
533                                if ((pcPosition < 0) || (pcPosition > d_e))
534                                        throw new Net2PlanException("Link: " + e + ". Wrong PC position: " + pcPosition + ", link length = " + d_e);
535
536                        for (double noiseFigure_dB : edfaNoiseFigures_dB)
537                                if ((noiseFigure_dB < Math.min(edfa_noiseFactorMinimumGain_dB.getDouble(), edfa_noiseFactorMaximumGain_dB.getDouble()))
538                                                || (noiseFigure_dB > Math.max(edfa_noiseFactorMinimumGain_dB.getDouble(), edfa_noiseFactorMaximumGain_dB.getDouble())))
539                                        throw new RuntimeException("Bad EDFA Noise Factor, out of range");
540
541                        /* All links and lightpaths allways begin with an OADM-ADD */
542                        if (index == 0)
543                                if (oadmNoiseFigures_dB.length > 0)
544                                        res.add(Quadruple.of(currentDistanceFromRouteInit_km, "OADM-ADD", (double) e.getOriginNode().getId(), oadmNoiseFigures_dB[oadmCounter++] + ""));
545                                else
546                                        res.add(Quadruple.of(currentDistanceFromRouteInit_km, "OADM-ADD", (double) e.getOriginNode().getId(), oadm_addChannelNoiseFactor_dB.getDouble() + ""));
547
548                        /* Place in a sorted form the spans, PCs and EDFAS. If PC and EDFA placed, PC goes first */
549                        final double[] pcAndEDFAPositions_km = DoubleUtils.concatenate(pcPositions_km, edfaPositions_km);
550                        final int[] sortedPCAndEDFAPositionsIndexes = pcAndEDFAPositions_km.length == 0 ? new int[0] : DoubleUtils.sortIndexes(pcAndEDFAPositions_km, OrderingType.ASCENDING);
551                        double posKmLastElementThisLink_km = 0;
552                        int fiberSpans = 0;
553                        for (int cont = 0; cont < sortedPCAndEDFAPositionsIndexes.length; cont++)
554                        {
555                                final int indexInCommonArray = sortedPCAndEDFAPositionsIndexes[cont];
556                                final boolean isPC = (indexInCommonArray < pcPositions_km.length);
557                                final double posFromLinkInit_km = pcAndEDFAPositions_km[indexInCommonArray];
558                                final double previousSpanLength = (Math.abs(posFromLinkInit_km - posKmLastElementThisLink_km) < 1E-3) ? 0 : posFromLinkInit_km - posKmLastElementThisLink_km;
559
560                                if (previousSpanLength < 0)
561                                        throw new RuntimeException("Bad");
562
563                                if (previousSpanLength > 0)
564                                {
565                                        if (fiberSpans < fiberTypes.length)
566                                                res.add(Quadruple.of(currentDistanceFromRouteInit_km, "SPAN", previousSpanLength, fiberTypes[fiberSpans++]));
567                                        else
568                                                res.add(Quadruple.of(currentDistanceFromRouteInit_km, "SPAN", previousSpanLength, fiber_default_type.getString()));
569                                        currentDistanceFromRouteInit_km += previousSpanLength;
570                                        posKmLastElementThisLink_km += previousSpanLength;
571                                }
572
573                                if (isPC)
574                                        res.add(Quadruple.of(currentDistanceFromRouteInit_km, "PC", pcLosses_dB[indexInCommonArray], null));
575                                else if (edfaNoiseFigures_dB.length > 0 && edfaNoiseFigures_dB.length < (indexInCommonArray - pcPositions_km.length))
576                                        res.add(Quadruple.of(currentDistanceFromRouteInit_km, "EDFA", edfaGains_dB[indexInCommonArray - pcPositions_km.length], edfaNoiseFigures_dB[indexInCommonArray - pcPositions_km.length] + ""));
577                                else
578                                        res.add(Quadruple.of(currentDistanceFromRouteInit_km, "EDFA", edfaGains_dB[indexInCommonArray - pcPositions_km.length], edfa_default_noiseFactor_dB.getDouble()+""));
579                        }
580
581                        /* Last span of the link before the OADM */
582                        final double lastSpanOfLink_km = (Math.abs(d_e - posKmLastElementThisLink_km) < 1E-3) ? 0 : d_e - posKmLastElementThisLink_km;
583
584                        if (lastSpanOfLink_km < 0)
585                                throw new RuntimeException("Bad");
586
587                        if (lastSpanOfLink_km > 0)
588                        {
589                                if (fiberSpans < fiberTypes.length)
590                                        res.add(Quadruple.of(currentDistanceFromRouteInit_km, "SPAN", lastSpanOfLink_km, fiberTypes[fiberSpans++]));
591                                else
592                                        res.add(Quadruple.of(currentDistanceFromRouteInit_km, "SPAN", lastSpanOfLink_km, fiber_default_type.getString()));
593                                currentDistanceFromRouteInit_km += lastSpanOfLink_km;
594                                posKmLastElementThisLink_km += lastSpanOfLink_km;
595                        }
596
597                        /* OADM at the end of the link */
598                        final long endNodeLink = e.getDestinationNode().getId();
599                        final long lastLink = seqLinks.get(seqLinks.size() - 1).getId();
600                        if (e.getId() == lastLink)
601                                if (oadmCounter < oadmNoiseFigures_dB.length)
602                                        res.add(Quadruple.of(currentDistanceFromRouteInit_km, "OADM-DROP", (double) endNodeLink, oadmNoiseFigures_dB[oadmCounter++] + ""));
603                                else
604                                        res.add(Quadruple.of(currentDistanceFromRouteInit_km, "OADM-DROP", (double) endNodeLink, oadm_dropChannelNoiseFactor_dB.getDouble() + ""));
605                        else if (oadmCounter < oadmNoiseFigures_dB.length)
606                                res.add(Quadruple.of(currentDistanceFromRouteInit_km, "OADM-EXPRESS", (double) endNodeLink, oadmNoiseFigures_dB[oadmCounter++] + ""));
607                        else
608                                res.add(Quadruple.of(currentDistanceFromRouteInit_km, "OADM-EXPRESS", (double) endNodeLink, oadm_expressChannelNoiseFactor_dB.getDouble() + ""));
609
610                }
611
612                /* Check current distance equals the sum of the traversed links */
613                double sumLinks = 0;
614                for (Link e : seqLinks)
615                        sumLinks += e.getLengthInKm();
616
617                if (Math.abs(sumLinks - currentDistanceFromRouteInit_km) > 1E-3)
618                        throw new RuntimeException("Bad");
619                return res;
620        }
621
622        private String printReport(Map<Link, List<Quadruple<Double, String, Double, String>>> elements_e, Map<Link, List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>>> impairments_e, Map<Link, List<String>> warnings_e,
623                        Map<Route, List<Quadruple<Double, String, Double, String>>> elements_r, Map<Route, List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>>> impairments_r, Map<Route, List<String>> warnings_r)
624        {
625                final StringBuilder out = new StringBuilder();
626                final DecimalFormat df_2 = new DecimalFormat("###.##");
627                final int centralChannel = Math.floorDiv(gn_spec_nCh.getInt(), 2);
628
629                out.append("<html><body>");
630                out.append("<head><title>WDM line engineering in multilayer (lightpath based) networks with GN calculations</title></head>");
631                out.append("<h1>WDM line engineering report for lighptath-based networks with GN calculations</h1>");
632
633                out.append(
634                                "<h3>This report shows line engineering information for WDM links in a multilayer optical network. " + "The impairment calculations are based on the Gaussian Noise Model developed by Politecnico di Torino and their analytic formula."
635                                                + " Other calculations are inspired in the procedures described in the 2009 ITU-T WDM manual  \"Optical fibres, cabbles and systems\".</h3>");
636
637                out.append("<p>The report assumes that the WDM network follows the scheme:</p>");
638
639                out.append("<ul>");
640                out.append("<li>In the net2plan object, nodes are OADMs, links are fiber links, and routes are lightpaths: " + "WDM channels optically switched at intermediate nodes. </li>");
641                out.append("<li>Nodes are connected by unidirectional fiber links. Fiber link distance is given by the link " + "length. Other specifications are given by fibers_XXX input parameters, each one describing the "
642                                + "parameter for the fiber types specified in fibers_types, in the same order and separated by " + "spaces. The fiber can be split into spans if optical amplifers (EDFAs) and/or passive components "
643                                + "(PCs) are placed along the fiber. These spans can be of different fiber types as long as they are " + "described in a link attribute called \"fiberTypes\". Must be separated by spaces and, in case that "
644                                + "there were more spans than elements of the attribute, the default type given in \"fiber_default_type\" " + "would be used.</li>");
645                out.append("<li>Optical line amplifiers (EDFAs) can be located in none, one or more positions in the fiber" + " link, separating them in different spans. EDFAs are supposed to operate in the automatic gain"
646                                + " control mode. Thus, the gain is the same, whatever the number of input WDM channels. EDFA" + " positions (as distance\" in km from the link start to the EDFA location), EDFA gains (assumed in"
647                                + " dB) and EDFA noise figures (in dB) are read from the \"edfaPositions_km\", \"edfaGains_dB\" and" + " \"edfaNoiseFigures_dB\" attributes of the links. The format of all attributes are the same: a"
648                                + " string of numbers separated by spaces. The <i>i</i>-th number corresponding to the position/gain" + " of the <i>i</i>-th EDFA. If the attributes do not exist, it is assumed that no EDFAs are placed in this link. "
649                                + "EDFA specifications are given by \"edfa_XXX\" parameters</li>");
650                out.append("<li>There are not Dispersion compensating modules (DCMs) in the topoology, since the Gaussian Noise Model is used.</li>");
651                out.append("<li>Passive components are described by the link attributes \"pcPositions_km\" and \"pcLosses_dB\"." + " The <i>i</i>-th number corresponding to the position/loss of the <i>i</i>-th PC. If the"
652                                + " attributes do not exist, it is assumed that no PCs are placed in this link. Other specifications for Passive Components" + " will be described in teh pc_XXX input parameters.</li>");
653                out.append("<li>Fiber links start and end in OADM modules, that permit adding, dropping and optically switch" + " individual WDM channels. OADMs have a pre-amplifier (traversed by drop and express channels) and"
654                                + " a boost amplifier (traversed by add and express channels). They are supposed to equalize the" + " channel power at their outputs, to a fixed value (added and express channels will thus have the"
655                                + " same power in the fibers). Also, OADMs attenuate appropriately the optical signal coming from the" + " pre-amplifier, in the drop channels, so that they fall within the receiver sensitivity range."
656                                + " OADM noise figures for add, drop and express channels are given as input parameters. PMD values" + " for add, drop and express channels are computed assumming that: (i) add channel traverse a"
657                                + " multiplexer and the booster, (ii) drop channels travese the pre-amplifier and a demultiplexer," + " (iii) express channels traverse the two amplifiers. The required parameters are provided in oadm_XXX parameters.</li>");
658                out.append("<li>Each channel ends in a receiver, with specifications given by \"tp_XXX\" parameters.</li>");
659                out.append("</ul></p>");
660                out.append("<p>The basic checks performed are:</p>");
661                out.append("<ul>");
662                out.append("<li>For each link, signal power levels are within operating ranges at the oadm/edfas, both when the link has one single active channel, or when all the");
663                out.append("\"gn_spec_nCh\" are active</li>");
664                out.append("<li>For each route (lightpath), OSNR (Optical Signal to Noise Ration) is within the operating range at the receiver.");
665                out.append("A set of margins are considered to account to several not directly considered impairments. </li>");
666                out.append("<li>For each route (lightpath), PMD (Polarization mode dispersion) is within the operating range at the receiver</li>");
667                out.append("</ul></p>");
668
669                out.append("<h2>Input Parameters</h2>");
670                out.append("<table border='1'>");
671                out.append("<tr><th><b>Name</b></th><th><b>Value</b></th><th><b>Description</b></th>");
672
673                for (Triple<String, String, String> paramDef : getParameters())
674                {
675                        final String name = paramDef.getFirst();
676                        final String description = paramDef.getThird();
677                        final String value = reportParameters.get(name);
678                        out.append("<tr><td>").append(name).append("</td><td>").append(value).append("</td><td>").append(description).append("</td></tr>");
679                }
680                out.append("</table>");
681
682                out.append("<h2>PER LINK INFORMATION SUMMARY - Signal metrics at the input of end OADM</h2>");
683                out.append("<table border='1'>");
684                out.append("<tr><th><b>Link #</b></th><th><b>Length (km)</b></th><th><b># EDFAs</b></th><th><b># PCs</b></th><th><b>OSNR total (dB)</b></th>"
685                                + "<th><b>Power per WDM channel (dBm)</b></th><th><b>Polarization Mode Dispersion (ps)</b></th><th><b>Warnings</b></th></tr>");
686
687                for (Link e : netPlan.getLinks())
688                {
689                        final double d_e = e.getLengthInKm();
690                        final String st_a_e = e.getOriginNode().getName();
691                        final String st_b_e = e.getDestinationNode().getName();
692                        final List<Quadruple<Double, String, Double, String>> el = elements_e.get(e);
693                        final List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> imp = impairments_e.get(e);
694                        final List<String> w = warnings_e.get(e);
695
696                        int numEDFAs = 0;
697                        for (Quadruple<Double, String, Double, String> t : el)
698                                if (t.getSecond().equalsIgnoreCase("EDFA"))
699                                        numEDFAs++;
700
701                        int numPCs = 0;
702                        for (Quadruple<Double, String, Double, String> t : el)
703                                if (t.getSecond().equalsIgnoreCase("PC"))
704                                        numPCs++;
705
706                        final Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double> impInfoInputOADM = imp.get(imp.size() - 1);
707                        final Map<String, double[]> prevSpectrum = impInfoInputOADM.getFirst();
708                        final double totalOSNR_dB = OpticalImpairmentUtils.getOSNR(prevSpectrum).getThird()[centralChannel];
709
710                        final StringBuilder warnings = new StringBuilder();
711                        for (String s : w)
712                                warnings.append(s);
713
714                        out.append("<tr><td>").append(e).append(" (").append(st_a_e).append(" --> ").append(st_b_e).append(") </td><td>").append(df_2.format(d_e)).append("</td><td>").append(numEDFAs).append("</td><td>").append(numPCs).append("</td><td>")
715                                        .append((totalOSNR_dB > infinityThreshold_dB) ? "&infin;" : df_2.format(totalOSNR_dB)).append("</td><td>")
716                                        .append(df_2.format(OpticalImpairmentUtils.linear2dB(prevSpectrum.get(OpticalImpairmentUtils.stSpectrum_powerPerChannel_W)[centralChannel] * 1e3))).append("</td><td>").append(df_2.format(Math.sqrt(impInfoInputOADM.getSecond())))
717                                        .append("</td><td>").append(warnings).append("</td></tr>");
718                }
719                out.append("</table>");
720
721                out.append("<h2>PER ROUTE INFORMATION SUMMARY - Signal metrics at the input of last OADM</h2>");
722                out.append("<table border='1'>");
723                out.append("<tr><th><b>Route #</b></th><th><b>Length (km)</b></th><th><b># EDFAs</b></th><th><b># PCs</b></th>"
724                                + "<th><b>OSNR total (dB)</b></th><th><b>Power per WDM channel (dBm)</b></th><th><b>Polarization Mode Dispersion (ps)</b></th><th><b>Warnings</b></th></tr>");
725                for (Route r : netPlan.getRoutes())
726                {
727                        final double d_r = r.getLengthInKm();
728                        final String st_a_r = r.getIngressNode().getName();
729                        final String st_b_r = r.getEgressNode().getName();
730                        final List<Quadruple<Double, String, Double, String>> el = elements_r.get(r);
731                        final List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> imp = impairments_r.get(r);
732                        final List<String> w = warnings_r.get(r);
733
734                        int numEDFAs = 0;
735                        for (Quadruple<Double, String, Double, String> t : el)
736                                if (t.getSecond().equalsIgnoreCase("EDFA"))
737                                        numEDFAs++;
738
739                        int numPCs = 0;
740                        for (Quadruple<Double, String, Double, String> t : el)
741                                if (t.getSecond().equalsIgnoreCase("PC"))
742                                        numPCs++;
743
744                        final Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double> impInfoInputOADM = imp.get(imp.size() - 1);
745                        final Map<String, double[]> preSpectrum = impInfoInputOADM.getFirst();
746                        final double totalOSNR_dB = OpticalImpairmentUtils.getOSNR(preSpectrum).getThird()[centralChannel];
747
748                        final StringBuilder warnings = new StringBuilder();
749                        for (String s : w)
750                                warnings.append(s);
751
752                        out.append("<tr><td>").append(r).append(" (").append(st_a_r).append(" --> ").append(st_b_r).append(") </td><td>").append(df_2.format(d_r)).append("</td><td>").append(numEDFAs).append("</td><td>").append(numPCs).append("</td><td>")
753                                        .append((totalOSNR_dB > infinityThreshold_dB) ? "&infin;" : df_2.format(totalOSNR_dB)).append("</td><td>")
754                                        .append(df_2.format(OpticalImpairmentUtils.linear2dB(preSpectrum.get(OpticalImpairmentUtils.stSpectrum_powerPerChannel_W)[centralChannel] * 1e3))).append("</td><td>").append(df_2.format(Math.sqrt(impInfoInputOADM.getSecond())))
755                                        .append("</td><td>").append(warnings.toString()).append("</td>" + "</tr>");
756
757                }
758                out.append("</table>");
759
760                out.append("<h2>PER-LINK DETAILED INFORMATION </h2>");
761                out.append("<p>Number of links: ").append(netPlan.getNumberOfLinks()).append("</p>");
762
763                for (Link e : netPlan.getLinks())
764                {
765                        final double d_e = e.getLengthInKm();
766                        final String st_a_e = e.getOriginNode().getName();
767                        final String st_b_e = e.getDestinationNode().getName();
768                        final List<Quadruple<Double, String, Double, String>> el = elements_e.get(e);
769                        final List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> imp = impairments_e.get(e);
770                        final List<String> w = warnings_e.get(e);
771                        final String st_edfaPositions_km = e.getAttribute("edfaPositions_km") == null ? "" : e.getAttribute("edfaPositions_km");
772                        final String st_edfaGains_dB = e.getAttribute("edfaGains_dB") == null ? "" : e.getAttribute("edfaGains_dB");
773                        final String st_edfaNoiseFigures_dB = e.getAttribute("edfaNoiseFigures_dB") == null ? "" : e.getAttribute("edfaNoiseFigures_dB");
774                        final String st_pcPositions_km = e.getAttribute("pcPositions_km") == null ? "" : e.getAttribute("pcPositions_km");
775                        final String st_pcLosses_dB = e.getAttribute("pcLosses_dB") == null ? "" : e.getAttribute("pcLosses_dB");
776
777                        out.append("<h3>LINK # ").append(e).append(" (").append(st_a_e).append(" --> ").append(st_b_e).append(")</h3>");
778                        out.append("<table border=\"1\">");
779                        out.append("<caption>Link information</caption>");
780                        out.append("<tr><td>Link length (km)</td><td>").append(d_e).append("</td></tr>");
781                        out.append("<tr><td>EDFA positions (km)</td><td>").append(st_edfaPositions_km).append("</td></tr>");
782                        out.append("<tr><td>EDFA gains (dB)</td><td>").append(st_edfaGains_dB).append("</td></tr>");
783                        out.append("<tr><td>EDFA Noise Figures (dB)</td><td>").append(st_edfaNoiseFigures_dB).append("</td></tr>");
784                        out.append("<tr><td>PC positions (km)</td><td>").append(st_pcPositions_km).append("</td></tr>");
785                        out.append("<tr><td>PC losses (dB)</td><td>").append(st_pcLosses_dB).append("</td></tr>");
786                        out.append("</table>");
787
788                        out.append("<table border=\"1\">");
789                        out.append("<caption>Signal metrics evolution at the output of each element.</caption>");
790                        out.append("<tr><th><b>Position (km)</b></th><th><b>Position (description)</b></th><th><b>OSNR total(dB)</b></th>"
791                                        + "<th><b>Power per WDM channel (dBm)</b></th><th><b>Polarization Mode Dispersion (ps)</b></th><th><b>Warnings</b></th></tr>");
792
793                        final Iterator<Quadruple<Double, String, Double, String>> it_el = el.iterator();
794                        final Iterator<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> it_imp = imp.iterator();
795                        final Iterator<String> it_w = w.iterator();
796                        while (it_el.hasNext())
797                        {
798                                final Quadruple<Double, String, Double, String> this_el = it_el.next();
799                                final Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double> this_imp = it_imp.next();
800                                final String this_warnings = it_w.next();
801
802                                final double pos_km = this_el.getFirst();
803                                String elementType = this_el.getSecond();
804                                final double elementData = this_el.getThird();
805                                final String elementAuxData = this_el.getFourth();
806
807                                final Map<String, double[]> postSpectrum = this_imp.getThird();
808                                final double totalOSNR_dB = OpticalImpairmentUtils.getOSNR(postSpectrum).getThird()[centralChannel];
809
810                                if (elementType.equalsIgnoreCase("EDFA"))
811                                        elementType += " (G: " + elementData + " dB, F: " + elementAuxData + " dB)";
812                                else if (elementType.equalsIgnoreCase("SPAN"))
813                                        elementType += " (Type: " + elementAuxData + ", " + elementData + " km)";
814                                else if (elementType.equalsIgnoreCase("PC"))
815                                        elementType += " (L: " + elementData + " dB)";
816
817                                out.append("<tr><td>").append(df_2.format(pos_km)).append("</td><td>" + "Output of ").append(elementType).append("</td><td>").append((totalOSNR_dB > infinityThreshold_dB) ? "&infin;" : df_2.format(totalOSNR_dB)).append("</td><td>")
818                                                .append(df_2.format(OpticalImpairmentUtils.linear2dB(postSpectrum.get(OpticalImpairmentUtils.stSpectrum_powerPerChannel_W)[centralChannel] * 1e3))).append("</td><td>").append(df_2.format(Math.sqrt(this_imp.getSecond())))
819                                                .append("</td><td>").append(this_warnings).append("</td>" + "</tr>");
820
821                        }
822                        out.append("</table>");
823                }
824
825                out.append("<h2>PER-LIGHTPATH DETAILED INFORMATION</h2>");
826                out.append("<p>Number of lightpaths: ").append(netPlan.getNumberOfRoutes()).append("</p>");
827
828                for (Route r : netPlan.getRoutes())
829                {
830                        final double d_r = r.getLengthInKm();
831                        final String st_a_r = r.getIngressNode().getName();
832                        final String st_b_r = r.getEgressNode().getName();
833                        final List<Quadruple<Double, String, Double, String>> el = elements_r.get(r);
834                        final List<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> imp = impairments_r.get(r);
835                        final List<String> w = warnings_r.get(r);
836
837                        out.append("<h3>ROUTE # ").append(r).append(" (").append(st_a_r).append(" --> ").append(st_b_r).append("), Length: ").append(d_r).append(" km</h3>");
838                        out.append("<table border=\"1\">");
839                        out.append("<caption>Signal metrics evolution</caption>");
840                        out.append("<tr><th><b>Position (km)</b></th><th><b>Position (description)</b></th><th><b>OSNR total (dB)</b></th>"
841                                        + "<th><b>Power per WDM channel (dBm)</b></th><th><b>Polarization Mode Dispersion (ps)</b></th><th><b>Warnings</b></th></tr>");
842
843                        final Iterator<Quadruple<Double, String, Double, String>> it_el = el.iterator();
844                        final Iterator<Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double>> it_imp = imp.iterator();
845                        final Iterator<String> it_w = w.iterator();
846                        while (it_el.hasNext())
847                        {
848                                final Quadruple<Double, String, Double, String> this_el = it_el.next();
849                                final Quadruple<Map<String, double[]>, Double, Map<String, double[]>, Double> this_imp = it_imp.next();
850                                final String this_warnings = it_w.next();
851
852                                final double pos_km = this_el.getFirst();
853                                String elementType = this_el.getSecond();
854                                final double elementData = this_el.getThird();
855                                final String elementAuxData = this_el.getFourth();
856
857                                final Map<String, double[]> postSpectrum = this_imp.getThird();
858                                final double totalOSNR_dB = OpticalImpairmentUtils.getOSNR(postSpectrum).getThird()[centralChannel];
859
860                                if (elementType.equalsIgnoreCase("EDFA"))
861                                        elementType += " (G: " + elementData + " dB, NF: " + elementAuxData + " dB)";
862                                else if (elementType.equalsIgnoreCase("SPAN"))
863                                        elementType += " (Type: " + elementAuxData + ", l:" + elementData + " km)";
864                                else if (elementType.equalsIgnoreCase("PC"))
865                                        elementType += " (L: " + elementData + " dB)";
866
867                                out.append("<tr><td>").append(df_2.format(pos_km)).append("</td><td>" + "Output of ").append(elementType).append("</td><td>").append((totalOSNR_dB > infinityThreshold_dB) ? "&infin;" : df_2.format(totalOSNR_dB)).append("</td><td>")
868                                                .append(df_2.format(OpticalImpairmentUtils.linear2dB(postSpectrum.get(OpticalImpairmentUtils.stSpectrum_powerPerChannel_W)[centralChannel] * 1e3))).append("</td><td>").append(df_2.format(Math.sqrt(this_imp.getSecond())))
869                                                .append("</td><td>").append(this_warnings).append("</td>" + "</tr>");
870
871                        }
872
873                        out.append("</table>");
874                }
875
876                out.append("</body></html>");
877                return out.toString();
878        }
879
880}