1 #include "listfile.h"
2 #include <QtCore>
3 #include <QTextStream>
4 #include "deprecation.h"
5 
6 /*
7  * This file handles the ShelXl lst file during and after the refinement
8  *
9  * TODO:
10  * - Make running SHELXL more visual. e.g. progress bar (especially while writing cif file)
11  *
12  L.S.:
13  wR2 =  0.165168 before cycle   1 for   29437 data and   2607 /   2607 parameters
14  GooF = S =     1.509;     Restrained GooF =      1.434 for    3259 restraints
15  Mean shift/esd =   0.013  Maximum =     1.375 for  U23 C54        at 09:31:33
16  Max. shift = 0.008 A for C54        Max. dU =-0.010 for C54
17  *
18  CGLS:
19  wR2 =  0.165155 before cycle   1 for   29437 data and   2607 /   2607 parameters
20  GooF = S =     1.509;     Restrained GooF =      1.434 for    3257 restraints
21  Max. shift = 0.004 A for C54        Max. dU =-0.007 for C54
22  */
23 
ListFileHandler(QString lfpath,QObject * parent)24 ListFileHandler::ListFileHandler(QString lfpath, QObject *parent) : QObject(parent)
25 {
26   REL_RESTR_CARDS << "SAME"<< "SADI"<< "SIMU"<< "RIGU"<< "ISOR"<< "NCSY"<< "FLAT" << "DELU";
27   my_lstpath = lfpath;
28   //QFileSystemWatcher fw;  // The filesystem watcher doesn't work in my windows 8.1
29   //fw.addPath(*my_lstpath);  // I am not shure why I added it? It works also without. DK
30   success = false; // turns to true if refinement was a sucess.
31   listfcontent = new QStringList;
32   listfcontent->append(this->loadLst());
33 }
34 
35 
loadLst()36 QStringList ListFileHandler::loadLst(){
37   //! Reads the SHELXL lst file from disk
38   //! and returns it as a QStringlist
39   QFile file(my_lstpath);
40   QStringList lstList;
41   if (file.open(QFile::ReadOnly | QFile::Text)) {
42     QTextStream textStream(&file);
43     while (true) {
44       QString line = textStream.readLine();
45       if (line.isNull()) {
46           emit lstFinished();
47           break;
48       }
49       if (line.isEmpty()) {
50         continue;
51       } else {
52         lstList.append(line);
53       }
54     }
55   }
56   file.close();
57   return lstList;
58 }
59 
listfileparser()60 QVector<QStringList> ListFileHandler::listfileparser() {
61   /*!
62   Gathers the residuals of the SHELXL lst file.
63   It searches for the final cycle summary and then for " Disagreeable restraints".
64   End is reached with ' Summary of restraints'.
65   Ported from: https://github.com/dkratzert/DSR-db
66 
67  Disagreeable restraints before cycle    6
68 
69    Observed   Target    Error     Sigma     Restraint
70 
71                         0.1487    0.0400    SAME/SADI O1_1b C2_1b O1_1b C4_1b
72 
73   */
74   if (listfcontent->isEmpty()) {
75     QVector<QStringList> dummy;
76     return dummy;
77   }
78   bool disag = false;
79   bool final = false;
80   QVector<QStringList> disargeelist;
81   int num = 0;
82   foreach(QString line, *listfcontent) {
83     QStringList splitline;
84     splitline = line.split(" ", skipEmptyParts);
85     if (splitline.isEmpty()) {
86       continue;
87     }
88     if (splitline[0].startsWith("Observed")) {
89       continue;
90     }
91     if (line.startsWith(" Final Structure Factor")) {
92       final = true;
93     }
94     if (final && line.startsWith(" Disagreeable restraints")) {
95       disag = true;
96       continue;
97     }
98     if (line.startsWith(" Summary of restraints applied")) {
99       final = false;
100       disag = false;
101     }
102     if (line.contains("RIGU")) {
103       continue;  // leave out RIGU, it produces too many lines...
104     }
105     if (splitline.size() < 2) continue;
106     if (disag) {
107       // in this case, the desired line is found:
108       disargeelist.append(lineformatter(splitline));
109       num = num+1;
110       if (num > 500) {
111         // Cutting restraints list. Too many bad restraints...
112         return disargeelist;
113       }
114     }
115   }
116   return disargeelist;
117 }
118 
119 
lineformatter(QStringList line)120 QStringList ListFileHandler::lineformatter(QStringList line) {
121   /*!
122   takes care of some extra things with different restraints. For example
123   RIGU xy should be in one column.
124   Ported from: https://github.com/dkratzert/DSR-db
125   */
126   int num = 0;
127   int pos = 0;
128   foreach(QString i, line) {
129     i = i.replace('/', ' ');
130     if (i[0].isLetter()){
131       pos = num;
132       break;
133     }
134     num += 1;
135   }
136   // remove the part symbol from e.g. F1_2a:
137   /*
138   num = 0;
139   foreach (QString i, line) {
140     line[num] = remove_partsymbol(i);
141     num += 1;
142   } */
143   // joining columns without numbers:
144   QStringList posline = line.mid(pos, -1);
145   QString joinline = posline.join(" ");
146   QStringList newline = line.mid(0, pos);
147   line = newline;
148   line.append(joinline);
149   QString tline = line.join(" ");
150   foreach (QString n, REL_RESTR_CARDS) {
151     if (tline.contains(n, Qt::CaseInsensitive)) {
152       // adding placeholders for empty fields:
153       QStringList nline;
154       nline.append("-");
155       nline.append("-");
156       nline.append(line);
157       return nline;
158     }
159   }
160   return line;
161 }
162 
163 
table_maker(QVector<QStringList> tabledata)164 QString ListFileHandler::table_maker(QVector<QStringList> tabledata) {
165     /*!
166     Builds a html table out of a datalist from the final
167     cycle summary of a shelxl list file.
168     */
169     QStringList table;
170     QString header;
171     QString footer;
172     QString html;
173     table.clear();
174     foreach(QStringList line, tabledata) {
175       table.append(this->row(line));
176     }
177     header = QString(
178         "<body style=\"font-family:SansSerif; font-size:11pt;\">"
179         "<table>"
180         "<tr>"
181           "<td width=60% align=left>"
182           "<b>List of most disagreeable restraints:</b> &nbsp;"
183           "</td>"
184           "<td width=40% align=right>"
185           "</td>"
186         "</tr>"
187         "</table>");
188     footer = "</body>";
189     html = QString(
190       "%1"
191       "<table border=0 cellpadding=0 cellspacing=6 width=100% >"
192         "<tr>"
193            "<td align='center'> Observed </td>"
194            "<td align='center'> Target   </td>"
195            "<td align='center'> Error    </td>"
196            "<td align='center'> Sigma    </td>"
197            "<td align='left'> Restraint  </td>"
198         "</tr>"
199         "%2"
200       "</table>"
201         "%3").arg(header, table.join("\n"), footer);
202 
203     if(table.isEmpty()) {
204       //return header+empty_data;
205       return QString(header+"All restraint deviations are within their standard deviation.");
206       //return QString("");
207     }
208     return html;
209 }
210 
row(QStringList rowdata)211 QString ListFileHandler::row(QStringList rowdata) {
212     /*!
213     creates a table row for the restraints list.
214     :type rowdata: list
215     */
216     QStringList td;
217     td.clear();
218     QString row = "";
219     QString bgcolor = "";
220     if (rowdata.size() >= 5) {
221       bool ok1 = false;
222       bool ok2 = false;
223       rowdata[2].toFloat(&ok1);
224       rowdata[3].toFloat(&ok2);
225       if (ok1 && ok2) {
226         if (qAbs(rowdata.at(2).toDouble()) > 2.5*rowdata[3].toDouble()) {
227           bgcolor = QString("bgcolor='#ffec99'"); // bgcolor='#FFD100'  // yellow
228         }
229         if (qAbs(rowdata.at(2).toDouble()) > 3.5*rowdata[3].toDouble()) {
230           bgcolor = QString("bgcolor='#ff9fac'"); // bgcolor='#FF1030'  // red
231         }
232       }
233     }
234     int num = 0;
235     foreach (QString item, rowdata) {
236       bool ok = false;
237       item.toDouble(&ok);
238       if (ok) {
239         // align right for numbers:
240         if (num < 2) {
241           // do not colorize the first two columns:
242           td.append(QString("<td align='right'> %1 </td>").arg(item));
243         } else {
244           td.append(QString("<td align='right' %1> %2 </td>").arg(bgcolor, item));
245         }
246       } else {
247         if (item.startsWith('-')) {
248           // only a minus sign
249           td.append(QString("<td align='center'> %1 </td>").arg(item));
250         } else {
251           if (num < 4) {
252             td.append(QString("<td align='right'> %1 </td>").arg(item));
253             continue;
254           }
255           // align left for words:
256           td.append(QString("<td align='left'> %1 </td>").arg(item));
257         }
258       }
259       num += 1;
260     }
261     if (td.isEmpty()) {
262       row = "<tr> No (disagreeable) restraints found in .lst file. </tr>";
263     } else {
264       row = QString("<tr> %1 </tr>").arg(td.join(""));
265     }
266     td.clear();
267     return row;
268 }
269 
restraintsTable()270 QString ListFileHandler::restraintsTable() {
271   QString txt;
272   txt.append(table_maker(listfileparser()));
273   return txt;
274 }
275 
getSuccess() const276 bool ListFileHandler::getSuccess() const {
277     return success;
278   }
279 
setSuccess(bool value)280 void ListFileHandler::setSuccess(bool value) {
281     success = value;
282   }
283 
thetaFromLst()284 QString ListFileHandler::thetaFromLst() {
285   //! Max. 2-theta =   55.30
286   //!   -8 =< h =<  8,    -21 =< k =< 21,    -21 =< l =< 21,   Max. 2-theta =   55.30
287   //! I might need this later
288   QString theta = "";
289   foreach(QString line, *listfcontent) {
290     if (line.contains("Max. 2-theta")) {
291       theta = line.section("theta", 1, 1).split(" ", skipEmptyParts).at(1);
292     }
293   }
294   return theta;
295 }
296 
297 
298 // ##########################################################################
299 
300 
XLOutputParser(QObject * parent)301 XLOutputParser::XLOutputParser(QObject *parent) : QObject(parent) {
302   //! Handles the content of the SHELXL list file.
303   //! Methods with "current" in the name return values of the current state
304   //! in the continuously growing SHELXL list file.
305   //! Methods with "final" return the values after successful refinement.
306   //!
307   // The constructor:
308   ls_cycles = 0;
309   listf_data.cmdline = "";
310   listf_data.xlversion = "";
311   listf_data.threads = "";
312   listf_data.procs = "";
313   listf_data.rint = "";
314   listf_data.rsigma = "";
315   listf_data.wr2 = "";
316   listf_data.cycle = "";
317   listf_data.data = "0";
318   listf_data.current_parameters = "0";
319   listf_data.all_parameters = "0";
320   listf_data.curGoof = " ";
321   listf_data.curRGoof = " ";
322   listf_data.R1_4sig = "";
323   listf_data.finalwR2 = "";
324   listf_data.maxshift_esd = "";
325   listf_data.maxshiftfor = "";
326   listf_data.meanshift = "";
327   listf_data.maxshift = "";
328   listf_data.dU = "";
329   listf_data.dUfor = "";
330   listf_data.peak = "";
331   listf_data.hole = "";
332   listf_data.peakpos = "";
333   listf_data.holepos = "";
334   listf_data.peakdist = "";
335   listf_data.holedist = "";
336   listf_data.warning = "";
337   listf_data.warnings.clear();
338   listf_data.time = "";
339 
340   rgr.Goof = 0.0;
341   rgr.cycle = 0.0;
342   rgr.dU = 0.0;
343   rgr.dUfor = "";
344   rgr.wR2 = 0.0;
345   rgr.final_wR2 = 0.0;
346   rgr.max_shift_esd_for = "";
347   rgr.maxshift = 0.0;
348   rgr.maxshift_esd = 0.0;
349   rgr.maxshiftfor = "";
350   rgr.meanshift = 0.0;
351   rgr.warnings.clear();
352   rgr.time = 0.0;
353   warnings.clear();
354 }
355 
356 
residualsGraphDisplay()357 RGraph XLOutputParser::residualsGraphDisplay() {
358   //! Returns a data structure that collects the values of
359   //! wR2, R1, Goof, ... during refinement to display them as
360   //! a graph.
361   // collect data from each line and append it to RGraph
362   rgr.Goof = listf_data.curGoof.toDouble();
363   rgr.wR2 = listf_data.wr2.toDouble();
364   rgr.maxshift = listf_data.maxshift.toDouble();
365   rgr.meanshift = listf_data.meanshift.toDouble();
366   rgr.cycle = listf_data.cycle.toInt();
367   rgr.final_wR2 = listf_data.finalwR2.toDouble();
368   rgr.warnings = listf_data.warnings;
369   return rgr;
370 }
371 
372 
fillListFileData(QString line)373 void XLOutputParser::fillListFileData(QString line) {
374   //! Fills listf_data with data but it is only filled
375   //! if the respective strings contain data
376   QStringList tproc = xl_threads(line);
377   QString cmdline = xl_cmdline(line);
378   QString version = xl_version(line);
379   QStringList rintsig = xl_rint_rsigma(line);
380   QStringList wr2data = xl_current_wr2_data_param(line);
381   QString R1_4sig = xl_final_R1_4sig(line);
382   QString R1_all = xl_final_R1all(line);
383   QString finalwR2 = xl_final_wR2(line);
384   QStringList goofs = xl_current_goof(line);
385   QStringList peakvalues = xl_peak(line);
386   QStringList holevalues = xl_hole(line);
387   //QStringList fgoofs = xl_final_goofs(line);
388   QStringList maxshifts = xl_current_max_shift(line);
389   QStringList meanshifts = xl_current_meanshift_esd(line);
390   xl_collect_warnings(line);
391   QStringList flackvals = xl_flacksvals(line);
392   QString timeval = xl_timeval(line);
393 
394   // Only fill listf_data with values if there are any:
395   if (!cmdline.isEmpty()) {
396     listf_data.cmdline = cmdline;
397   }
398   if (!version.isEmpty()) {
399     listf_data.xlversion = version;
400   }
401   if ((!tproc[0].isEmpty()) && (!tproc[1].isEmpty())) {
402     listf_data.threads = tproc[0];
403     listf_data.procs = tproc[1];
404   }
405   if ((!rintsig[0].isEmpty()) && (!rintsig[1].isEmpty())) {
406     listf_data.rint = rintsig[0];
407     listf_data.rsigma = rintsig[1];
408   }
409   if (!wr2data.at(0).isEmpty()) {
410     listf_data.wr2 = wr2data.at(0);
411   }
412   if (!wr2data.at(1).isEmpty()) {
413     emit newLScycle();
414     listf_data.cycle = wr2data.at(1);
415   }
416   if (!wr2data.at(2).isEmpty()) {
417     listf_data.data = wr2data.at(2);
418   }
419   if (!wr2data.at(3).isEmpty()) {
420     listf_data.current_parameters = wr2data.at(3);
421   }
422   if (!wr2data.at(4).isEmpty()) {
423     listf_data.all_parameters = wr2data.at(4);
424   }
425   if (!goofs[0].isEmpty() && !goofs[1].isEmpty()) {
426     listf_data.curGoof = goofs[0];
427     listf_data.curRGoof = goofs[1];
428   }
429   if (!goofs[2].isEmpty()) {
430     listf_data.nrestraints = goofs[2];
431   }
432   if (!R1_4sig.isEmpty()) {
433     listf_data.R1_4sig = R1_4sig;
434   }
435   if (!R1_all.isEmpty()) {
436     listf_data.R1_all = R1_all;
437   }
438   if (!finalwR2.isEmpty()) {
439     listf_data.finalwR2 = finalwR2;
440   }
441   if (!maxshifts[0].isEmpty() && !maxshifts[1].isEmpty()) {
442     //shifts.append(maxshift);
443     //shifts.append(maxshiftfor);
444     //shifts.append(dU);
445     //shifts.append(dUfor);
446     listf_data.maxshift = maxshifts[0];
447     listf_data.maxshiftfor = maxshifts[1];
448     listf_data.dU = maxshifts[2];
449     listf_data.dUfor = maxshifts[3];
450   }
451   if (!meanshifts[0].isEmpty() && !meanshifts[2].isEmpty()) {
452     //shifts.append(meanshift);
453     //shifts.append(maxshift_esd);
454     //shifts.append(max_esd_shiftfor);
455     listf_data.meanshift = meanshifts[0];
456     listf_data.maxshift_esd = meanshifts[1];
457     listf_data.max_shift_esd_for = meanshifts[2];
458   }
459   if (!holevalues[0].isEmpty() && !holevalues[1].isEmpty() && !holevalues[2].isEmpty()
460       && !holevalues[3].isEmpty()) {
461     listf_data.hole = holevalues[0];
462     listf_data.holepos = holevalues[1];
463     listf_data.holedist = holevalues[2];
464     listf_data.holefrom = holevalues[3];
465   }
466   if (!peakvalues[0].isEmpty() && !peakvalues[1].isEmpty() && !peakvalues[2].isEmpty()
467       && !peakvalues[3].isEmpty()) {
468     listf_data.peak = peakvalues[0];
469     listf_data.peakpos = peakvalues[1];
470     listf_data.peakdist = peakvalues[2];
471     listf_data.peakfrom = peakvalues[3];
472   }
473   if (!flackvals[0].isEmpty()) {
474     listf_data.flack = flackvals.at(0);
475   }
476   if (!flackvals[1].isEmpty()) {
477     listf_data.parson = flackvals.at(1);
478   }
479   if (!timeval.isEmpty()) {
480     listf_data.time = timeval;
481   }
482 }
483 
residualsTextDisplay(QString & line,int ls_cycles)484 QString XLOutputParser::residualsTextDisplay(QString &line, int ls_cycles) {
485   //! returns a html string to display the refinement results in a QLabel
486   this->ls_cycles = ls_cycles;
487   fillListFileData(line);
488   QString html_output = "";
489   QString html_final = "";
490   QString wr2line = "";
491   QString shiftyellow = "";
492   QString shift_esd_yellow = "";
493   QString dUyellow = "";
494   QString cycleText = "";
495   QString progressbar = "";
496   float restr_ratio = 0.0;
497   float data_to_param_ratio = 0.0;
498   if (!listf_data.data.isEmpty() && !listf_data.nrestraints.isEmpty() && !listf_data.all_parameters.isEmpty()){
499     restr_ratio = (listf_data.data.toDouble()+listf_data.nrestraints.toInt())/listf_data.all_parameters.toDouble();
500     data_to_param_ratio = listf_data.data.toFloat()/listf_data.all_parameters.toFloat();
501   }
502   if (!listf_data.R1_4sig.isEmpty() && !listf_data.finalwR2.isEmpty()) { // final values:
503     html_final = QString("<tr><td align=right><b><i>wR</i><sub>2</sub> = </b></td> <td><b> %2% </b>(all data) </td>"
504                          "    <td align=right><b><i>R</i><sub>1</sub></b> [<i>I</i> > 4&sigma;<i>I</i>] = </td> <td><b>%1%</b> (%9% all data)</td></tr>"
505                          "<tr><td align=right>Highest peak = </td> <td> %3 e&Aring;<sup>-3</sup> at %4 &Aring; from %7 &nbsp;&nbsp;&nbsp;&nbsp;</td>"
506                          "    <td align=right>Deepest hole = </td> <td> %5 e&Aring;<sup>-3</sup> at %6 &Aring; from %8 </td></tr>"
507                          )
508           .arg(listf_data.R1_4sig.toDouble()*100, 4, 'f', 2, 0)      // 1
509           .arg(listf_data.finalwR2.toDouble()*100, 4, 'f', 2, 0)     // 2
510           .arg(listf_data.peak)           // 3
511           .arg(listf_data.peakdist)       // 4
512           .arg(listf_data.hole)           // 5
513           .arg(listf_data.holedist)       // 6
514           .arg(listf_data.peakfrom)       // 7
515           .arg(listf_data.holefrom)       // 8
516           .arg(listf_data.R1_all.toDouble()*100, 4, 'f', 2, 0)       // 9
517         ;
518     // One of flack or parson exists:
519     if (!listf_data.flack.isEmpty() || !listf_data.parson.isEmpty()) {
520       // both exist:
521       if (!listf_data.flack.isEmpty() && !listf_data.parson.isEmpty()) {
522         wr2line = QString("<tr><td align=right> <b>Flack X </b>= </td> <td> %1 (Parsons' method)</td> <td>  </td> </tr> "
523                           "<tr><td align=right> </td> <td> %2 (classical fit)</td> <td>  </td> </tr>").arg(listf_data.parson).arg(listf_data.flack);
524       }
525       // Only classical exists:
526       if (!listf_data.flack.isEmpty() && listf_data.parson.isEmpty()) {
527         wr2line = QString("<tr><td align=right> <b>Flack X </b>= </td> <td> %1 (classical fit)</td> "
528                                   "<td>  </td> </tr>").arg(listf_data.flack);
529       }
530       // Only Parsons exists:
531       if (!listf_data.parson.isEmpty() && listf_data.flack.isEmpty()){
532         wr2line = QString("<tr><td align=right> <b>Flack X </b>= </td> <td> %1 (Parsons' method)</td> "
533                            "<td>  </td> </tr>").arg(listf_data.parson);
534       }
535     } else { // no flack parameter:
536       wr2line = ""; // No Table row to prevent empty line
537     }
538   } else {  // shelxl is currently runnig:
539     html_final = "";
540     wr2line = QString("<tr><td align=right> <i>wR</i><sub>2</sub> =   </td> <td> %7% </td></tr>")
541                       .arg(listf_data.wr2.toDouble()*100, 4, 'f', 2);
542   }
543   if (qAbs(listf_data.maxshift.toDouble()) > 0.005) {
544     shiftyellow = QString("bgcolor=#ffec99");
545   }
546   if (qAbs(listf_data.maxshift_esd.toDouble()) > 0.01) {
547     shift_esd_yellow = QString("bgcolor=#ffec99");
548   }
549   if (qAbs(listf_data.dU.toDouble()) > 0.005) {
550     dUyellow = QString("bgcolor=#ffec99");
551   }
552   int repeat = 12;
553   if (listf_data.cycle.toInt() < ls_cycles) {
554     int progress;
555     progress = int((listf_data.cycle.toFloat() / ls_cycles)*10);
556     progressbar = QString("<td bgcolor=#69CB3F >%1</td>").arg(QString("&nbsp;").repeated(repeat)).repeated(progress)
557                   +QString("<td bgcolor=#D0D0D0 >%1</td>").arg(QString("&nbsp;").repeated(repeat)).repeated(10-progress);
558     cycleText = QString("<tr><td> <b>Cycle %1 of %2</b> </td></tr>")
559         .arg(listf_data.cycle) // 1
560         .arg(ls_cycles)        // 2
561         ;
562   } else {
563     cycleText = QString("<tr><td> <b>Final Cycle of %1</b></td></tr>")
564         .arg(ls_cycles);       // 1
565     progressbar = QString("<td bgcolor=#69CB3F>%1</td>").arg(QString("&nbsp;").repeated(repeat)).repeated(10);
566   }
567   html_output = QString(
568         "<body style=\"font-family:SansSerif; font-size:11pt;\">"
569         "<H2>Running SHELXL %1</H2> "
570         "<table cellpadding=3% align=left>"
571         "<tr><td colspan=2 align=left> <table><tr> %2 </tr></table> </td></tr>"
572         "<tr><td> %3 threads on %4 processors </td></tr>"
573         "<tr><td> Using %5 data and %6 of %7 parameters (ratio %8; %11 with restraints) </td></tr>"
574         "<tr><td><i>R</i><sub>int</sub> = %9%, <i>R</i><sub>&sigma;</sub> = %10% </td></tr>"
575         "</table>")
576       .arg(listf_data.xlversion)            // 1
577       .arg(progressbar)                     // 2
578       .arg(listf_data.threads)              // 3
579       .arg(listf_data.procs)                // 4
580       .arg(listf_data.data)                 // 5
581       .arg(listf_data.current_parameters)   // 6
582       .arg(listf_data.all_parameters)       // 7
583       .arg(data_to_param_ratio, 10, 'f', 1) // 8  data/restr. ratio
584       .arg(listf_data.rint.toFloat()*100, 10, 'f', 2)     // 9
585       .arg(listf_data.rsigma.toFloat()*100, 10, 'f', 2)   // 10
586       .arg(restr_ratio, 10, 'f', 1) // 11  (data+restraints)/parameter
587       ;
588 
589   QString maxshift_row = "";
590 
591   if (!listf_data.maxshift_esd.isEmpty()) {
592     maxshift_row = QString("<tr><td align=right> Max. shift = </td> <td %1> %2 @  %3 </td> </tr>")
593       .arg(shift_esd_yellow)                 // 1 color of max shifting param.
594       .arg(listf_data.maxshift_esd)          // 2
595       .arg(listf_data.max_shift_esd_for)     // 3
596       ;
597   }
598 
599   QString xyz_shift_row = "";
600 
601   if (!listf_data.maxshift.isEmpty()) {
602     xyz_shift_row = QString("<tr><td align=right> Max. xyz shift =     </td> <td %1> %2 &Aring; @ %3     </td>"
603                           "    <td align=right> Max. &Delta;U =      </td> <td %4> %5 @ %6             </td></tr>")
604         .arg(shiftyellow)                // 1
605         .arg(listf_data.maxshift)        // 2
606         .arg(listf_data.maxshiftfor)     // 3
607         .arg(dUyellow)                   // 4
608         .arg(listf_data.dU)              // 5
609         .arg(listf_data.dUfor)           // 6
610       ;
611   }
612 
613   QString html_output2 = QString("<table cellpadding=2%>"
614           "%1"
615           "%2"
616           "%3"
617           "<tr><td align=right> <i>GooF</i> =             </td> <td> %4                     </td>"
618           "    <td align=right  width=18%> Restr. <i>GooF</i> =  </td> <td> %5              </td></tr>"
619           "%6"
620           "%7"
621           "</table>"
622           "<table><tr><td bgcolor=#ffec99><b> %8 </b></td></tr></table>"
623           "</body>")
624           .arg(cycleText)                        // 1
625           .arg(html_final)                       // 2
626           .arg(wr2line)                          // 3 //wr2
627           .arg(listf_data.curGoof)               // 4
628           .arg(listf_data.curRGoof)              // 5
629           .arg(xyz_shift_row)                    // 6
630           .arg(maxshift_row)                     // 7
631           .arg(listf_data.warnings.join("<br>")) // 8
632 
633       ;
634   if (!listf_data.wr2.isEmpty()) {
635     html_output += html_output2;
636   } else {
637     // In this case, no refinement happened:
638     if (!listf_data.warnings.isEmpty()) {
639       // even if no refinenemt happened, I want to display the warnings:
640       html_output += QString("<table><tr><td bgcolor=#ffec99> %2 </td></tr></table>")
641           .arg(listf_data.warnings.join("<br>"));
642     } else {
643       // no warning, but still waiting for output from SHLEXL:
644       html_output += QString("<b>Waiting for output from SHELXL...</b>");
645     }
646   }
647   if (!listf_data.time.isEmpty() && listf_data.wr2.isEmpty()) {
648     // SHELXL finished regularly (time elapsed output), but no refinement happened (wr2 is empty)
649     html_output += QString("<br><b>SHELXL failed to run! Please inspect the instructions.</b>");
650   }
651   return html_output;
652 }
653 
654 
xl_version(QString line)655 QString XLOutputParser::xl_version(QString line) {
656   //! Returns the running shelxl version
657   //! " +  Copyright(C) George M. Sheldrick 1993-2014     Version 2014/7  +"
658   QRegExp versionregex = QRegExp("Version\\s+\\d+\\/\\d+");
659   QString version;
660   QStringList linesplit;
661   if (line.contains(versionregex)) {
662     linesplit = line.split(" ", skipEmptyParts);
663     int index = linesplit.indexOf("Version");
664     version = linesplit.at(index+1);
665   }
666   return version;
667 }
668 
xl_threads(QString line)669 QStringList XLOutputParser::xl_threads(QString line) {
670   //! Returns on how many threads and processors shelxl is running
671   //! "Running  6 threads on  8 processors"
672   QStringList threadproc;
673   QString threads = "";
674   QString processors = "";
675   QStringList linesplit;
676   QRegExp tprocregex = QRegExp("Running\\s+\\d\\s+threads\\s+on\\s+\\d\\s+processors");
677   if (line.contains(tprocregex)) {
678     linesplit = line.split(" ", skipEmptyParts);
679     threads = linesplit.at(1);
680     processors = linesplit.at(4);
681   }
682   threadproc.append(threads);
683   threadproc.append(processors);
684   return threadproc;
685 }
686 
xl_cmdline(QString line)687 QString XLOutputParser::xl_cmdline(QString line) {
688   //! Returns the command line options shelxl was started with
689   //! " Command line parameters: p21c -a50000 -b3000 -c624 -t6"
690   QString cmdline;
691   if (line.contains("Command line parameters:")) {
692     cmdline = line;
693   }
694   return cmdline;
695 }
696 
xl_current_wr2_data_param(QString line)697 QStringList XLOutputParser::xl_current_wr2_data_param(QString line) {
698   //! Returns the number of data (reflections) shelxl is refining against.
699   //! This is without sigma cutoff:
700   //! " wR2 = 0.4016 before cycle   1 for   10786 data and    737 /    737 parameters"
701   //! returns list of [wR2, cycles, data, current_parameters, all_parameters]
702   QStringList linesplit;
703   QString wr2 = "";
704   QString cycle = "";
705   QString data = "";
706   QString current_parameters = "";
707   QString all_parameters = "";
708   QStringList alldata;
709   if (line.contains("wR2") && line.contains("cycle") && line.contains("data")
710       && (line.split(" ", skipEmptyParts).size() >= 12) ) {
711     linesplit = line.split(" ", skipEmptyParts);
712     wr2 = linesplit.at(2);
713     cycle = linesplit.at(5);
714     data = linesplit.at(7);
715     current_parameters = linesplit.at(10);
716     all_parameters = linesplit.at(12);
717   }
718   alldata.append(wr2);
719   alldata.append(cycle);
720   alldata.append(data);
721   alldata.append(current_parameters);
722   alldata.append(all_parameters);
723   return alldata;
724 }
725 
xl_rint_rsigma(QString line)726 QStringList XLOutputParser::xl_rint_rsigma(QString line) {
727   //! Returns Rint and Rsigma of the dataset.
728   //! " R(int) = 0.0504     R(sigma) = 0.0585      Friedel opposites merged"
729   //chuebsch: this crashed:
730   //Data:      19 unique,      0 suppressed   R(int) = 0.0000   R(sigma) =13.2352
731   //
732   QString rint = "";
733   QString rsigma = "";
734   QStringList linesplit, rvals;
735   QString l=line.remove('=');
736   if (line.contains("R(sigma)")) {
737     linesplit = l.split(" ", skipEmptyParts);
738     int RintInd = linesplit.indexOf(QRegExp("R\\(int\\)"));
739     if ((RintInd != -1) && linesplit.length() > RintInd+1) {//>= leads to segmentation fault
740       rint = linesplit.at(RintInd+1);
741     }
742     int RsigInd = linesplit.indexOf(QRegExp("R\\(sigma\\)"));
743     if ((RsigInd != -1) && linesplit.length() > RsigInd+1) {//>= leads to segmentation fault
744       rsigma = linesplit.at(RsigInd+1);
745     }//else qDebug()<<linesplit<<RsigInd;
746 
747   }
748   rvals.append(rint);
749   rvals.append(rsigma);
750   return rvals;
751 }
752 
xl_current_goof(QString line)753 QStringList XLOutputParser::xl_current_goof(QString line) {
754   //! Returns the current Goof of the refinement. This value is
755   //! the last value in the continuesly growing lst file.
756   //! "GooF = S =     4.130;     Restrained GooF =      5.432 for    1662 restraints"
757   QString goof;
758   QString rgoof;
759   QString restraints;
760   QStringList linesplit;
761   QStringList goofs;
762   if (line.contains("Restrained GooF") && line.contains("restraints")
763       && (line.split(" ", skipEmptyParts).size() >= 8)) {
764     linesplit = line.split(" ", skipEmptyParts);
765     goof = linesplit.at(4);
766     goof.remove(QChar(';'));
767     rgoof = linesplit.at(8);
768     restraints = linesplit.at(10);
769   }
770   goofs.append(goof);
771   goofs.append(rgoof);
772   goofs.append(restraints);
773   return goofs;
774 }
775 
xl_final_goofs(QString line)776 QStringList XLOutputParser::xl_final_goofs(QString line) {
777   //! Returns the final Goof after refinement finished.
778   //! "wR2 =  0.2277,  GooF = S =   2.275,  Restrained GooF =    2.122  for all data"
779   QString goof;
780   QString rgoof;
781   QStringList linesplit;
782   QStringList goofs;
783   if (line.contains("for all data") && (line.split(" ", skipEmptyParts).size() >= 11) ) {
784     linesplit = line.split(" ", skipEmptyParts);
785     goof = linesplit.at(7);
786     goof.remove(QChar(','));
787     rgoof = linesplit.at(11);
788   }
789   goofs.append(goof);
790   goofs.append(rgoof);
791   return goofs;
792 }
793 
794 
xl_current_meanshift_esd(QString line)795 QStringList XLOutputParser::xl_current_meanshift_esd(QString line) {
796   //! Returns the current shift per esd.
797   //! "Mean shift/esd =   0.016  Maximum =    -8.976 for  U11 C6         at 09:36:30
798   QStringList shifts;
799   QString meanshift = "";
800   QString maxshift_esd = "";
801   QString max_esd_shiftfor = "";
802   QStringList linesplit;
803   if (line.contains("shift/esd") && line.contains("Maximum")
804       && (line.split(" ", skipEmptyParts).size() >= 6) ) {
805     linesplit = line.split(" ", skipEmptyParts);
806     meanshift = linesplit.at(3);
807     maxshift_esd = linesplit.at(6);
808     max_esd_shiftfor = line.split("for", skipEmptyParts).at(1).trimmed()
809         .split("at", skipEmptyParts).at(0).trimmed();
810   }
811   shifts.append(meanshift);
812   shifts.append(maxshift_esd);
813   shifts.append(max_esd_shiftfor);
814   return shifts;
815 }
816 
817 
xl_current_max_shift(QString line)818 QStringList XLOutputParser::xl_current_max_shift(QString line) {
819   //! Returns the maximum shift during the refinement.
820   //! "Max. shift = 0.000 A for H26C      Max. dU =-0.007 for C6"
821   //! "=-0.007 for C6"
822   QString maxshift;
823   QString maxshiftfor;
824   QString dU;
825   QString dUfor;
826   QStringList linesplit;
827   QStringList shifts;
828   if (line.contains("Max. shift") && line.contains("dU") && line.contains("for")
829       && (line.split(" ", skipEmptyParts).size() >= 6) ) {
830     linesplit = line.split(" ", skipEmptyParts);
831     maxshift = linesplit.at(3);
832     maxshiftfor = linesplit.at(6);
833     dU = line.split("dU").at(1).trimmed().remove("=").split(" ", skipEmptyParts).at(0);
834     dUfor = line.split("for").at(2).trimmed();
835   }
836   shifts.append(maxshift);
837   shifts.append(maxshiftfor);
838   shifts.append(dU);
839   shifts.append(dUfor);
840   return shifts;
841 }
842 
843 
xl_current_warning(QString line)844 QString XLOutputParser::xl_current_warning(QString line) {
845   //! Returns a Warning if there is any in the current line.
846   QRegExp warnregexp = QRegExp("\\*\\*");  //.*\\*\\*");
847   QString warn = "";
848   if ( line.contains(warnregexp) ) {
849     warn = line.simplified();
850   }
851   return warn;
852 }
853 
xl_collect_warnings(QString line)854 void XLOutputParser::xl_collect_warnings(QString line) {
855   //! Returns all warning that occoured during refinement.
856   QRegExp warnregexp = QRegExp("\\*\\*");  //.*\\*\\*");
857   if ( line.contains(warnregexp) ) {
858     warnings.append(line.simplified());
859   }
860   if (line.contains("CANNOT OPEN FILE")) {
861     warnings.append(line.simplified());
862   }
863   listf_data.warnings = warnings;
864   rgr.warnings = warnings;
865 }
866 
xl_final_R1_4sig(QString line)867 QString XLOutputParser::xl_final_R1_4sig(QString line) {
868   // R1 =  0.0401 for    7085 Fo > 4sig(Fo)  and  0.0796 for all   10786 data
869   QString r1_4sigma = "";
870   QStringList linesplit;
871   if (line.contains("R1") && line.contains("4sig") && line.contains("for all")) {
872     linesplit = line.split(" ", skipEmptyParts);
873     r1_4sigma = linesplit.at(2);
874     r1_4sigma.remove(QChar(','));
875   }
876   return r1_4sigma;
877 }
878 
xl_final_R1all(QString line)879 QString XLOutputParser::xl_final_R1all(QString line) {
880   // R1 =  0.0766 for  10786 unique reflections after merging for Fourier
881   QString r1all = "";
882   QStringList linesplit;
883   if (line.contains("R1") && line.contains("merging for Fourier") && line.contains("for")) {
884     linesplit = line.split(" ", skipEmptyParts);
885     r1all = linesplit.at(2);
886     r1all.remove(QChar(','));
887   }
888   return r1all;
889 }
890 
891 
xl_final_wR2(QString line)892 QString XLOutputParser::xl_final_wR2(QString line) {
893   QString finalwr2;
894   QStringList linesplit;
895   if (line.contains("wR2") && line.contains("for all data")) {
896     linesplit = line.split(" ", skipEmptyParts);
897     finalwr2 = linesplit.at(2);
898     finalwr2.remove(QChar(','));
899   }
900   return finalwr2;
901 }
902 
xl_peak(QString line)903 QStringList XLOutputParser::xl_peak(QString line) {
904   //! Returns a list of the highest peak and the position
905   // Highest peak    8.15  at  0.3160  0.7496  0.4960  [  1.30 A from F12 ]
906   QStringList values;
907   QString peak = "";
908   QString coord = "";
909   QString dist = "";
910   QString from = "";
911   QStringList linesplit;
912   if (line.contains("Highest peak") && line.contains("from")
913       && (line.split(" ", skipEmptyParts).size() >= 6) ) {
914     linesplit = line.split(" ", skipEmptyParts);
915     peak = linesplit.at(2);
916     coord = linesplit.at(4)+" "+linesplit.at(5)+" "+linesplit.at(6);
917     dist = line.split("[").at(1).split("]").at(0).trimmed().split(" ").at(0);
918     from = line.split("[").at(1).split("]").at(0).trimmed().split(" ").at(3);
919   }
920   values.append(peak);
921   values.append(coord);
922   values.append(dist);
923   values.append(from);
924   return values;
925 }
926 
xl_hole(QString line)927 QStringList XLOutputParser::xl_hole(QString line) {
928   //! Returns a list of the deepest hole and the position
929   // Deepest hole   -0.74  at  0.4337  0.4402  0.7821  [  0.84 A from GA1 ]
930   QStringList values;
931   QString hole = "";
932   QString coord = "";
933   QString dist = "";
934   QString from = "";
935   QStringList linesplit;
936   if (line.contains("Deepest hole") && line.contains("from")
937       && (line.split(" ", skipEmptyParts).size() >= 12) ) {
938     linesplit = line.split(" ", skipEmptyParts);
939     hole = linesplit.at(2);
940     coord = linesplit.at(4)+" "+linesplit.at(5)+" "+linesplit.at(6);
941     dist = line.split("[").at(1).split("]").at(0).trimmed().split(" ").at(0);
942     from = line.split("[").at(1).split("]").at(0).trimmed().split(" ").at(3);
943   }
944   values.append(hole);
945   values.append(coord);
946   values.append(dist);
947   values.append(from);
948   return values;
949 }
950 
xl_flacksvals(QString line)951 QStringList XLOutputParser::xl_flacksvals(QString line) {
952   //! Flack x =   -0.449(792) by classical fit to all intensities
953   //! Flack x =   -0.315(296) from 3107 selected quotients (Parsons' method)
954   QStringList values;
955   QString flack = "";
956   QString parson = "";
957   QStringList linesplit;
958   if (line.contains("Flack x") && line.contains("classical fit")
959       && (!line.contains("No quotients"))
960       && (line.split(" ", skipEmptyParts).size() >= 3) ) {
961     linesplit = line.split(" ", skipEmptyParts);
962     flack = linesplit.at(3);
963   }
964   if (line.contains("Flack x") && line.contains("Parsons' method")
965       && (line.split(" ", skipEmptyParts).size() >= 3) ) {
966     linesplit = line.split(" ", skipEmptyParts);
967     parson = linesplit.at(3);
968 
969   }
970   values.append(flack);
971   values.append(parson);
972   return values;
973 }
974 
xl_timeval(QString line)975 QString XLOutputParser::xl_timeval(QString line) {
976   //! +  pn_a          finished at 12:33:29   Total elapsed time:      0.00 secs  +
977   //! returns the elapsed time of a SHELXL run
978   QStringList linesplit;
979   QString time = "";
980   if (line.contains("finished") && line.contains("time")) {
981     linesplit = line.split(" ", skipEmptyParts);
982     time = linesplit.at(linesplit.indexOf("time:")+1);
983   }
984   return time;
985 }
986