001package com.net2plan.examples.general.onlineSim;
002/*******************************************************************************
003 * Copyright (c) 2017 Pablo Pavon Marino and others.
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the 2-clause BSD License 
006 * which accompanies this distribution, and is available at
007 * https://opensource.org/licenses/BSD-2-Clause
008 *
009 * Contributors:
010 *     Pablo Pavon Marino and others - initial API and implementation
011 *******************************************************************************/
012
013
014import java.util.ArrayList;
015import java.util.HashMap;
016import java.util.HashSet;
017import java.util.List;
018import java.util.Map;
019import java.util.Random;
020import java.util.Set;
021import java.util.stream.Collectors;
022
023import com.net2plan.interfaces.networkDesign.Demand;
024import com.net2plan.interfaces.networkDesign.Link;
025import com.net2plan.interfaces.networkDesign.Net2PlanException;
026import com.net2plan.interfaces.networkDesign.NetPlan;
027import com.net2plan.interfaces.networkDesign.NetworkElement;
028import com.net2plan.interfaces.networkDesign.NetworkLayer;
029import com.net2plan.interfaces.networkDesign.Node;
030import com.net2plan.interfaces.networkDesign.Route;
031import com.net2plan.interfaces.networkDesign.SharedRiskGroup;
032import com.net2plan.interfaces.simulation.IEventProcessor;
033import com.net2plan.interfaces.simulation.SimEvent;
034import com.net2plan.libraries.GraphUtils;
035import com.net2plan.libraries.SRGUtils;
036import com.net2plan.libraries.WDMUtils;
037import com.net2plan.libraries.WDMUtils.TransponderTypesInfo;
038import com.net2plan.utils.Constants.RoutingType;
039import com.net2plan.utils.InputParameter;
040import com.net2plan.utils.Pair;
041import com.net2plan.utils.Quadruple;
042import com.net2plan.utils.RandomUtils;
043import com.net2plan.utils.Triple;
044
045import cern.colt.matrix.tdouble.DoubleMatrix2D;
046import cern.jet.math.tdouble.DoubleFunctions;
047
048/** Implements the reactions of a WDM network carrying lightpaths in a fixed or flexi grid of wavelengths. 
049 * 
050 * <p>The design follows the assumptions described in {@link com.net2plan.libraries.WDMUtils WDMUtils} Net2Plan library</p>
051 * <p>This algorithm implements the reactions of a WDM network carrying lightpaths, to the following events:</p> 
052 * <ul>
053 * <li>{@link com.net2plan.libraries.WDMUtils.LightpathAdd WDMUtils.LightpathAdd}: Adds the corresponding lightpath to the network 
054 * (the Route object and potentially a ProtectionSegment object if the lightpath is asked to be 1+1 protected), 
055 * if enough resources exist for it. If the event includes a Demand object, the lightpath is associated to it. 
056 * If not, a new demand is created. The object establishes the line rate of the lightpath to establish. If such line rate is 
057 * not present in any transponder type defined, an exception is raised (on valid transponders exist for such a lightpath).
058 * If the event includes the RSA (also of the backup, if 1+1 protection is selected), the algorithm 
059 * tries this RSA. If not, the user-selected strategy for the routing and spectrum assignment of the lightpath is applied.
060 * Such strategy is tried first with the first transponder type (in order) with the appropriate line rate. If a valid RSA is not found 
061 * it is repeated with the next. The lightpath request is blocked if under no transponder type of the appropriate line rate, a RSA 
062 * is found (note that different transponders can have e.g. different optical reaches or number of occupied slots).</li>
063 * <li>{@link com.net2plan.libraries.WDMUtils.LightpathModify WDMUtils.LightpathModify}: Modifies the carried traffic and/or the RSA of a lightpath.</li>
064 * <li>{@link com.net2plan.libraries.WDMUtils.LightpathRemove WDMUtils.LightpathRemove}: Removes the corresponding lightpath (including the Demand and Route objects, and potentially the 1+1 segment if any), releasing the resources.</li>
065 * <li>{@link com.net2plan.interfaces.simulation.SimEvent.NodesAndLinksChangeFailureState SimEvent.NodesAndLinksChangeFailureState}: Fails/repairs the indicated nodes and/or links, and reacts to such failures 
066 * (the particular form depends on the network recovery options selected).</li>
067 * <li>{@link com.net2plan.interfaces.simulation.SimEvent.DemandModify SimEvent.DemandModify}: Modifies the offered traffic of a demand.</li>
068 * <li>{@link com.net2plan.interfaces.simulation.SimEvent.DemandRemove SimEvent.DemandRemove}: Removes a demand, and all its associated lightpaths if any, releasing the resources.</li>
069 * </ul>
070 * 
071 * This module can be used in conjunction with the {@code Online_evGen_wdm} generator for creating the events to which 
072 * this module reacts. 
073 * 
074 * See in {@link com.net2plan.libraries.WDMUtils} Javadoc the WDM technology conventions used in Net2Plan built-in algorithms and libraries to represent WDM networks. 
075 * @net2plan.keywords WDM, Network recovery: protection, Network recovery: restoration
076 * @net2plan.inputParameters 
077 */
078public class Online_evProc_wdm extends IEventProcessor
079{
080        private InputParameter wdmNumFrequencySlotsPerFiber = new InputParameter ("wdmNumFrequencySlotsPerFiber", (int) 40 , "Set the number of frequency slots per fiber. If < 1, the number of slots set in the input file is used.");
081        private InputParameter wdmRwaType = new InputParameter ("wdmRwaType", "#select# srg-disjointness-aware-route-first-fit alternate-routing least-congested-routing load-sharing" , "Criteria to decide the route of a connection among the available paths");
082        private InputParameter wdmK = new InputParameter ("wdmK", (int) 5 , "Maximum number of admissible paths per demand in the candidate list computation" , 1 , Integer.MAX_VALUE);
083        private InputParameter wdmRandomSeed = new InputParameter ("wdmRandomSeed", (long) 1 , "Seed for the random generator (-1 means random)");
084        private InputParameter wdmMaxLightpathNumHops = new InputParameter ("wdmMaxLightpathNumHops", (int) -1 , "A lightpath cannot have more than this number of hops. A non-positive number means this limit does not exist");
085//      private InputParameter wdmRecoveryType = new InputParameter ("wdmRecoveryType", "#select# protection restoration none" , "None, nothing is done, so affected routes fail. Restoration, affected routes are visited sequentially, and we try to reroute them in the available capacity; in protection, affected routes are rerouted using the protection segments.");
086        private InputParameter wdmRemovePreviousLightpaths = new InputParameter ("wdmRemovePreviousLightpaths", false  , "If true, previous lightpaths are removed from the system during initialization.");
087        //private InputParameter wdmProtectionTypeToNewRoutes = new InputParameter ("wdmProtectionTypeToNewRoutes", "#select# none 1+1-link-disjoint 1+1-node-disjoint 1+1-srg-disjoint" , "New lightpaths are not protected, or are protected by a 1+1 link disjoint, or a node disjoint or a SRG disjoint lightpath");
088        private InputParameter wdmDefaultAndNewRouteRevoveryType = new InputParameter ("wdmDefaultAndNewRouteRevoveryType", "#select# none restoration 1+1-link-disjoint 1+1-node-disjoint 1+1-srg-disjoint" , "New lightpaths are not protected, or are protected by a 1+1 link disjoint, or a node disjoint or a SRG disjoint lightpath");
089        private InputParameter wdmTransponderTypesInfo = new InputParameter ("wdmTransponderTypesInfo", "10 1 1 9600 1" , "Transpoder types separated by \";\" . Each type is characterized by the space-separated values: (i) Line rate in Gbps, (ii) cost of the transponder, (iii) number of slots occupied in each traversed fiber, (iv) optical reach in km (a non-positive number means no reach limit), (v) cost of the optical signal regenerator (regenerators do NOT make wavelength conversion ; if negative, regeneration is not possible).");
090
091        private NetworkLayer wdmLayer;
092        //private Map<Route,Pair<WDMUtils.RSA,WDMUtils.RSA>> wdmRouteOriginalRwa;
093        private Map<Pair<Node,Node>,List<List<Link>>> cplWdm;
094        private Map<Pair<Node,Node>,List<Pair<List<Link>,List<Link>>>> cplWdm11;
095        private DoubleMatrix2D wavelengthFiberOccupancy;
096        private TransponderTypesInfo tpInfo;
097        private Map<Route,Integer> transponderTypeOfNewLps;
098
099//      private boolean newRoutesHave11Protection;
100        private boolean isRestorationRecovery , isProtectionRecovery;
101        private boolean isAlternateRouting , isLeastCongestedRouting , isLoadSharing , isSrgDisjointAwareLpRouting;
102        private Random rng;
103        private int protectionTypeCode;
104        private Demand.IntendedRecoveryType defaultRecoveryType;
105
106        private double stat_trafficOfferedConnections , stat_trafficCarriedConnections;
107        private double stat_trafficAttemptedToRecoverConnections , stat_trafficSuccessfullyRecoveredConnections;
108        private long stat_numOfferedConnections , stat_numCarriedConnections;
109        private long stat_numAttemptedToRecoverConnections , stat_numSuccessfullyRecoveredConnections;
110        private double stat_transitoryInitTime;
111        public boolean DEBUG = false;
112        
113        @Override
114        public String getDescription()
115        {
116                return "Implements the reactions of a WDM network carrying lightpaths in a fixed grid of wavelengths";
117        }
118        
119        @Override
120        public List<Triple<String, String, String>> getParameters()
121        {
122                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
123                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
124        }
125
126        @Override
127        public void initialize(NetPlan initialNetPlan, Map<String, String> algorithmParameters, Map<String, String> simulationParameters, Map<String, String> net2planParameters)
128        {
129                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
130                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
131                
132                this.wdmLayer = initialNetPlan.getNetworkLayer("WDM"); if (wdmLayer == null) throw new Net2PlanException ("WDM layer not found");
133                this.isRestorationRecovery = wdmDefaultAndNewRouteRevoveryType.getString ().equalsIgnoreCase("restoration");
134                this.isProtectionRecovery = wdmDefaultAndNewRouteRevoveryType.getString ().startsWith("1+1");
135                this.isAlternateRouting = wdmRwaType.getString().equalsIgnoreCase("alternate-routing");
136                this.isLeastCongestedRouting = wdmRwaType.getString().equalsIgnoreCase("least-congested-routing");
137                this.isSrgDisjointAwareLpRouting =      wdmRwaType.getString().equalsIgnoreCase("srg-disjointness-aware-route-first-fit");;
138                this.isLoadSharing = wdmRwaType.getString().equalsIgnoreCase("load-sharing");
139                this.rng = new Random(wdmRandomSeed.getLong () == -1? (long) RandomUtils.random(0, Long.MAX_VALUE - 1) : wdmRandomSeed.getLong ());
140                
141                this.defaultRecoveryType = isProtectionRecovery? Demand.IntendedRecoveryType.PROTECTION_REVERT : isRestorationRecovery? Demand.IntendedRecoveryType.RESTORATION : Demand.IntendedRecoveryType.NONE;
142
143                if (wdmRemovePreviousLightpaths.getBoolean())
144                {
145                        initialNetPlan.removeAllRoutes(wdmLayer);
146                        initialNetPlan.removeAllMulticastTrees(wdmLayer);
147                }
148                if (wdmNumFrequencySlotsPerFiber.getInt() > 0) WDMUtils.setFibersNumFrequencySlots(initialNetPlan , wdmNumFrequencySlotsPerFiber.getInt() , wdmLayer);
149                
150                /* Get the transponders information */
151                this.tpInfo = new TransponderTypesInfo(wdmTransponderTypesInfo.getString());
152                this.transponderTypeOfNewLps = new HashMap<Route,Integer> ();
153
154                /* Create empty candidate path lists: they will be filled on demand */
155                this.cplWdm = new HashMap<> ();
156                this.protectionTypeCode = wdmDefaultAndNewRouteRevoveryType.getString ().equals("1+1-srg-disjoint") ? 0 : wdmDefaultAndNewRouteRevoveryType.getString ().equals("1+1-node-disjoint")? 1 : 2;
157                this.cplWdm11 = isProtectionRecovery? new HashMap<> () : null; 
158                
159                this.wavelengthFiberOccupancy = WDMUtils.getNetworkSlotAndRegeneratorOcupancy(initialNetPlan, true , wdmLayer).getFirst();
160                if (DEBUG) { checkWaveOccupEqualsNp(initialNetPlan); checkClashing (initialNetPlan); } 
161                initialNetPlan.setLinkCapacityUnitsName("Frequency slots" , wdmLayer);
162
163                this.finishTransitory(0);
164                if (DEBUG) { checkWaveOccupEqualsNp(initialNetPlan); checkClashing (initialNetPlan); } 
165        }
166
167        @Override
168        public void processEvent(NetPlan currentNetPlan, SimEvent event)
169        {
170                try {
171                        if (event.getEventObject () instanceof WDMUtils.LightpathAdd)
172                        {
173                                WDMUtils.LightpathAdd addLpEvent = (WDMUtils.LightpathAdd) event.getEventObject ();
174                                if (addLpEvent.layer != this.wdmLayer) throw new Net2PlanException ("Lightpaths cannot be added to layer " + addLpEvent.demand.getLayer() + ", and just to the WDM layer (" + wdmLayer + ")");
175                                final Node ingressNode = addLpEvent.ingressNode;
176                                final Node egressNode = addLpEvent.egressNode;
177                                final Pair<Node,Node> cplNodePair = Pair.of(ingressNode,egressNode);
178                                final double lineRateThisLp_Gbps = addLpEvent.lineRateGbps;
179                                if (!tpInfo.isValidLineRateForAtLeastOneType (lineRateThisLp_Gbps)) throw new Net2PlanException ("Requested to set up a lightpath of a line rate (" + lineRateThisLp_Gbps + ") for which I don't have transpoders");
180
181                                /* update the offered traffic of the demand */
182                                this.stat_numOfferedConnections ++;
183                                this.stat_trafficOfferedConnections += lineRateThisLp_Gbps;
184                                
185                                /* Computes one or two paths over the links (the second path would be a segment). You cannot use the already existing segments in these paths */
186                                if (isProtectionRecovery)
187                                {
188                                        /* The RWA may be computed by me, or mandated by the event */
189                                        Pair<WDMUtils.RSA,WDMUtils.RSA> rwa = null;
190                                        int transponderTypeUsed = -1;
191                                        if ((addLpEvent.primaryRSA != null) && (addLpEvent.backupRSA != null))
192                                        {
193                                                if (!WDMUtils.isAllocatableRSASet(wavelengthFiberOccupancy , addLpEvent.primaryRSA , addLpEvent.backupRSA))
194                                                        throw new Net2PlanException ("A request was received to add a conflicting lightpath 1+1 pair");
195                                                rwa = Pair.of (addLpEvent.primaryRSA , addLpEvent.backupRSA);
196                                        }
197                                        else
198                                        {
199                                                /* Check among the transponder types (in order), the one in the same rate that fits (optical reach in range). */
200                                                for (int t = 0; t < tpInfo.getNumTypes() ; t ++)
201                                                {
202                                                        if (lineRateThisLp_Gbps != tpInfo.getLineRateGbps(t)) continue;
203                                                        rwa = computeValid11PathPairNewRoute(cplNodePair , currentNetPlan , tpInfo.getNumSlots(t) , tpInfo.getOpticalReachKm(t) , tpInfo.isOpticalRegenerationPossible(t));
204                                                        if (rwa != null) { transponderTypeUsed = t; break; }
205                                                }
206                                        }
207
208                                        if (rwa != null)
209                                        {
210                                                if (!WDMUtils.isAllocatableRSASet(wavelengthFiberOccupancy , rwa.getFirst() , rwa.getSecond())) throw new RuntimeException ("Bad");
211                                                if (!WDMUtils.isAllocatableRSASet(wavelengthFiberOccupancy , rwa.getSecond())) throw new RuntimeException ("Bad");
212                                                for (Link e : rwa.getFirst ().seqLinks) if (e.getNetPlan() != currentNetPlan) throw new RuntimeException ("Bad. e.getNetPlan is null?: " + (e.getNetPlan() ==null) + ", currentNp is null: " + (currentNetPlan == null));
213                                                for (Link e : rwa.getSecond ().seqLinks) if (e.getNetPlan() != currentNetPlan) throw new RuntimeException ("Bad");
214
215                                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
216                                                
217                                                final Demand wdmLayerDemand = addLpEvent.demand == null? currentNetPlan.addDemand(addLpEvent.ingressNode, addLpEvent.egressNode, lineRateThisLp_Gbps , RoutingType.SOURCE_ROUTING , null, wdmLayer) : addLpEvent.demand;
218                                                wdmLayerDemand.setIntendedRecoveryType(Demand.IntendedRecoveryType.PROTECTION_REVERT);
219                                                final Route wdmLayerRoute = WDMUtils.addLightpath(wdmLayerDemand, rwa.getFirst(), lineRateThisLp_Gbps);
220                                                WDMUtils.allocateResources(rwa.getFirst() , wavelengthFiberOccupancy , null);
221
222                                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
223
224                                                if (rwa.getFirst().seqLinks.equals(rwa.getSecond().seqLinks)) throw new RuntimeException ("Both 1+1 same route");
225                                                final Route wdmLayerBackupRoute = WDMUtils.addLightpath(wdmLayerDemand, rwa.getSecond(), 0); // it is a backup, has no traffic carried then
226                                                wdmLayerRoute.addBackupRoute(wdmLayerBackupRoute);
227                                                WDMUtils.allocateResources(rwa.getSecond() , wavelengthFiberOccupancy , null);
228                                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
229                                                checkDisjointness(wdmLayerRoute.getSeqLinks() , rwa.getSecond().seqLinks , protectionTypeCode);
230                                                this.transponderTypeOfNewLps.put(wdmLayerRoute , transponderTypeUsed);
231                                                this.stat_numCarriedConnections ++;
232                                                this.stat_trafficCarriedConnections += lineRateThisLp_Gbps;
233                                                addLpEvent.lpAddedToFillByProcessor = wdmLayerRoute;
234                                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
235                                        }
236                                        if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
237                                }
238                                else
239                                {
240                                        /* The RWA may be computed by me, or mandated by the event */
241                                        WDMUtils.RSA rwa = null;
242                                        int transponderTypeUsed = -1;
243                                        if (addLpEvent.primaryRSA != null)
244                                        {
245                                                if (!WDMUtils.isAllocatableRSASet(wavelengthFiberOccupancy , addLpEvent.primaryRSA))
246                                                        throw new Net2PlanException ("A request was received to add a conflicting lightpath");
247                                                rwa = addLpEvent.primaryRSA; 
248                                        }
249                                        else
250                                        {
251                                                /* Check among the transponder types (in order), the one in the same rate that fits (optical reach in range). */
252                                                for (int t = 0; t < tpInfo.getNumTypes() ; t ++)
253                                                {
254                                                        if (lineRateThisLp_Gbps != tpInfo.getLineRateGbps(t)) continue;
255                                                        rwa = computeValidPathNewRoute(cplNodePair , currentNetPlan , tpInfo.getNumSlots(t) , tpInfo.getOpticalReachKm(t) , tpInfo.isOpticalRegenerationPossible(t));
256                                                        if (rwa != null) { transponderTypeUsed = t; break; }
257                                                }
258                                        }
259                                        if (rwa != null)
260                                        {
261                                                final Demand wdmLayerDemand = addLpEvent.demand == null? currentNetPlan.addDemand(addLpEvent.ingressNode, addLpEvent.egressNode, lineRateThisLp_Gbps , RoutingType.SOURCE_ROUTING , null, wdmLayer) : addLpEvent.demand;
262                                                wdmLayerDemand.setIntendedRecoveryType(isRestorationRecovery? Demand.IntendedRecoveryType.RESTORATION : Demand.IntendedRecoveryType.NONE);
263                                                final Route wdmLayerRoute = WDMUtils.addLightpath(wdmLayerDemand, rwa , lineRateThisLp_Gbps);
264                                                WDMUtils.allocateResources(rwa , wavelengthFiberOccupancy , null);
265                                                this.transponderTypeOfNewLps.put(wdmLayerRoute , transponderTypeUsed);
266                                                this.stat_numCarriedConnections ++;
267                                                this.stat_trafficCarriedConnections += lineRateThisLp_Gbps;
268                                                addLpEvent.lpAddedToFillByProcessor = wdmLayerRoute;
269                                        }
270                                }
271                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
272                        }
273                        else if (event.getEventObject () instanceof WDMUtils.LightpathRemove)
274                        {
275                                WDMUtils.LightpathRemove lpEvent = (WDMUtils.LightpathRemove) event.getEventObject ();
276                                final Route lpToRemove = lpEvent.lp;
277                                WDMUtils.releaseResources(new WDMUtils.RSA(lpToRemove , false) , wavelengthFiberOccupancy, null);
278                                for (Route backupLp : new ArrayList<> (lpToRemove.getBackupRoutes()))
279                                {
280                                        WDMUtils.releaseResources(new WDMUtils.RSA(backupLp , false), wavelengthFiberOccupancy, null);
281                                        backupLp.remove();
282                                        this.transponderTypeOfNewLps.remove(backupLp);
283                                }
284                                lpToRemove.remove (); 
285                                this.transponderTypeOfNewLps.remove(lpToRemove);
286                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
287                        } else if (event.getEventObject () instanceof SimEvent.NodesAndLinksChangeFailureState)
288                        {
289                                SimEvent.NodesAndLinksChangeFailureState ev = (SimEvent.NodesAndLinksChangeFailureState) event.getEventObject ();
290
291                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
292
293                                /* This automatically sets as up the routes affected by a repair in its current path, and sets as down the affected by a failure in its current path */
294                                Set<Route> routesFromDownToUp = new HashSet<> (currentNetPlan.getRoutesDown(wdmLayer));
295                                currentNetPlan.setLinksAndNodesFailureState(ev.linksToUp , ev.linksToDown , ev.nodesToUp , ev.nodesToDown);
296                                routesFromDownToUp.removeAll(currentNetPlan.getRoutesDown(wdmLayer));
297
298                                /* If primary GOES up in PROTECTION-REVERT => backup carried is set to zero in all backups */ 
299                                for (Route r : routesFromDownToUp)
300                                {
301                                        final Demand wdmDemand = r.getDemand();
302                                        final Demand.IntendedRecoveryType recoveryType = wdmDemand.getIntendedRecoveryType() == Demand.IntendedRecoveryType.NOTSPECIFIED? defaultRecoveryType : wdmDemand.getIntendedRecoveryType();
303                                        if (recoveryType ==  Demand.IntendedRecoveryType.PROTECTION_REVERT)
304                                        for (Route backup : r.getBackupRoutes())
305                                                backup.setCarriedTraffic(0, null); // primary to up => carried in backup to zero
306                                }
307
308                                /* Now take down routes one by one, and see what to do with them (if something)  */ 
309                                for (Route r : new ArrayList<> (currentNetPlan.getRoutesDown(wdmLayer)))
310                                {
311                                        final Demand wdmDemand = r.getDemand();
312                                        final Demand.IntendedRecoveryType recovType = wdmDemand.getIntendedRecoveryType() == Demand.IntendedRecoveryType.NOTSPECIFIED? defaultRecoveryType : wdmDemand.getIntendedRecoveryType();
313
314                                        if (recovType == Demand.IntendedRecoveryType.NONE) continue;
315
316                                        if (recovType == Demand.IntendedRecoveryType.PROTECTION_REVERT)
317                                        {
318                                                /* If is 1+1 protection, do nothing with backup routes that are down */
319                                                if (r.isBackupRoute()) continue;
320                                                this.stat_numAttemptedToRecoverConnections ++;
321                                                this.stat_trafficAttemptedToRecoverConnections += r.getCarriedTrafficInNoFailureState();
322                                                /* The primary routes goes down => its backup has now carried traffic (if no failure) */
323                                                if (r.hasBackupRoutes())
324                                                {
325                                                        final Route backupRoute = r.getBackupRoutes().get(0);
326                                                        backupRoute.setCarriedTraffic(r.getCarriedTrafficInNoFailureState(), null);
327                                                        if (!backupRoute.isDown())
328                                                        {
329                                                                this.stat_numSuccessfullyRecoveredConnections ++; 
330                                                                this.stat_trafficSuccessfullyRecoveredConnections += r.getCarriedTrafficInNoFailureState(); 
331                                                        }
332                                                }
333                                        }
334                                        else if (recovType == Demand.IntendedRecoveryType.RESTORATION)
335                                        {
336                                                this.stat_numAttemptedToRecoverConnections ++;
337                                                this.stat_trafficAttemptedToRecoverConnections += r.getCarriedTrafficInNoFailureState();
338                                                final Pair<Node,Node> cplNodePair= Pair.of(r.getIngressNode() , r.getEgressNode());
339                                                final Integer transponderTypeLp = transponderTypeOfNewLps.get(r);
340                                                final double maxOpticalReachKm = transponderTypeLp == null? Double.MAX_VALUE : tpInfo.getOpticalReachKm(transponderTypeLp);
341                                                final boolean isSignalRegenerationPossible = transponderTypeLp == null? true : tpInfo.isOpticalRegenerationPossible(transponderTypeLp); 
342                                                final int numSlots = transponderTypeLp == null? new WDMUtils.RSA(r,false).getNumSlots() : tpInfo.getNumSlots(transponderTypeLp);
343                                                WDMUtils.RSA rwa = computeValidPathNewRoute (cplNodePair , currentNetPlan , numSlots , maxOpticalReachKm , isSignalRegenerationPossible);
344                                                if (rwa != null)
345                                                { 
346                                                        WDMUtils.releaseResources(new WDMUtils.RSA (r , false) , wavelengthFiberOccupancy, null);
347                                                        WDMUtils.allocateResources(rwa , wavelengthFiberOccupancy , null);
348                                                        r.setSeqLinks(rwa.seqLinks);
349                                                        WDMUtils.setLightpathRSAAttributes(r , rwa , false);
350                                                        
351                                                        this.stat_numSuccessfullyRecoveredConnections ++; 
352                                                        this.stat_trafficSuccessfullyRecoveredConnections += r.getCarriedTrafficInNoFailureState(); 
353                                                }
354                                        }
355                                }
356                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
357                                
358                        } else if (event.getEventObject () instanceof SimEvent.DemandModify)
359                        {
360                                SimEvent.DemandModify ev = (SimEvent.DemandModify) event.getEventObject ();
361                                Demand d = ev.demand; 
362                                if (ev.modificationIsRelativeToCurrentOfferedTraffic) 
363                                        d.setOfferedTraffic(d.getOfferedTraffic() + ev.offeredTraffic);
364                                else
365                                        d.setOfferedTraffic(ev.offeredTraffic);
366                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
367                        } else if (event.getEventObject () instanceof SimEvent.DemandRemove)
368                        {
369                                SimEvent.DemandRemove ev = (SimEvent.DemandRemove) event.getEventObject ();
370                                Demand d = ev.demand;
371                                for (Route lpToRemove : new ArrayList<> (d.getRoutes()))
372                                {
373                                        WDMUtils.releaseResources(new WDMUtils.RSA(lpToRemove , false), wavelengthFiberOccupancy, null);
374                                        lpToRemove.remove();
375                                        transponderTypeOfNewLps.remove(lpToRemove);
376                                }
377                                d.remove();
378                                if (DEBUG) { checkWaveOccupEqualsNp(currentNetPlan); checkClashing (currentNetPlan); } 
379                        }
380                        else if(event.getEventObject() instanceof WDMUtils.LightpathModify)
381                        {
382                                WDMUtils.LightpathModify modifyLpEvent = (WDMUtils.LightpathModify) event.getEventObject();
383                                final Route lpRoute = modifyLpEvent.lp;
384                                if (lpRoute != null) 
385                                {
386                                        WDMUtils.RSA oldRSA = new WDMUtils.RSA(lpRoute , false);
387                                        final Integer tpType = transponderTypeOfNewLps.get (lpRoute);
388                                        if (modifyLpEvent.rsa == null) throw new Net2PlanException ("Modifying the lightpath line rate requires setting the RSA also");
389                                        if (tpType == null) throw new Net2PlanException ("Cannot find the transponder type of the lightpath!");
390                                        if ((modifyLpEvent.carriedTraffic != 0) && (modifyLpEvent.carriedTraffic != tpInfo.getLineRateGbps(tpType))) throw new Net2PlanException ("Cannot change the line rate of an existing lightpath");
391                                        if ((modifyLpEvent.rsa.getNumSlots() != 0) && (modifyLpEvent.rsa.getNumSlots() != tpInfo.getNumSlots(tpType))) throw new Net2PlanException ("Cannot change the number of slots of an existing lightpath");
392                                        if ((modifyLpEvent.rsa.getLengthInKm() > tpInfo.getOpticalReachKm(tpType)) && (!tpInfo.isOpticalRegenerationPossible(tpType))) throw new Net2PlanException ("Cannot modify the lightpath RSA in sucha way that the lightpath length exceeds the transponder maximum reach");
393                                        
394                                        lpRoute.setCarriedTraffic(modifyLpEvent.carriedTraffic, modifyLpEvent.rsa.getNumSlots());
395                                        WDMUtils.releaseResources( oldRSA, wavelengthFiberOccupancy , null);
396                                        WDMUtils.allocateResources(modifyLpEvent.rsa , wavelengthFiberOccupancy , null);
397                                        lpRoute.setSeqLinks(modifyLpEvent.rsa.seqLinks);
398                                        WDMUtils.setLightpathRSAAttributes(lpRoute , modifyLpEvent.rsa , false);
399                                }
400                        }
401                        else throw new Net2PlanException ("Unknown event type: " + event);
402                } catch (Exception e) {
403                        e.printStackTrace();
404                        throw e;
405                }
406        }       
407        @Override
408        public String finish(StringBuilder output, double simTime)
409        {
410                final double trafficBlockingOnConnectionSetup = stat_trafficOfferedConnections == 0? 0 : 1 - (stat_trafficCarriedConnections / stat_trafficOfferedConnections );
411                final double connectionBlockingOnConnectionSetup = stat_numOfferedConnections == 0? 0.0 : 1 - (((double) stat_numCarriedConnections) / ((double) stat_numOfferedConnections));
412                final double successProbabilityRecovery = stat_numAttemptedToRecoverConnections == 0? 0.0 : (((double) stat_numSuccessfullyRecoveredConnections) / ((double) stat_numAttemptedToRecoverConnections));
413                final double successProbabilityTrafficRecovery = stat_trafficAttemptedToRecoverConnections == 0? 0.0 : (((double) stat_trafficSuccessfullyRecoveredConnections) / ((double) stat_trafficAttemptedToRecoverConnections));
414                final double dataTime = simTime - stat_transitoryInitTime;
415                if (dataTime <= 0) { output.append ("<p>No time for data acquisition</p>"); return ""; }
416
417                output.append (String.format("<p>Total traffic of offered connections: number connections %d, total time average traffic: %f</p>", stat_numOfferedConnections, stat_trafficOfferedConnections / dataTime));
418                output.append (String.format("<p>Total traffic of carried connections: number connections %d, total time average traffic: %f</p>", stat_numCarriedConnections, stat_trafficCarriedConnections / dataTime));
419                output.append (String.format("<p>Blocking at connection setup: Probability of blocking a connection: %f, Fraction of blocked traffic: %f</p>", connectionBlockingOnConnectionSetup , trafficBlockingOnConnectionSetup));
420                output.append (String.format("<p>Number connections attempted to recover: %d (summing time average traffic: %f). </p>", stat_numAttemptedToRecoverConnections, stat_trafficAttemptedToRecoverConnections / dataTime));
421                output.append (String.format("<p>Number connections successfully recovered: %d (summing time average traffic: %f). </p>", stat_numSuccessfullyRecoveredConnections, stat_trafficSuccessfullyRecoveredConnections / dataTime));
422                output.append (String.format("<p>Probability of successfully recover a connection: %f. Proability weighted by the connection traffic: %f). </p>", successProbabilityRecovery, successProbabilityTrafficRecovery));
423//              output.append (String.format("<p>Total traffic carried at current state: %f. </p>", -1);
424                return "";
425        }
426
427        @Override
428        public void finishTransitory(double simTime)
429        {
430                this.stat_trafficOfferedConnections = 0;
431                this.stat_trafficCarriedConnections = 0;
432                this.stat_trafficAttemptedToRecoverConnections  = 0;
433                this.stat_trafficSuccessfullyRecoveredConnections = 0;
434                this.stat_numOfferedConnections = 0;
435                this.stat_numCarriedConnections = 0;
436                this.stat_numAttemptedToRecoverConnections = 0;
437                this.stat_numSuccessfullyRecoveredConnections = 0;
438                this.stat_transitoryInitTime = simTime;
439        }
440        
441
442        /* down links or segments cannot be used */
443        private WDMUtils.RSA computeValidPathNewRoute (Pair<Node,Node> cplNodePair , NetPlan currentNetPlan , int numContiguousSlots , double opticalReachKm , boolean isSignalRegenerationPossible)
444        {
445                /* If load sharing */
446                final double maxLightpathLengthKm = isSignalRegenerationPossible? Double.MAX_VALUE : opticalReachKm;
447                if (isLoadSharing)
448                {
449                        final List<List<Link>> paths = getAndUpdateCplWdm(cplNodePair, currentNetPlan);
450                        final int randomChosenIndex = rng.nextInt(paths.size ());
451                        final List<Link> seqLinksThisNetPlan = paths.get(randomChosenIndex);
452                        final Triple<Boolean,Integer,Double> rwaEval = computeValidWAFirstFit_path (seqLinksThisNetPlan , numContiguousSlots , maxLightpathLengthKm);
453                        if (rwaEval.getFirst()) return new WDMUtils.RSA (seqLinksThisNetPlan, rwaEval.getSecond() , numContiguousSlots , WDMUtils.computeRegeneratorPositions(seqLinksThisNetPlan , maxLightpathLengthKm)); else return null;
454                }
455                else if (isAlternateRouting || isLeastCongestedRouting)
456                {
457                        /* If alternate or LCR */
458                        WDMUtils.RSA lcrSoFar = null; double lcrIdleCapacitySoFar = -Double.MAX_VALUE; 
459                        for (List<Link> seqLinks : getAndUpdateCplWdm(cplNodePair, currentNetPlan))
460                        {
461                                final Triple<Boolean,Integer,Double> rwaEval = computeValidWAFirstFit_path (seqLinks , numContiguousSlots , maxLightpathLengthKm);
462                                if (!rwaEval.getFirst()) continue;
463                                if (isAlternateRouting) return new WDMUtils.RSA (seqLinks, rwaEval.getSecond(),numContiguousSlots , WDMUtils.computeRegeneratorPositions(seqLinks , maxLightpathLengthKm));
464                                if (rwaEval.getThird() > lcrIdleCapacitySoFar)
465                                {
466                                        lcrIdleCapacitySoFar = rwaEval.getThird();
467                                        lcrSoFar = new WDMUtils.RSA (seqLinks , rwaEval.getSecond());
468                                }
469                        }
470                        return (isAlternateRouting || (lcrSoFar == null))? (WDMUtils.RSA) null : lcrSoFar; 
471                }
472                else if (isSrgDisjointAwareLpRouting)
473                {
474                        final Node thisNpIngressNode = cplNodePair.getFirst();
475                        final Node thisNpEgressNode = cplNodePair.getSecond();
476                        WDMUtils.RSA chosenRWA = null; 
477                        final Set<Route> existingPrimaryRoutesBetweenPairOfNodes = currentNetPlan.getNodePairRoutes(thisNpIngressNode, thisNpEgressNode , false , wdmLayer);
478                        int cplChosenRoute_numSRGOverlappingRoutes = Integer.MAX_VALUE;
479                        for(List<Link> seqLinks : getAndUpdateCplWdm(cplNodePair, currentNetPlan))
480                        {
481                                /* Check that the route has an available wavlength */
482                                final Triple<Boolean,Integer,Double> rwaEval = computeValidWAFirstFit_path (seqLinks , numContiguousSlots , maxLightpathLengthKm);
483                                if (!rwaEval.getFirst()) continue;
484                                /* compute the number of SRG-route pairs this route overlaps with, respect to the already existing lightpaths  */
485                                Set<SharedRiskGroup> affectingSRGs = SRGUtils.getAffectingSRGs(seqLinks);
486                                int numOverlappingSRGs = 0; 
487                                for (Route currentRoute : existingPrimaryRoutesBetweenPairOfNodes)
488                                        for (SharedRiskGroup srg : currentRoute.getSRGs()) if (affectingSRGs.contains(srg)) numOverlappingSRGs ++;
489                                if (numOverlappingSRGs < cplChosenRoute_numSRGOverlappingRoutes) 
490                                { 
491                                        cplChosenRoute_numSRGOverlappingRoutes = numOverlappingSRGs; 
492                                        chosenRWA = new WDMUtils.RSA (seqLinks , rwaEval.getSecond(),numContiguousSlots , WDMUtils.computeRegeneratorPositions(seqLinks, maxLightpathLengthKm));
493                                        if (numOverlappingSRGs == 0) break; // no need to search more
494                                }
495                        }
496                        return chosenRWA;
497                }
498                throw new RuntimeException ("Bad");
499        }
500
501        private Pair<WDMUtils.RSA,WDMUtils.RSA> computeValid11PathPairNewRoute (Pair<Node,Node> cplNodePair , NetPlan currentNetPlan , int numContigousSlotsOccupies , double opticalReachKm , boolean isSignalRegenerationPossible)
502        {
503                final double maximumLengthOfLighptathKm = isSignalRegenerationPossible? Double.MAX_VALUE : opticalReachKm;
504                
505                /* If load sharing */
506                if (isLoadSharing)
507                {
508                        final List<Pair<List<Link>,List<Link>>> routePairs = getAndUpdateCplWdm11(cplNodePair, currentNetPlan);
509                        final int randomChosenIndex = rng.nextInt(routePairs.size());
510                        final List<Link> seqLinksPrimaryThisNetPlan = routePairs.get(randomChosenIndex).getFirst();
511                        final List<Link> seqLinksBackupThisNetPlan = routePairs.get(randomChosenIndex).getSecond();
512                        final Quadruple<Boolean,Integer,Integer,Double> rwaEval = computeValidWAFirstFit_pathPair (seqLinksPrimaryThisNetPlan , seqLinksBackupThisNetPlan , numContigousSlotsOccupies , maximumLengthOfLighptathKm);
513                        if (rwaEval.getFirst()) 
514                                return Pair.of(new WDMUtils.RSA (seqLinksPrimaryThisNetPlan, rwaEval.getSecond() , numContigousSlotsOccupies , WDMUtils.computeRegeneratorPositions(seqLinksPrimaryThisNetPlan, maximumLengthOfLighptathKm)) , new WDMUtils.RSA (seqLinksBackupThisNetPlan, rwaEval.getThird() , numContigousSlotsOccupies , WDMUtils.computeRegeneratorPositions(seqLinksBackupThisNetPlan , maximumLengthOfLighptathKm))); 
515                        else 
516                                return null;
517                }
518                else if (isAlternateRouting || isLeastCongestedRouting)
519                {
520                        Pair<WDMUtils.RSA,WDMUtils.RSA> lcrSoFar = null; double lcrIdleCapacitySoFar= -Double.MAX_VALUE; 
521                        for (Pair<List<Link>,List<Link>> pathPair : getAndUpdateCplWdm11(cplNodePair, currentNetPlan))
522                        {
523                                final List<Link> seqLinksPrimaryThisNetPlan = pathPair.getFirst();
524                                final List<Link> seqLinksBackupThisNetPlan = pathPair.getSecond();
525                                final Quadruple<Boolean,Integer,Integer,Double> rwaEval = computeValidWAFirstFit_pathPair (seqLinksPrimaryThisNetPlan , seqLinksBackupThisNetPlan , numContigousSlotsOccupies , maximumLengthOfLighptathKm);
526                                if (!rwaEval.getFirst()) continue;
527                                if (isAlternateRouting) return Pair.of(new WDMUtils.RSA (seqLinksPrimaryThisNetPlan, rwaEval.getSecond() , numContigousSlotsOccupies , WDMUtils.computeRegeneratorPositions(seqLinksPrimaryThisNetPlan , maximumLengthOfLighptathKm)) , new WDMUtils.RSA (seqLinksBackupThisNetPlan, rwaEval.getThird() , numContigousSlotsOccupies , WDMUtils.computeRegeneratorPositions(seqLinksBackupThisNetPlan , maximumLengthOfLighptathKm)));
528                                if (rwaEval.getFourth() > lcrIdleCapacitySoFar)
529                                {
530                                        lcrIdleCapacitySoFar = rwaEval.getFourth();
531                                        lcrSoFar = Pair.of(new WDMUtils.RSA (seqLinksPrimaryThisNetPlan , rwaEval.getSecond()) , new WDMUtils.RSA (seqLinksBackupThisNetPlan , rwaEval.getThird()));
532                                }
533                        }
534                        return lcrSoFar; //if alternate, this is null also
535                } else if (isSrgDisjointAwareLpRouting)
536                {
537                        final Node thisNpIngressNode = cplNodePair.getFirst();
538                        final Node thisNpEgressNode = cplNodePair.getSecond();
539                        Pair<WDMUtils.RSA,WDMUtils.RSA> chosenRWA = null; 
540                        Set<Route> existingRoutesBetweenPairOfNodes = currentNetPlan.getNodePairRoutes(thisNpIngressNode, thisNpEgressNode , false , wdmLayer).stream().filter(e -> !e.isBackupRoute()).collect(Collectors.toSet());
541                        int cplChosenRoute_numSRGOverlappingRoutes = Integer.MAX_VALUE;
542                        for(Pair<List<Link>,List<Link>> pathPair : getAndUpdateCplWdm11(cplNodePair, currentNetPlan))
543                        {
544                                final List<Link> seqLinksPrimaryThisNetPlan = pathPair.getFirst();
545                                final List<Link> seqLinksBackupThisNetPlan = pathPair.getSecond();
546                                final Quadruple<Boolean,Integer,Integer,Double> rwaEval = computeValidWAFirstFit_pathPair (seqLinksPrimaryThisNetPlan , seqLinksBackupThisNetPlan , numContigousSlotsOccupies , maximumLengthOfLighptathKm);
547                                if (!rwaEval.getFirst()) continue;
548                                /* compute the number of SRG-route pairs this route overlaps with, respect to the already existing lightpaths  */
549                                final Set<SharedRiskGroup> affectingSRGs_primaryOrBackup = SRGUtils.getAffectingSRGs(seqLinksPrimaryThisNetPlan);
550                                affectingSRGs_primaryOrBackup.addAll(SRGUtils.getAffectingSRGs(seqLinksBackupThisNetPlan));
551                                int numOverlappingSRGs = 0; 
552                                for (Route currentRoute : existingRoutesBetweenPairOfNodes)
553                                {
554                                        for (SharedRiskGroup srg : currentRoute.getSRGs()) if (affectingSRGs_primaryOrBackup.contains (srg)) numOverlappingSRGs ++;
555                                        Set<SharedRiskGroup> affectingSRGs = currentRoute.hasBackupRoutes()? currentRoute.getBackupRoutes().get(0).getSRGs() : new HashSet<> ();
556                                        for (SharedRiskGroup srg : affectingSRGs) if (affectingSRGs_primaryOrBackup.contains(srg)) numOverlappingSRGs ++;
557                                }
558                                if (numOverlappingSRGs < cplChosenRoute_numSRGOverlappingRoutes) 
559                                { 
560                                        cplChosenRoute_numSRGOverlappingRoutes = numOverlappingSRGs; 
561                                        chosenRWA = Pair.of (new WDMUtils.RSA (seqLinksPrimaryThisNetPlan , rwaEval.getSecond() , 
562                                                        numContigousSlotsOccupies , 
563                                                        WDMUtils.computeRegeneratorPositions(seqLinksPrimaryThisNetPlan , maximumLengthOfLighptathKm)) , 
564                                                        new WDMUtils.RSA (seqLinksBackupThisNetPlan , rwaEval.getThird() , numContigousSlotsOccupies , 
565                                                                        WDMUtils.computeRegeneratorPositions(seqLinksBackupThisNetPlan , maximumLengthOfLighptathKm)));
566                                        if (numOverlappingSRGs == 0) break; // no need to search more
567                                }
568                        }
569                        return chosenRWA;
570                }
571                throw new RuntimeException ("Bad");
572        }
573
574        private Triple<Boolean,Integer,Double> computeValidWAFirstFit_path (List<Link> seqLinks , int numContiguousSlots , double maxOpticalReachKm)
575        {
576                if (seqLinks.isEmpty()) throw new RuntimeException ("Bad");
577                if (seqLinks.get(0).getOriginNode().isDown()) return Triple.of(false,-1,-1.0);
578                if (getLengthInKm(seqLinks) > maxOpticalReachKm) return Triple.of(false,-1,-1.0);
579                double worseCaseSpareCapacity = Double.MAX_VALUE; 
580                for (Link e : seqLinks) 
581                { 
582                        worseCaseSpareCapacity = Math.min(worseCaseSpareCapacity, e.getCapacity() - e.getOccupiedCapacity());
583                        if (e.getDestinationNode().isDown() || e.isDown()) return Triple.of(false,-1,-1.0);
584                }
585                int wavelength = WDMUtils.spectrumAssignment_firstFit(seqLinks, wavelengthFiberOccupancy , numContiguousSlots);
586                if (wavelength != -1) 
587                        return Triple.of(true , wavelength , worseCaseSpareCapacity); 
588                else return Triple.of(false,-1,-1.0);
589        }
590        
591        private Quadruple<Boolean,Integer,Integer,Double> computeValidWAFirstFit_pathPair (List<Link> seqLinks_1 , List<Link> seqLinks_2 , int numSlots , double maxOpticalReachKm)
592        {
593                if (seqLinks_1.isEmpty() || seqLinks_2.isEmpty()) throw new RuntimeException ("Bad");
594                if (seqLinks_1.get(0).getOriginNode().isDown()) return Quadruple.of(false,-1,-1,-1.0);
595                if (seqLinks_2.get(0).getOriginNode().isDown()) return Quadruple.of(false,-1,-1,-1.0);
596                if (getLengthInKm(seqLinks_1) > maxOpticalReachKm) return Quadruple.of(false,-1,-1,-1.0);
597                if (getLengthInKm(seqLinks_2) > maxOpticalReachKm) return Quadruple.of(false,-1,-1,-1.0);
598                double worseCaseSpareCapacity = Double.MAX_VALUE; 
599                for (Link e : seqLinks_1) 
600                { 
601                        worseCaseSpareCapacity = Math.min(worseCaseSpareCapacity, e.getCapacity() - e.getOccupiedCapacity());
602                        if (e.getDestinationNode().isDown() || e.isDown()) return Quadruple.of(false,-1,-1,-1.0);
603                }
604                for (Link e : seqLinks_2) 
605                { 
606                        worseCaseSpareCapacity = Math.min(worseCaseSpareCapacity, e.getCapacity() - e.getOccupiedCapacity());
607                        if (e.getDestinationNode().isDown() || e.isDown()) return Quadruple.of(false,-1,-1,-1.0);
608                }
609                Pair<Integer,Integer> seqWavelengths = WDMUtils.spectrumAssignment_firstFitTwoRoutes(seqLinks_1, seqLinks_2, wavelengthFiberOccupancy,numSlots);
610                if (seqWavelengths != null) 
611                {
612                        if (!WDMUtils.isAllocatableRSASet(wavelengthFiberOccupancy , new WDMUtils.RSA(seqLinks_1 , seqWavelengths.getFirst() , numSlots)))
613                                throw new RuntimeException();
614                        if (!WDMUtils.isAllocatableRSASet(wavelengthFiberOccupancy , new WDMUtils.RSA(seqLinks_2 , seqWavelengths.getSecond() , numSlots)))
615                                throw new RuntimeException();
616                        return Quadruple.of(true , seqWavelengths.getFirst () , seqWavelengths.getSecond() , worseCaseSpareCapacity);
617                }
618                else return Quadruple.of(false,-1,-1,-1.0);
619        }
620
621        private void checkClashing (NetPlan np)
622        {
623                WDMUtils.checkResourceAllocationClashing(np, false, false, wdmLayer);
624        }
625        
626        private static void checkDisjointness (List<Link> p1 , List<Link> p2 , int disjointnessType)
627        {
628                if ((p1 == null) || (p2 == null)) throw new RuntimeException ("Bad");
629                if (disjointnessType == 0) // SRG disjoint
630                {
631                        Set<SharedRiskGroup> srg1 = new HashSet<SharedRiskGroup> (); for (Link e : p1) srg1.addAll (e.getSRGs());
632                        for (Link e : p2) for (SharedRiskGroup srg : e.getSRGs()) if (srg1.contains(srg)) throw new RuntimeException ("Bad");
633                }
634                else if (disjointnessType == 1) // link and node
635                {
636                        Set<NetworkElement> resourcesP1 = new HashSet<NetworkElement> (); boolean firstLink = true; 
637                        for (Link e : p1) { resourcesP1.add (e); if (firstLink) firstLink = false; else resourcesP1.add (e.getOriginNode()); }
638                        for (Link e : p2) if (resourcesP1.contains(e)) throw new RuntimeException ("Bad");
639                }
640                else if (disjointnessType == 2) // link 
641                {
642                        Set<Link> linksP1 = new HashSet<Link> (p1); 
643                        for (Link e : p2) if (linksP1.contains (e)) throw new RuntimeException ("Bad: p1: " + p1 + ", p2: " + p2);
644                }
645                else throw new RuntimeException ("Bad");
646                        
647        }
648        
649        private static double getLengthInKm (List<Link> p) { double res = 0; for (Link e : p) res += e.getLengthInKm(); return res; }
650        private void checkWaveOccupEqualsNp (NetPlan currentNetPlan)
651        {
652                DoubleMatrix2D freqNow_se = WDMUtils.getNetworkSlotAndRegeneratorOcupancy(currentNetPlan, true , wdmLayer).getFirst();
653                if (!freqNow_se.equals(wavelengthFiberOccupancy))
654                {
655                        System.out.println(freqNow_se.assign(wavelengthFiberOccupancy , DoubleFunctions.minusMult(1.0)));
656                        throw new RuntimeException ();
657                } 
658
659        }
660        
661        private List<List<Link>> getAndUpdateCplWdm (Pair<Node,Node> pair , NetPlan np)
662        {
663                List<List<Link>> res = cplWdm.get(pair);
664                if (res != null) return res;
665                final Map<Link,Double> linkCostMap = new HashMap<> (); for (Link e : np.getLinks(wdmLayer)) linkCostMap.put(e, e.getLengthInKm());
666        res = GraphUtils.getKLooplessShortestPaths(np.getNodes(), np.getLinks(wdmLayer), pair.getFirst(), 
667                        pair.getSecond(), linkCostMap, wdmK.getInt(), tpInfo.getMaxOpticalReachKm(), wdmMaxLightpathNumHops.getInt(), -1, -1, -1, -1);
668        if (res.isEmpty()) throw new Net2PlanException ("There is no path between nodes: " + pair.getFirst() + " -> " + pair.getSecond());
669        cplWdm.put(pair ,  res);
670        return res;
671        }
672        private List<Pair<List<Link>,List<Link>>> getAndUpdateCplWdm11 (Pair<Node,Node> pair , NetPlan np)
673        {
674                List<Pair<List<Link>,List<Link>>> res = cplWdm11.get(pair);
675                if (res != null) return res;
676                final List<List<Link>> basePathList = getAndUpdateCplWdm (pair , np);
677                final Map<Pair<Node,Node> , List<List<Link>>> singleNodePairCpl = new HashMap<>();
678                singleNodePairCpl.put(pair , basePathList); 
679                final Map<Pair<Node, Node>, List<Pair<List<Link>, List<Link>>>> cpl11ThisPair = 
680                                NetPlan.computeUnicastCandidate11PathList(singleNodePairCpl , protectionTypeCode);
681        if (cpl11ThisPair.isEmpty()) throw new Net2PlanException ("There is no 1+1 paths between nodes: " + pair.getFirst() + " -> " + pair.getSecond());
682        cplWdm11.put(pair ,  cpl11ThisPair.get(pair));
683        return cpl11ThisPair.get(pair);
684        }
685}