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