001/*******************************************************************************
002 * Copyright (c) 2017 Pablo Pavon Marino and others.
003 * All rights reserved. This program and the accompanying materials
004 * are made available under the terms of the 2-clause BSD License 
005 * which accompanies this distribution, and is available at
006 * https://opensource.org/licenses/BSD-2-Clause
007 *
008 * Contributors:
009 *     Pablo Pavon Marino and others - initial API and implementation
010 *******************************************************************************/
011
012
013
014
015package com.net2plan.examples.ocnbook.reports;
016
017import cern.colt.function.tdouble.DoubleDoubleFunction;
018import cern.colt.function.tdouble.DoubleFunction;
019import cern.colt.list.tdouble.DoubleArrayList;
020import cern.colt.list.tint.IntArrayList;
021import cern.colt.matrix.tdouble.DoubleFactory1D;
022import cern.colt.matrix.tdouble.DoubleMatrix1D;
023import cern.colt.matrix.tdouble.DoubleMatrix2D;
024import com.net2plan.interfaces.networkDesign.*;
025import com.net2plan.interfaces.simulation.IEventProcessor;
026import com.net2plan.interfaces.simulation.SimEvent;
027import com.net2plan.libraries.SRGUtils;
028import com.net2plan.utils.ClassLoaderUtils;
029import com.net2plan.utils.InputParameter;
030import com.net2plan.utils.StringUtils;
031import com.net2plan.utils.Triple;
032
033import java.io.Closeable;
034import java.io.File;
035import java.text.DecimalFormat;
036import java.util.*;
037
038/**
039 * This report receives as an input a network design, the network recovery scheme algorithm, and the network risks (SRGs), and estimates the availability of the 
040 * network (including individual availabilities for each demand), using an enumerative process that also provides an estimation of the estimation error. 
041 * 
042 * @net2plan.keywords Network recovery: protection , Network recovery: restoration
043 * @net2plan.ocnbooksections Section 3.7.3
044 * @net2plan.inputParameters 
045 * @author Pablo Pavon-Marino
046 */
047public class Report_availability implements IReport
048{
049        private InputParameter provisioningAlgorithm = new InputParameter ("provisioningAlgorithm" , "#eventProcessor#" , "Algorithm to process failure events");
050        private InputParameter analyzeDoubleFailures = new InputParameter ("analyzeDoubleFailures" , true , "Indicates whether double failures are studied");
051        private InputParameter defaultMTTFInHours = new InputParameter ("defaultMTTFInHours" , (double) 8748 , "Default value for Mean Time To Fail (hours)" , 0 , false , Double.MAX_VALUE , true);
052        private InputParameter defaultMTTRInHours = new InputParameter ("defaultMTTRInHours" , (double) 12 , "Default value for Mean Time To Repair (hours)" , 0 , false , Double.MAX_VALUE , true);
053        private InputParameter failureModel = new InputParameter ("failureModel" , "#select# perBidirectionalLinkBundle SRGfromNetPlan perNode perLink perDirectionalLinkBundle" , "Failure model selection: SRGfromNetPlan, perNode, perLink, perDirectionalLinkBundle, perBidirectionalLinkBundle");
054        private InputParameter considerTrafficInOversubscribedLinksAsLost = new InputParameter ("considerTrafficInOversubscribedLinksAsLost" , true , "If true, all the demands whose traffic (even only a fraction of it) traverses an oversubscribed link, are considered that all its treaffic is blocked, as they are supposed to fail to satisfy QoS agreements");
055        private InputParameter maximumE2ELatencyMs = new InputParameter ("maximumE2ELatencyMs", (double) -1 , "Maximum end-to-end latency of the traffic of any demand (a non-positive value means no limit). All the traffic of demands where a fraction of its traffic can exceed this value, are considered as lost, as they are supposed to fail to satisfy QoS agreements");
056        
057        private ArrayList<DoubleMatrix1D> availabilityClassicNoFailure_ld, availabilityWeightedNoFailure_ld, availabilityClassicNoFailure_lmd, availabilityWeightedNoFailure_lmd;
058        private ArrayList<DoubleMatrix1D> availabilityClassicTotal_ld, availabilityWeightedTotal_ld, availabilityClassicTotal_lmd, availabilityWeightedTotal_lmd;
059        private double pi_excess;
060
061        private IEventProcessor algorithm;
062        
063        @Override
064        public String executeReport(NetPlan netPlan, Map<String, String> reportParameters, Map<String, String> net2planParameters)
065        {
066                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
067                InputParameter.initializeAllInputParameterFieldsOfObject(this, reportParameters);
068
069                String algorithmFile = reportParameters.get("provisioningAlgorithm_file");
070                String algorithmName = reportParameters.get("provisioningAlgorithm_classname");
071                String algorithmParam = reportParameters.get("provisioningAlgorithm_parameters");
072                if (algorithmFile.isEmpty() || algorithmName.isEmpty()) throw new Net2PlanException("A provisioning algorithm must be defined");
073                final double PRECISION_FACTOR_hd = Double.parseDouble(net2planParameters.get("precisionFactor"));
074                final double PRECISION_FACTOR_blocking = PRECISION_FACTOR_hd * PRECISION_FACTOR_hd;
075
076                Map<String, String> algorithmParameters = StringUtils.stringToMap(algorithmParam);
077                switch (failureModel.getString ())
078                {
079                        case "SRGfromNetPlan":
080                                break;
081
082                        case "perNode":
083                                SRGUtils.configureSRGs(netPlan, defaultMTTFInHours.getDouble(), defaultMTTRInHours.getDouble(), SRGUtils.SharedRiskModel.PER_NODE, true);
084                                break;
085
086                        case "perLink":
087                                SRGUtils.configureSRGs(netPlan, defaultMTTFInHours.getDouble(), defaultMTTRInHours.getDouble(), SRGUtils.SharedRiskModel.PER_LINK, true);
088                                break;
089
090                        case "perDirectionalLinkBundle":
091                                SRGUtils.configureSRGs(netPlan, defaultMTTFInHours.getDouble(), defaultMTTRInHours.getDouble(), SRGUtils.SharedRiskModel.PER_DIRECTIONAL_LINK_BUNDLE, true);
092                                break;
093
094                        case "perBidirectionalLinkBundle":
095                                SRGUtils.configureSRGs(netPlan, defaultMTTFInHours.getDouble(), defaultMTTRInHours.getDouble(), SRGUtils.SharedRiskModel.PER_BIDIRECTIONAL_LINK_BUNDLE, true);
096                                break;
097
098                        default:
099                                throw new Net2PlanException("Failure model not valid. Please, check algorithm parameters description");
100                }
101
102                netPlan.setAllNodesFailureState(true);
103                for (NetworkLayer layer : netPlan.getNetworkLayers ())
104                        netPlan.setAllLinksFailureState(true , layer);
105                DoubleMatrix1D A_f = netPlan.getVectorSRGAvailability();
106                List<SharedRiskGroup> srgs = netPlan.getSRGs();
107                DoubleMatrix2D F_s = SRGUtils.getMatrixFailureState2SRG(srgs, true, analyzeDoubleFailures.getBoolean());
108                
109                /* Compute state probabilities (pi_s) */
110                DoubleMatrix1D pi_s = SRGUtils.computeStateProbabilities(F_s , A_f);
111                final double sum_pi_s = pi_s.zSum();
112                
113                final double pi_s0 = pi_s.get(0); 
114                
115                pi_excess = 1 - sum_pi_s;
116
117                /* Initialize statistics variables */
118                availabilityClassicNoFailure_ld = new ArrayList<DoubleMatrix1D> ();
119                availabilityWeightedNoFailure_ld = new ArrayList<DoubleMatrix1D> ();
120                availabilityClassicNoFailure_lmd = new ArrayList<DoubleMatrix1D> ();
121                availabilityWeightedNoFailure_lmd = new ArrayList<DoubleMatrix1D> ();
122                availabilityClassicTotal_ld = new ArrayList<DoubleMatrix1D> (); 
123                availabilityWeightedTotal_ld = new ArrayList<DoubleMatrix1D> ();
124                availabilityClassicTotal_lmd = new ArrayList<DoubleMatrix1D> ();
125                availabilityWeightedTotal_lmd = new ArrayList<DoubleMatrix1D> ();
126                for(int indexLayer = 0 ; indexLayer < netPlan.getNumberOfLayers() ; indexLayer ++)
127                {
128                        final NetworkLayer layer = netPlan.getNetworkLayer (indexLayer);
129                        final int D = netPlan.getNumberOfDemands(layer);
130                        final int MD = netPlan.getNumberOfMulticastDemands(layer);
131                        availabilityClassicTotal_ld.add (DoubleFactory1D.dense.make (D,0.0));
132                        availabilityWeightedTotal_ld.add (DoubleFactory1D.dense.make (D,0.0));
133                        availabilityClassicTotal_lmd.add (DoubleFactory1D.dense.make (MD,0.0));
134                        availabilityWeightedTotal_lmd.add (DoubleFactory1D.dense.make (MD,0.0));
135                }
136
137//              List<Link> upAndOversubscribedLinksSetToDown = new LinkedList<Link> ();
138//              if (considerTrafficInOversubscribedLinksAsLost.getBoolean())
139//              {
140//                      for (NetworkLayer layer : netPlan.getNetworkLayers ())
141//                      {
142////                            System.out.println ("Layer " + layer + ", initial link capacity traffic: " + netPlan.getVectorLinkCapacity(layer));
143////                            System.out.println ("Layer " + layer + ", initial demand carried traffic: " + netPlan.getVectorDemandCarriedTraffic(layer));
144//                              for (Link e : netPlan.getLinks (layer)) if (e.isUp() && e.isOversubscribed()) { upAndOversubscribedLinksSetToDown.add (e); }
145////                            System.out.println ("Layer: " + layer + ", upAndOversubscribedLinksSetToDown: " + upAndOversubscribedLinksSetToDown);
146//                      }
147//                      netPlan.setLinksAndNodesFailureState(null , upAndOversubscribedLinksSetToDown , null , null);
148//              }
149//
150//              System.out.println ("Links set to down all layers: " + netPlan.getLinksDownAllLayers());
151
152                /* Statistics for the no-failure state */
153                for(int indexLayer = 0 ; indexLayer < netPlan.getNumberOfLayers() ; indexLayer ++)
154                {
155                        final NetworkLayer layer = netPlan.getNetworkLayer (indexLayer);
156
157                        final DoubleMatrix1D h_d = netPlan.getVectorDemandOfferedTraffic(layer);
158                        final DoubleMatrix1D blocked_d = netPlan.getVectorDemandBlockedTraffic(layer);
159                        if (considerTrafficInOversubscribedLinksAsLost.getBoolean()) for (Demand d : netPlan.getDemands (layer)) if (d.isTraversingOversubscribedLinks()) blocked_d.set (d.getIndex () , d.getOfferedTraffic());
160                        if (maximumE2ELatencyMs.getDouble () > 0) for (Demand d : netPlan.getDemands (layer)) if (d.getWorstCasePropagationTimeInMs() > maximumE2ELatencyMs.getDouble ()) blocked_d.set (d.getIndex () , d.getOfferedTraffic());
161                        
162                        final DoubleMatrix1D h_md = netPlan.getVectorMulticastDemandOfferedTraffic(layer);
163                        final DoubleMatrix1D blocked_md = netPlan.getVectorMulticastDemandBlockedTraffic(layer);
164                        if (considerTrafficInOversubscribedLinksAsLost.getBoolean()) for (MulticastDemand d : netPlan.getMulticastDemands (layer)) if (d.isTraversingOversubscribedLinks()) blocked_md.set (d.getIndex () , d.getOfferedTraffic());
165                        if (maximumE2ELatencyMs.getDouble () > 0) for (MulticastDemand d : netPlan.getMulticastDemands(layer)) if (d.getWorseCasePropagationTimeInMs() > maximumE2ELatencyMs.getDouble ()) blocked_md.set (d.getIndex () , d.getOfferedTraffic());
166
167                        //                      System.out.println ("****** No failure Layer  " + layer + ", blocked_d: " + blocked_d);
168                        final DoubleMatrix1D availabilityClassic_d = blocked_d.copy ().assign (new DoubleFunction () { public double apply (double x) { return x > PRECISION_FACTOR_blocking? 0 : 1;  }  } );
169                        final DoubleMatrix1D availabilityWeighted_d = blocked_d.copy ().assign (h_d , new DoubleDoubleFunction () { public double apply (double x , double y) { return y < PRECISION_FACTOR_hd? 1 : 1 - x/y; }  } );
170                        final DoubleMatrix1D availabilityClassic_md = blocked_md.copy ().assign (new DoubleFunction () { public double apply (double x) { return x > PRECISION_FACTOR_blocking ? 0 : 1;  }  } );
171                        final DoubleMatrix1D availabilityWeighted_md = blocked_md.copy ().assign (h_md , new DoubleDoubleFunction () { public double apply (double x , double y) { return y < PRECISION_FACTOR_hd ? 1 : 1 - x/y; }  } );
172
173                        availabilityClassicTotal_ld.get(layer.getIndex ()).assign (availabilityClassic_d , new DoubleDoubleFunction () { public double apply (double x , double y) { return x + pi_s0 * y; } } );
174                        availabilityWeightedTotal_ld.get(layer.getIndex ()).assign (availabilityWeighted_d , new DoubleDoubleFunction () { public double apply (double x , double y) { return x + pi_s0 * y; } } );
175                        availabilityClassicTotal_lmd.get(layer.getIndex ()).assign (availabilityClassic_md , new DoubleDoubleFunction () { public double apply (double x , double y) { return x + pi_s0 * y; } } );
176                        availabilityWeightedTotal_lmd.get(layer.getIndex ()).assign (availabilityWeighted_md , new DoubleDoubleFunction () { public double apply (double x , double y) { return x + pi_s0 * y; } } );
177
178                        availabilityClassicNoFailure_ld.add(availabilityClassic_d);
179                        availabilityWeightedNoFailure_ld.add(availabilityWeighted_d);
180                        availabilityClassicNoFailure_lmd.add(availabilityClassic_md);
181                        availabilityWeightedNoFailure_lmd.add(availabilityWeighted_md);
182                }
183//              System.out.println ("Before any failure state: availabilityClassicTotal_ld: " + availabilityClassicTotal_ld);
184
185                
186                /* the up and oversubscribed links that were set as down, are set to up again */
187//              if (considerTrafficInOversubscribedLinksAsLost.getBoolean())
188//                      netPlan.setLinksAndNodesFailureState(upAndOversubscribedLinksSetToDown , null , null , null);
189
190                if (!netPlan.getLinksDownAllLayers().isEmpty() || !netPlan.getNodesDown().isEmpty()) throw new RuntimeException ("Bad");
191
192                NetPlan auxNetPlan = netPlan.copy ();
193                this.algorithm = ClassLoaderUtils.getInstance(new File(algorithmFile), algorithmName, IEventProcessor.class , null);
194                this.algorithm.initialize(auxNetPlan , algorithmParameters , reportParameters , net2planParameters);
195                Set<Link> initialLinksDownAllLayers = auxNetPlan.getLinksDownAllLayers();
196                Set<Node> initialNodesDown = auxNetPlan.getNodesDown();
197                Set<Link> linksAllLayers = new HashSet<Link> (); for (NetworkLayer layer : auxNetPlan.getNetworkLayers()) linksAllLayers.addAll (auxNetPlan.getLinks (layer));
198
199                for (int failureState = 1 ; failureState < F_s.rows () ; failureState ++) // first failure state (no failure) was already considered
200                {
201                        if (!auxNetPlan.getLinksDownAllLayers().equals (initialLinksDownAllLayers) || !auxNetPlan.getNodesDown().equals(initialNodesDown)) throw new RuntimeException ("Bad");
202
203                        IntArrayList srgs_thisState = new IntArrayList (); F_s.viewRow (failureState).getNonZeros (srgs_thisState , new DoubleArrayList ());
204                        final double pi_s_thisState = pi_s.get (failureState);
205
206                        Set<Link> linksToSetAsDown = new HashSet<Link> ();
207                        Set<Node> nodesToSetAsDown = new HashSet<Node> ();
208                        for (SharedRiskGroup srg : auxNetPlan.getSRGs())
209                        {
210                                if (F_s.get(failureState , srg.getIndex ()) != 1) continue;
211                                nodesToSetAsDown.addAll (srg.getNodes ());
212                                linksToSetAsDown.addAll (srg.getLinksAllLayers ());
213                        }
214
215                        /* Make the algorithm process the event of nodes and links down */
216                        SimEvent.NodesAndLinksChangeFailureState failureInfo = new SimEvent.NodesAndLinksChangeFailureState(null , nodesToSetAsDown , null , linksToSetAsDown);
217                        try
218                        {
219                                /* Apply the reaction algorithm */
220                                algorithm.processEvent(auxNetPlan, new SimEvent(0, SimEvent.DestinationModule.EVENT_PROCESSOR , -1 , failureInfo));
221                        }
222                        catch (Throwable e)
223                        {
224                                try { ((Closeable) algorithm.getClass().getClassLoader()).close();  }
225                                catch (Throwable e1) { e.printStackTrace();}                                    
226
227                                throw (e);
228                        }
229                        
230                        /* Only close the class loader if it is a different one than this class. If problems: just do not close the class loader, and wait for garbage collection*/
231                        if (!this.getClass().getClassLoader().equals(algorithm.getClass().getClassLoader()))
232                        {
233                                try { ((Closeable) algorithm.getClass().getClassLoader()).close();      } catch (Throwable e1) { }                                      
234                        }
235                        
236                        for(NetworkLayer layer : auxNetPlan.getNetworkLayers ())
237                        {
238                                final DoubleMatrix1D h_d = auxNetPlan.getVectorDemandOfferedTraffic(layer);
239                                final DoubleMatrix1D blocked_d = auxNetPlan.getVectorDemandBlockedTraffic(layer);
240                                if (considerTrafficInOversubscribedLinksAsLost.getBoolean()) for (Demand d : auxNetPlan.getDemands (layer)) if (d.isTraversingOversubscribedLinks()) blocked_d.set (d.getIndex () , d.getOfferedTraffic());
241
242                                if (maximumE2ELatencyMs.getDouble () > 0) for (Demand d : auxNetPlan.getDemands (layer)) if (d.getWorstCasePropagationTimeInMs() > maximumE2ELatencyMs.getDouble ()) blocked_d.set (d.getIndex () , d.getOfferedTraffic());
243                                final DoubleMatrix1D h_md = auxNetPlan.getVectorMulticastDemandOfferedTraffic(layer);
244                                final DoubleMatrix1D blocked_md = auxNetPlan.getVectorMulticastDemandBlockedTraffic(layer);
245                                if (considerTrafficInOversubscribedLinksAsLost.getBoolean()) for (MulticastDemand d : auxNetPlan.getMulticastDemands (layer)) if (d.isTraversingOversubscribedLinks()) blocked_md.set (d.getIndex () , d.getOfferedTraffic());
246
247                                if (maximumE2ELatencyMs.getDouble () > 0) for (MulticastDemand d : auxNetPlan.getMulticastDemands(layer)) if (d.getWorseCasePropagationTimeInMs() > maximumE2ELatencyMs.getDouble ()) blocked_md.set (d.getIndex () , d.getOfferedTraffic());
248
249                                final DoubleMatrix1D availabilityClassic_d = blocked_d.copy ().assign (new DoubleFunction () { public double apply (double x) { return x > PRECISION_FACTOR_blocking? 0 : 1;  }  } );
250                                final DoubleMatrix1D availabilityWeighted_d = blocked_d.copy ().assign (h_d , new DoubleDoubleFunction () { public double apply (double x , double y) { return y < PRECISION_FACTOR_hd? 1 : 1 - x/y; }  } );
251                                final DoubleMatrix1D availabilityClassic_md = blocked_md.copy ().assign (new DoubleFunction () { public double apply (double x) { return x > PRECISION_FACTOR_blocking? 0 : 1;  }  } );
252                                final DoubleMatrix1D availabilityWeighted_md = blocked_md.copy ().assign (h_md , new DoubleDoubleFunction () { public double apply (double x , double y) { return y < PRECISION_FACTOR_hd? 1 : 1 - x/y; }  } );
253
254                                availabilityClassicTotal_ld.get(layer.getIndex ()).assign (availabilityClassic_d , new DoubleDoubleFunction () { public double apply (double x , double y) { return x + pi_s_thisState * y; } } );
255                                availabilityWeightedTotal_ld.get(layer.getIndex ()).assign (availabilityWeighted_d , new DoubleDoubleFunction () { public double apply (double x , double y) { return x + pi_s_thisState * y; } } );
256                                availabilityClassicTotal_lmd.get(layer.getIndex ()).assign (availabilityClassic_md , new DoubleDoubleFunction () { public double apply (double x , double y) { return x + pi_s_thisState * y; } } );
257                                availabilityWeightedTotal_lmd.get(layer.getIndex ()).assign (availabilityWeighted_md , new DoubleDoubleFunction () { public double apply (double x , double y) { return x + pi_s_thisState * y; } } );
258                                
259//                              System.out.println ("Failure " + failureState + ", availabilityClassicTotal_ld: " + availabilityClassicTotal_ld);
260                        }
261                        
262                        failureInfo = new SimEvent.NodesAndLinksChangeFailureState(auxNetPlan.getNodes() , null , linksAllLayers , null);
263                        algorithm.processEvent(auxNetPlan, new SimEvent(0, SimEvent.DestinationModule.EVENT_PROCESSOR , -1 , failureInfo));
264                }
265
266                return printReport(netPlan , reportParameters);
267        }
268
269        @Override
270        public String getDescription()
271        {
272                return "This report receives as an input a network design, the network recovery scheme algorithm, and the network risks (SRGs), and estimates the availability of the network (including individual availabilities for each demand), using an enumerative process that also provides an estimation of the estimation error. ";
273        }
274
275        @Override
276        public List<Triple<String, String, String>> getParameters()
277        {
278                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
279                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
280        }
281
282        @Override
283        public String getTitle()
284        {
285                return "Availability report";
286        }
287
288        
289        private String printReport(NetPlan np , Map<String,String> reportParameters)
290        {
291                StringBuilder out = new StringBuilder();
292                DecimalFormat df_6 = new DecimalFormat("#.######");
293                out.append("<html><body>");
294                out.append("<head><title>Availability report</title></head>");
295                out.append("<h1>Introduction</h1>");
296                out.append("<p>This report computes several availability measures for the different unicast and multicast demands in the different layers of the network." +
297                                "The network is supposed to be subject to the failures defined by the SRGs, and the possible network states considered are: </p>");
298                out.append ("<ul>");
299                out.append ("<li>No failure occurs in the network</li>");
300                out.append ("<li>Single SRG failure: A single SRG fails, while the rest of failures associated to the other SRGs do not occur</li>");
301                out.append ("<li>Double SRG failure (optional): Two SRGs simultaneously fail, while the rest of failures associated to the other SRGs do not occur</li>");
302                out.append ("</ul>");
303                out.append("<p>A numerical method is used that (i) enumerates the possible failure states considered and the probability of occurrence, (ii) computes the network reaction" +
304                                "to that failure state using a used-defined algorithm, (iii) builds up the availability results for each demand from that.</p>");
305                out.append("<p>Then, the method is able to produce an estimation of the availability of each unicast and multicast demand (and from them network-wide availability measures) under the given failures, and" +
306                                " assuming that the network reacts according to an arbitrary protection/restoration provided algorithm.</p>");
307                out.append("<p>Two types of availability metrics are provided:</p>");
308                out.append ("<ul>");
309                out.append ("<li>Availability of a demand/network: Fraction of time in which the demand/network is carrying the 100% of the offered traffic</li>");
310                out.append ("<li>Traffic survivability of a demand/network: Fraction of the offered traffic that the network carries</li>");
311                out.append ("</ul>");
312                out.append("<p>Traffic survivability is always equals or higher than analogous availability value. For instance, if during some failure states that mean a 1% of the time, a demad is " +
313                                "carrying just a 50% of the traffic, the network availability is 99% (99% of the time everythign works perfectly well), but traffic survivability is " +
314                                "0.99*1 + 0.01*0.5 = 99.5%</p>");
315                out.append("<p>In each metric, we provide a pessimistic and optimistic estimation. The pessimistic estimation, considers that in the triple, quadruple etc. " +
316                                "failure states, all the traffic is lost. In the optimistic case, we assume that in such cases, all the traffic is carried. </p>");
317                
318                out.append("<p>For more information of this method:</p>");
319                out.append("<p>P. Pav�n Mariño, \"Optimization of computer networks. Modeling and algorithms. A hands-on approach\", Wiley 2016</p>");
320                out.append("<h1>Global information</h1>");
321                out.append("<h2>Input Parameters</h2>");
322                out.append("<table border='1'>");
323                out.append("<tr><th><b>Name</b></th><th><b>Value</b></th><th><b>Description</b></th>");
324                for (Triple<String, String, String> paramDef : getParameters())
325                {
326                        String name = paramDef.getFirst();
327                        String description = paramDef.getThird();
328                        String value = reportParameters.get(name);
329                        out.append("<tr><td>").append(name).append("</td><td>").append(value).append("</td><td>").append(description).append("</td></tr>");
330                }
331                out.append("<tr><td>--Estimated error in availability calculations: </td><td>" + pi_excess + "</td><td>Probabilities of triple failure, quadruple etc. of non enumerated network states</td></tr>");
332                out.append("</table>");
333
334                out.append("<h1>PER LAYER INFORMATION SUMMARY</h1>");
335                for (NetworkLayer layer : np.getNetworkLayers ())
336                {
337                        double val;
338
339                        out.append("<h2>Layer " + layer.getName () + ", index = " + layer.getIndex () + ", id = " + layer.getId () + "</h2>");
340                        final DoubleMatrix1D h_d = np.getVectorDemandOfferedTraffic(layer);
341                        final DoubleMatrix1D h_md = np.getVectorMulticastDemandOfferedTraffic(layer);
342                        final DoubleMatrix1D avNoFailure_d = this.availabilityClassicNoFailure_ld.get(layer.getIndex ());
343                        final DoubleMatrix1D survNoFailure_d = this.availabilityWeightedNoFailure_ld.get(layer.getIndex ());
344                        final DoubleMatrix1D avNoFailure_md = this.availabilityClassicNoFailure_lmd.get(layer.getIndex ());
345                        final DoubleMatrix1D survNoFailure_md = this.availabilityWeightedNoFailure_lmd.get(layer.getIndex ());
346                        final DoubleMatrix1D avTotal_d = this.availabilityClassicTotal_ld.get(layer.getIndex ());
347                        final DoubleMatrix1D survTotal_d = this.availabilityWeightedTotal_ld.get(layer.getIndex ());
348                        final DoubleMatrix1D avTotal_md = this.availabilityClassicTotal_lmd.get(layer.getIndex ());
349                        final DoubleMatrix1D survTotal_md = this.availabilityWeightedTotal_lmd.get(layer.getIndex ());
350
351                        out.append ("<ul>");
352                        final double H = h_d.zSum();
353                        val = h_d.size () == 0? 0 : H; out.append ("<li>UNICAST TRAFFIC: (Deterministic) Total offered traffic: " + df_6.format (val) + "</li>");
354                        if (np.getNumberOfDemands (layer) != 0)
355                        {
356                                val = survNoFailure_d.size () == 0? 0 : 1 - (survNoFailure_d.zDotProduct(h_d) / H); out.append ("<li>UNICAST TRAFFIC: (Deterministic) Blocked traffic when no failure occurs: " + df_6.format (val) + "</li>");
357                                val = avTotal_d.size () == 0? 0 : avTotal_d.zDotProduct(h_d) / H; out.append ("<li>UNICAST TRAFFIC: (Estimated) Network availability: " + df_6.format(val) + " - " + df_6.format(val + pi_excess)  + "</li>");
358                                val = survTotal_d.size () == 0? 0 : survTotal_d.zDotProduct(h_d) / H; out.append ("<li>UNICAST TRAFFIC: (Estimated) Network traffic survivability: " + df_6.format(val) + " - " + df_6.format(val + pi_excess) + "</li>");
359                                val = avTotal_d.size () == 0? 0 : avTotal_d.getMinLocation() [0]; out.append ("<li>UNICAST TRAFFIC: (Estimated) Worse availability among demands: " + df_6.format(val) + " - " + df_6.format(val + pi_excess)  + "</li>");
360                                val = survTotal_d.size () == 0? 0 : survTotal_d.getMinLocation() [0]; out.append ("<li>UNICAST TRAFFIC: (Estimated) Worse traffic survivability among demands: " + df_6.format(val) + " - " + df_6.format(val + pi_excess)  + "</li>");
361                        }                       
362                        final double MH = h_md.zSum();
363                        val = h_md.size () == 0? 0 : MH; out.append ("<li>MULTICAST TRAFFIC: (Deterministic) Total offered traffic: " + df_6.format (val) + "</li>");
364                        if (np.getNumberOfMulticastDemands(layer) != 0)
365                        {
366                                val = survNoFailure_md.size () == 0? 0 : 1-(survNoFailure_md.zDotProduct(h_md) / MH); out.append ("<li>MULTICAST TRAFFIC: (Deterministic) Blocked traffic when no failure occurs: " + df_6.format (val) + "</li>");
367                                val = avTotal_md.size () == 0? 0 : avTotal_md.zDotProduct(h_md) / MH; out.append ("<li>MULTICAST TRAFFIC: (Estimated) Network availability: " + df_6.format(val) + " - " + df_6.format(val + pi_excess)  + "</li>");
368                                val = survTotal_md.size () == 0? 0 : survTotal_md.zDotProduct(h_md) /MH; out.append ("<li>MULTICAST TRAFFIC: (Estimated) Network traffic survivability: " + df_6.format(val) + " - " + df_6.format(val + pi_excess) + "</li>");
369                                val = avTotal_md.size () == 0? 0 : avTotal_md.getMinLocation() [0]; out.append ("<li>MULTICAST TRAFFIC: (Estimated) Worse availability among demands: " + df_6.format(val) + " - " + df_6.format(val + pi_excess)  + "</li>");
370                                val = survTotal_md.size () == 0? 0 : survTotal_md.getMinLocation() [0]; out.append ("<li>MULTICAST TRAFFIC: (Estimated) Worse traffic survivability among demands: " + df_6.format(val) + " - " + df_6.format(val + pi_excess)  + "</li>");
371                        }
372                }
373
374                
375                out.append("<h1>PER LAYER INFORMATION DETAILED INFORMATION</h1>");
376                for (NetworkLayer layer : np.getNetworkLayers ())
377                {
378                        out.append("<h2>Layer " + layer.getName () + ", index = " + layer.getIndex () + ", id = " + layer.getId () + "</h2>");
379                        final DoubleMatrix1D avTotal_d = this.availabilityClassicTotal_ld.get(layer.getIndex ());
380                        final DoubleMatrix1D survTotal_d = this.availabilityWeightedTotal_ld.get(layer.getIndex ());
381                        final DoubleMatrix1D avTotal_md = this.availabilityClassicTotal_lmd.get(layer.getIndex ());
382                        final DoubleMatrix1D survTotal_md = this.availabilityWeightedTotal_lmd.get(layer.getIndex ());
383
384                        if (np.getNumberOfDemands(layer) != 0)
385                        {
386                                out.append("<table border='1'>");
387                                out.append("<tr><th><b>Demand</b></th><th><b>Origin node</b></th><th><b>Destination node</b></th><th><b>Availability</b></th><th><b>Traffic survivability</b></th></tr>");
388                                for (Demand d : np.getDemands (layer))
389                                        out.append("<tr><td> " + d.getIndex () + " id (" + d.getId () + ")" + "</td><td>" + d.getIngressNode ().getIndex () + " (" + d.getIngressNode ().getName () + ") </td><td> " + d.getEgressNode().getIndex () + " (" + d.getEgressNode().getName () + ")</td><td>" + avTotal_d.get(d.getIndex()) +" ... " + (avTotal_d.get(d.getIndex()) + pi_excess) + "</td><td>" +  survTotal_d.get(d.getIndex()) + " ... " + (survTotal_d.get(d.getIndex()) + pi_excess) + "</td></tr>");
390                                out.append("</table>");
391        
392                                out.append("<p></p><p></p>");
393                        }
394                        if (np.getNumberOfMulticastDemands(layer) != 0)
395                        {
396                                out.append("<table border='1'>");
397                                out.append("<tr><th><b>Multicast demand</b></th><th><b>Origin node</b></th><th><b>Destination nodes</b></th><th><b>Availability</b></th><th><b>Traffic survivability</b></th></tr>");
398                                for (MulticastDemand d : np.getMulticastDemands (layer))
399                                {
400                                        String egressNodesInfo = ""; for (Node n : d.getEgressNodes()) egressNodesInfo += n.getIndex () + "(" + n.getName () + ") ";
401                                        out.append("<tr><td> " + d.getIndex () + " id (" + d.getId () + ")" + "</td><td>" + d.getIngressNode ().getIndex () + " (" + d.getIngressNode ().getName () + ") </td><td> " + egressNodesInfo + "</td><td>" + avTotal_md.get(d.getIndex())  + " ... " + (avTotal_md.get(d.getIndex()) + pi_excess) + "</td><td>" +  survTotal_md.get(d.getIndex())  + " ... " + (survTotal_md.get(d.getIndex()) + pi_excess) + "</td></tr>");
402                                }
403                                out.append("</table>");
404                        }                       
405                }
406                
407                return out.toString();
408        }
409
410        
411        
412}