1 // $Id: xml_report.cpp,v 1.15 2011/04/04 21:51:48 ewalkup Exp $
2 
3 /*
4   Copyright 2008  Peeter Beerli, Mary Kuhner, Jon Yamato and Joseph Felsenstein
5 
6   This software is distributed free of charge for non-commercial use
7   and is copyrighted.  Of course, we do not guarantee that the software
8   works, and are not responsible for any damage you may cause or have.
9 */
10 
11 #include <cassert>
12 
13 #include "chainmanager.h"
14 #include "chainout.h"
15 #include "force.h"
16 #include "mathx.h"
17 #include "parameter.h"
18 #include "region.h"
19 #include "reportpage.h"
20 #include "timex.h"
21 #include "vector_constants.h"
22 #include "xml_report.h"
23 
24 //------------------------------------------------------------------------------------
25 
XMLReport(std::string filename)26 XMLReport::XMLReport(std::string filename)
27     :   m_filename(filename),
28         m_doc(filename.c_str())
29 {
30 }
31 
~XMLReport()32 XMLReport::~XMLReport()
33 {
34 }
35 
36 void
AddSlicesTo(TiXmlElement * parentElem,const Parameter & param,force_type tag,long regNo,bool do_overall)37 XMLReport::AddSlicesTo( TiXmlElement * parentElem,
38                         const Parameter& param,
39                         force_type tag,
40                         long regNo,
41                         bool do_overall)
42 {
43 
44     bool isBayes = registry.GetChainParameters().IsBayesian();
45     proftype ptype = param.GetProfileType();
46     verbosity_type verbosity = registry.GetUserParameters().GetVerbosity();
47 
48     TiXmlElement * profileElem = new TiXmlElement("profile");
49     std::string profTypeString = ToString(ptype);
50     profileElem->SetAttribute("type",profTypeString.c_str());
51     profileElem->SetAttribute("analysis", isBayes ? "bayesian" : "likelihood");
52     parentElem->LinkEndChild(profileElem);
53 
54     double mleval = -DBL_BIG;
55     double mleperc = -DBL_BIG;      // only for bayesian
56     double llikeAtMle = -DBL_BIG;   // log likelihood for like, point prob for bayes
57 
58 #if 0 // EWFIX.REMOVE
59     vector<vector<centilepair> > centiles;
60 #endif
61 
62     vector<centilepair> CIvec;
63     vector<centilepair> priorLikeVec;
64 
65     unsigned long paramindex = param.GetParamVecIndex();
66     DoubleVec1d modifiers = registry.GetForceSummary().GetModifiers(paramindex);
67     DoubleVec1d special_modifiers;  // used for fixed w/ growth
68 
69     if(do_overall)
70     {
71         mleval = param.GetOverallMLE();
72         llikeAtMle = registry.GetForceSummary().GetOverallLlikeMle();
73 #if 0   // EWFIX.REMOVE
74         centiles = param.GetOverallProfile();
75 #endif
76         if(param.IsProfiled())
77         {
78             CIvec = param.GetOverallCIs();
79             priorLikeVec = param.GetOverallPriorLikes();
80         }
81     }
82     else
83     {
84         mleval = param.GetMLE(regNo);
85         llikeAtMle = registry.GetForceSummary().GetLlikeMle(regNo);
86 #if 0   // EWFIX.REMOVE
87         centiles = param.GetProfiles(regNo);
88 #endif
89         if(param.IsProfiled())
90         {
91             CIvec = param.GetCIs(regNo);
92             priorLikeVec = param.GetPriorLikes(regNo);
93         }
94     }
95 
96     // 999999999999999999999
97     // EWFIX -- need to sort on double val
98     std::map< std::pair<double,double>, TiXmlElement *> slices;
99 
100     if(param.IsProfiled())
101     {
102         if(isBayes)
103         {
104             mleperc = ReportPage::GetReverseCentile(CIvec,mleval);
105             llikeAtMle = ReportPage::GetCentile(priorLikeVec,mleperc);
106         }
107 
108         DoubleVec1d modTimesMleToPercs;
109         if(ptype == profile_FIX )
110         {
111             if (param.IsForce(force_GROW) && verbosity != CONCISE && verbosity != NONE)
112             {
113                 special_modifiers = vecconst::growthmultipliers;
114                 special_modifiers.insert(special_modifiers.end(),vecconst::growthfixed.begin(),vecconst::growthfixed.end());
115             }
116             if (isBayes)
117             {
118                 if (param.IsForce(force_GROW) && verbosity != CONCISE && verbosity != NONE)
119                 {
120                     modifiers = ProfPage::MakeGrowFixedModifiers(mleval);
121                     for (unsigned long ind = 0; ind < modifiers.size(); ++ind)
122                     {
123                         modTimesMleToPercs.push_back(modifiers[ind]);
124                     }
125                 }
126                 else
127                 {
128                     for (unsigned long ind = 0; ind < modifiers.size(); ++ind)
129                     {
130                         modTimesMleToPercs.push_back(modifiers[ind]*mleval);
131                     }
132                 }
133                 ProfPage::TradeValsForPercs(modTimesMleToPercs,CIvec);
134             }
135         }
136 
137         for (unsigned long ind = 0; ind < modifiers.size(); ++ind)
138         {
139             if (ptype == profile_NONE) continue;
140 
141             TiXmlElement * oneSlice = new TiXmlElement("profile-slice");
142 
143             double paramValue = -DBL_BIG;
144             if(ptype == profile_FIX && (isBayes || verbosity == CONCISE || verbosity == NONE))
145             {
146                 if (param.IsForce(force_GROW) && verbosity != CONCISE && verbosity != NONE)
147                 {
148                     paramValue = modifiers[ind];
149                 }
150                 else
151                 {
152                     paramValue = mleval * modifiers[ind];
153                 }
154             }
155             else
156             {
157                 paramValue = ReportPage::GetCentile(CIvec,modifiers[ind]);
158             }
159 
160             ///////////////////////////////////////////////////////////
161             // <multiplier> -- only for fixed
162             ///////////////////////////////////////////////////////////
163             if(ptype == profile_FIX)
164             {
165                 std::string modString = "multiplier";
166                 double modValue = modifiers[ind];
167 
168                 if(verbosity != CONCISE && verbosity != NONE && param.IsForce(force_GROW))
169                 {
170                     modValue = special_modifiers[ind];
171                     if(paramValue == special_modifiers[ind])
172                     {
173                         modString = "fixed-value";
174                     }
175                 }
176 
177                 TiXmlElement * modElem = new TiXmlElement(modString);
178                 modElem->SetAttribute("value",ToString(modValue).c_str());
179                 oneSlice->LinkEndChild(modElem);
180             }
181 
182             ///////////////////////////////////////////////////////////
183             // <param-value>
184             ///////////////////////////////////////////////////////////
185             TiXmlElement * paramValueElem = new TiXmlElement("param-value");
186             oneSlice->LinkEndChild(paramValueElem);
187             paramValueElem->SetAttribute("value",ToString(paramValue).c_str());
188             if(ptype == profile_PERCENTILE)
189             {
190                 //If the profiler gave up before finding values with as low a
191                 // log likelihood as it would have liked, that information was
192                 // saved in the analyzer, and we can get it out here to tell
193                 // users that the value is "<1.53" instead of "1.53".
194                 if (param.CentileIsExtremeLow(modifiers[ind], regNo))
195                 {
196                     if (param.CentileIsExtremeHigh(modifiers[ind], regNo))
197                     {
198                         // EWFIX -- do we have better warn status?
199                         paramValueElem->SetAttribute("warning","failed");
200                         paramValueElem->SetAttribute("value","***");
201                     }
202                     else
203                     {
204                         paramValueElem->SetAttribute("warning","lower");
205                     }
206                 }
207                 else if (param.CentileIsExtremeHigh(modifiers[ind], regNo))
208                 {
209                     paramValueElem->SetAttribute("warning","higher");
210                 }
211                 else if (param.CentileHadWarning(modifiers[ind], regNo))
212                 {
213                     paramValueElem->SetAttribute("warning","other");
214                 }
215             }
216 
217             ///////////////////////////////////////////////////////////
218             // <percentile>
219             ///////////////////////////////////////////////////////////
220             if(ptype == profile_PERCENTILE)
221             {
222                 TiXmlElement * percElem = new TiXmlElement("percentile");
223                 percElem->SetAttribute("value",ToString(modifiers[ind]));
224                 oneSlice->LinkEndChild(percElem);
225             }
226             if(ptype == profile_FIX && isBayes)
227             {
228                 TiXmlElement * percElem = new TiXmlElement("percentile");
229 
230                 double modVal = modifiers[ind];
231                 if (! (param.IsForce(force_GROW) && verbosity != CONCISE && verbosity != NONE))
232                 {
233                     modVal *= mleval;
234                 }
235 
236                 double val = ReportPage::GetReverseCentile(CIvec,modVal);
237                 percElem->SetAttribute("value",ToString(val));
238                 oneSlice->LinkEndChild(percElem);
239             }
240 
241             ///////////////////////////////////////////////////////////
242             // <log-likelihood> -- not for bayes
243             ///////////////////////////////////////////////////////////
244             if(!isBayes)
245             {
246                 TiXmlElement * logLike = new TiXmlElement("log-likelihood");
247                 double lnl = ReportPage::GetCentile(priorLikeVec,modifiers[ind]);
248 
249 #if 0  // Potentially DEAD CODE (bobgian, Feb 2010)
250                 // EWFIX.HACK.HACK.HACK -- I don't understand what's going
251                 // on in reportpage.cpp, but at least this gets the right
252                 // values out
253                 if(lnl == FLAGLONG && ptype == profile_FIX && param.IsForce(force_GROW) && verbosity != CONCISE && verbosity != NONE)
254                 {
255                     lnl = ReportPage::GetCentile(priorLikeVec,modifiers[ind]/mleval);
256                 }
257 #endif
258 
259                 logLike->SetAttribute("value",ToString(lnl).c_str());
260                 oneSlice->LinkEndChild(logLike);
261             }
262 
263             ///////////////////////////////////////////////////////////
264             // <point-probability> -- only for bayes
265             ///////////////////////////////////////////////////////////
266             if(isBayes)
267             {
268                 TiXmlElement * pointProb = new TiXmlElement("point-probability");
269                 double lnl = -DBL_BIG;
270                 if(ptype == profile_FIX)
271                 {
272                     lnl = ReportPage::GetCentile(priorLikeVec,modTimesMleToPercs[ind]);
273                 }
274                 else
275                 {
276                     lnl = ReportPage::GetCentile(priorLikeVec,modifiers[ind]);
277                 }
278                 std::string likeStr = (lnl == -DBL_BIG) ? "***" : ToString(lnl);
279                 pointProb->SetAttribute("value",likeStr.c_str());
280                 oneSlice->LinkEndChild(pointProb);
281             }
282 
283             slices[std::pair<double,double>(paramValue,modifiers[ind])]=oneSlice;
284 
285         }
286     }
287 
288     // add in element at MLE/MPE
289     bool haveMle = false;
290 
291     // element may already be there, but not marked as MLE
292     // I believe this only happens for profile_FIX && isBayes
293     // It can also be there because we've hit the maximum or
294     // minimum value -- but in that case we want the duplicate
295     // entries
296     std::map< std::pair<double,double>, TiXmlElement *>::const_iterator sliter;
297     for(sliter = slices.begin(); sliter != slices.end(); sliter++)
298     {
299         std::pair<double,double> sortVal = (*sliter).first;
300         double paramValue = sortVal.first;
301         if(isBayes && ptype == profile_FIX && CloseEnough(paramValue,mleval))
302         {
303             haveMle = true;
304             assert(sortVal.second == 1.0 || param.IsForce(force_GROW));
305             ((*sliter).second)->SetAttribute("special", "mpe");
306         }
307     }
308 
309     if (!haveMle)
310     {
311         TiXmlElement * newSlice = new TiXmlElement("profile-slice");
312         double secondSort = ptype == profile_FIX ? 1.0 : 0.5;
313         slices[std::pair<double,double>(mleval,secondSort)] = newSlice;
314 
315         newSlice->SetAttribute("special", isBayes ? "mpe" : "mle");
316 
317         if(ptype == profile_FIX)
318         {
319             TiXmlElement * multiplierElem = new TiXmlElement("multiplier");
320             newSlice->LinkEndChild(multiplierElem);
321             multiplierElem->SetAttribute("value",ToString(1.0).c_str());
322         }
323 
324         TiXmlElement * paramValueElem = new TiXmlElement("param-value");
325         newSlice->LinkEndChild(paramValueElem);
326         paramValueElem->SetAttribute("value",ToString(mleval).c_str());
327 
328         if(param.IsProfiled())
329         {
330             if(isBayes)
331             {
332                 TiXmlElement * percElem = new TiXmlElement("percentile");
333                 newSlice->LinkEndChild(percElem);
334                 percElem->SetAttribute("value",ToString(mleperc).c_str());
335 
336                 // EWFIX -- wrong for bayes/fix/grow ?? test this !!
337                 TiXmlElement * pointProbElem = new TiXmlElement("point-probability");
338                 newSlice->LinkEndChild(pointProbElem);
339                 pointProbElem->SetAttribute("value",ToString(llikeAtMle).c_str());
340             }
341             else
342             {
343                 TiXmlElement * logLikeElem = new TiXmlElement("log-likelihood");
344                 newSlice->LinkEndChild(logLikeElem);
345                 logLikeElem->SetAttribute("value",ToString(llikeAtMle).c_str());
346             }
347         }
348 
349     }
350 
351     for(sliter = slices.begin(); sliter != slices.end(); sliter++)
352     {
353         profileElem->LinkEndChild((*sliter).second);
354     }
355 }
356 
357 TiXmlElement *
MakeParameter(const Parameter & param,force_type force_tag)358 XMLReport::MakeParameter(const Parameter& param, force_type force_tag)
359 {
360 
361     string paramname = param.GetName();
362     ReportPage::TrimString(paramname);
363     string shortparamname = param.GetShortName();
364     ReportPage::TrimString(shortparamname);
365     unsigned long pindex = param.GetParamVecIndex();
366     proftype ptype = param.GetProfileType();
367 
368     TiXmlElement * paramElem = new TiXmlElement("parameter");
369     paramElem->SetAttribute("short-name",shortparamname.c_str());
370     paramElem->SetAttribute("name",paramname.c_str());
371     paramElem->SetAttribute("index",ToString(pindex));
372     paramElem->SetAttribute("type",ToString(ptype));
373 
374     long nregs = registry.GetDataPack().GetNRegions();
375     for (long reg = 0; reg <= nregs; ++reg)
376     {
377         if (reg == nregs && nregs <= 1) break;
378         bool do_overall = (reg==nregs && nregs > 1);
379 
380         TiXmlElement * estimateElem = new TiXmlElement("estimate");
381         paramElem->LinkEndChild(estimateElem);
382         if(do_overall)
383         {
384             estimateElem->SetAttribute("type","overall");
385         }
386         else
387         {
388             estimateElem->SetAttribute("type","single-region");
389             std::string regionname = registry.GetDataPack().GetRegion(reg).GetRegionName();
390             estimateElem->SetAttribute("region-name",regionname.c_str());
391         }
392 
393         AddSlicesTo(estimateElem,param,force_tag,reg,do_overall);
394     }
395     return paramElem;
396 }
397 
398 TiXmlElement *
MakeParameters()399 XMLReport::MakeParameters()
400 {
401 
402     TiXmlElement * parametersElem = new TiXmlElement("parameters");
403 
404     const ForceVec forces = registry.GetForceSummary().GetAllForces();
405 
406     ForceVec::const_iterator fit;
407     for(fit = forces.begin(); fit != forces.end(); ++fit)
408     {
409         // organizing parameters by force
410         TiXmlElement * forceElem = new TiXmlElement("force");
411         parametersElem->LinkEndChild(forceElem);
412 
413         // identify the force
414         string forcename = (*fit)->GetFullparamname();
415         string shortforcename = (*fit)->GetShortparamname();
416         force_type tag = (*fit)->GetTag();
417 
418         forceElem->SetAttribute("long-name",forcename.c_str());
419         forceElem->SetAttribute("short-name",shortforcename.c_str());
420 
421         const vector<Parameter>& parameters = (*fit)->GetParameters();
422         vector<Parameter>::const_iterator param;
423         for (param = parameters.begin(); param != parameters.end(); ++param)
424         {
425             ParamStatus mystatus = param->GetStatus();
426             if (!mystatus.Valid()) continue;
427 
428             TiXmlElement * paramElem = MakeParameter(*param,tag);
429             forceElem->LinkEndChild(paramElem);
430         }
431     }
432     // RegionGammaInfo should go here
433     return parametersElem;
434 }
435 
436 TiXmlElement *
MakeChainReport(const ChainOut & co,size_t regNo,size_t repNo,size_t chainNo)437 XMLReport::MakeChainReport(const ChainOut& co,size_t regNo, size_t repNo, size_t chainNo)
438 {
439     TiXmlElement * elem = new TiXmlElement("chain-info");
440     elem->SetAttribute("region",ToString(regNo).c_str());
441     elem->SetAttribute("replicate",ToString(repNo).c_str());
442     elem->SetAttribute("chain",ToString(chainNo).c_str());
443 
444     // info on timing
445     TiXmlElement * timeElem = new TiXmlElement("runtime");
446     time_t starttime = co.GetStarttime();
447     time_t endtime = co.GetEndtime();
448     timeElem->SetAttribute("start",PrintTime(starttime).c_str());
449     timeElem->SetAttribute("end",PrintTime(endtime).c_str());
450     timeElem->SetAttribute("seconds",ToString(endtime-starttime).c_str());
451     elem->LinkEndChild(timeElem);
452 
453     // info on discarded trees
454     TiXmlElement * discardsElem = new TiXmlElement("discarded-trees");
455 
456     TiXmlElement * badTreesElem = new TiXmlElement("bad-trees");
457     badTreesElem->SetAttribute("value",ToString(co.GetNumBadTrees()).c_str());
458     discardsElem->LinkEndChild(badTreesElem);
459 
460     TiXmlElement * tinyPopTreesElem = new TiXmlElement("tiny-pop-trees");
461     tinyPopTreesElem->SetAttribute("value",ToString(co.GetTinyPopTrees()).c_str());
462     discardsElem->LinkEndChild(tinyPopTreesElem);
463 
464     TiXmlElement * zeroDlTreesElem = new TiXmlElement("zero-dl-trees");
465     zeroDlTreesElem->SetAttribute("value",ToString(co.GetZeroDLTrees()).c_str());
466     discardsElem->LinkEndChild(zeroDlTreesElem);
467 
468     TiXmlElement * longBranchTreesElem = new TiXmlElement("long-branch-trees");
469     longBranchTreesElem->SetAttribute("value",ToString(co.GetStretchedTrees()).c_str());
470     discardsElem->LinkEndChild(longBranchTreesElem);
471 
472     elem->LinkEndChild(discardsElem);
473 
474     // info on acceptance rates
475     TiXmlElement * acceptRatesElem = new TiXmlElement("acceptance-rates");
476     elem->LinkEndChild(acceptRatesElem);
477 
478     TiXmlElement * overallAcceptRate = new TiXmlElement("overall-acceptance");
479     overallAcceptRate->SetAttribute("value",ToString(co.GetAccrate()).c_str());
480     acceptRatesElem->LinkEndChild(overallAcceptRate);
481 
482     ratemap::const_iterator iter;
483     ratemap rates = co.GetAllAccrates();
484     for(iter=rates.begin(); iter != rates.end(); iter++)
485     {
486         std::string rateType = (*iter).first;
487         std::pair<long,long> values = (*iter).second;
488         long accepted = values.first;
489         long proposed = values.second;
490 
491         TiXmlElement * rateElem = new TiXmlElement("arranger-rate");
492         rateElem->SetAttribute("type",rateType);
493         rateElem->SetAttribute("accepted",ToString(accepted).c_str());
494         rateElem->SetAttribute("proposed",ToString(proposed).c_str());
495         acceptRatesElem->LinkEndChild(rateElem);
496     }
497 
498     // unique sampled values (for Bayesian)
499     vector<long> bayesunique = co.GetBayesUnique();
500     if (!bayesunique.empty())
501     {
502         TiXmlElement * uniqBayesElem = new TiXmlElement("unique-sampled-values");
503         elem->LinkEndChild(uniqBayesElem);
504 
505         const ParamVector paramvec(true);
506         assert(paramvec.size() == bayesunique.size());
507         for (unsigned long pnum=0; pnum<paramvec.size(); pnum++)
508         {
509             ParamStatus mystatus = paramvec[pnum].GetStatus();
510             if (mystatus.Inferred())
511             {
512                 TiXmlElement * pElem = new TiXmlElement("parameter");
513                 uniqBayesElem->LinkEndChild(pElem);
514 
515                 pElem->SetAttribute("name",paramvec[pnum].GetName());
516                 pElem->SetAttribute("samples",bayesunique[pnum]);
517 
518                 if (bayesunique[pnum] < 50)
519                 {
520                     pElem->SetAttribute("warning","too few");
521                 }
522             }
523         }
524     }
525 
526     return elem;
527 }
528 
529 TiXmlElement *
MakeWarnings()530 XMLReport::MakeWarnings()
531 {
532     TiXmlElement * warnElem = new TiXmlElement("warnings");
533 
534     TiXmlElement * frontEndElem = new TiXmlElement("front-end-warnings");
535     warnElem->LinkEndChild(frontEndElem);
536     TiXmlComment * warnComment = new TiXmlComment();
537     warnComment->SetValue("foo!");
538     warnElem->LinkEndChild(warnComment);
539 
540     TiXmlElement * backEndElem = new TiXmlElement("back-end-warnings");
541     warnElem->LinkEndChild(backEndElem);
542     warnComment = new TiXmlComment();
543     warnComment->SetValue("fie!");
544     warnElem->LinkEndChild(warnComment);
545 
546     return warnElem;
547 }
548 
549 void
AddRangeElements(TiXmlElement * traitElem,rangeset sites,std::string label)550 XMLReport::AddRangeElements(TiXmlElement * traitElem,rangeset sites,std::string label)
551 {
552     TiXmlElement * range = new TiXmlElement("range");
553     traitElem->LinkEndChild(range);
554     range->SetAttribute("type",label.c_str());
555 
556     for(rangeset::iterator i = sites.begin(); i != sites.end(); i++)
557     {
558         rangepair rp = *i;
559         TiXmlElement * sites = new TiXmlElement("sites");
560         range->LinkEndChild(sites);
561         sites->SetAttribute("start",ToString(rp.first).c_str());
562         sites->SetAttribute("stop",ToString(rp.second-1).c_str());
563     }
564 }
565 
566 TiXmlElement *
Write(const ChainManager & chainMan)567 XMLReport::Write(const ChainManager& chainMan)
568 {
569     TiXmlElement * topItem = new TiXmlElement("lamarc-run-report");
570     m_doc.LinkEndChild( topItem );
571 
572     // parameters
573     TiXmlElement * parameters = MakeParameters();
574     topItem->LinkEndChild(parameters);
575 
576     // mapping
577     if (registry.GetDataPack().AnyMapping())
578     {
579         TiXmlElement * traitsElem = new TiXmlElement("traits");
580         topItem->LinkEndChild(traitsElem);
581 
582         for (long reg=0; reg<registry.GetDataPack().GetNRegions(); reg++)
583         {
584             const Region& region = registry.GetDataPack().GetRegion(reg);
585             for (long mloc=0; mloc<region.GetNumMovingLoci(); mloc++)
586             {
587                 const Locus& locus = region.GetMovingLocus(mloc);
588 
589 
590                 TiXmlElement * traitElem = new TiXmlElement("trait");
591                 traitsElem->LinkEndChild(traitElem);
592 
593                 traitElem->SetAttribute("name",locus.GetName().c_str());
594                 traitElem->SetAttribute("type",ToString(locus.GetAnalysisType()).c_str());
595 
596 
597                 long regoffset = region.GetSiteSpan().first;
598                 std::set<std::pair<double, long int> > orderedsites = locus.MakeOrderedSites(regoffset);
599 
600                 rangeset bestsites = locus.GetBestSites(orderedsites);
601                 AddRangeElements(traitElem,bestsites,"best");
602 
603                 rangeset top5 = locus.GetTopSites(orderedsites,0.05);
604                 AddRangeElements(traitElem,top5,"0.05");
605 
606                 rangeset top50 = locus.GetTopSites(orderedsites,0.50);
607                 AddRangeElements(traitElem,top50,"0.50");
608 
609                 rangeset top95 = locus.GetTopSites(orderedsites,0.95);
610                 AddRangeElements(traitElem,top95,"0.95");
611             }
612         }
613     }
614 
615     // chains and arrangers report
616     TiXmlElement * chainPackElem = new TiXmlElement("chains");
617     topItem->LinkEndChild(chainPackElem);
618     vector<vector<vector<ChainOut > > > chains = chainMan.GetChainPack().GetAllChains();
619     for(size_t i = 0; i < chains.size(); i++)
620     {
621         for(size_t j = 0; j < chains[i].size(); j++)
622         {
623             for(size_t k = 0; k < chains[i][j].size(); k++)
624             {
625                 const ChainOut & co = chains[i][j][k];
626                 chainPackElem->LinkEndChild(MakeChainReport(co,i,j,k));
627             }
628         }
629     }
630 
631     // EWFIX.ADD full paths to output files ??
632 
633     // EWFIX.ADD warnings given
634 #if 0  // Potentially DEAD CODE (bobgian, Feb 2010)
635     TiXmlElement * warnings = MakeWarnings();
636     topItem->LinkEndChild(warnings);
637 #endif
638 
639     // this writes it all out -- file name already set at construction
640     // of m_doc
641     m_doc.SaveFile();
642 
643     return topItem;
644 }
645 
646 //____________________________________________________________________________________
647