UltraScan III
us_dcdt.cpp
Go to the documentation of this file.
1 
3 #include <QApplication>
4 #include <QtSvg>
5 
6 #include "us_dcdt.h"
7 #include "us_license_t.h"
8 #include "us_license.h"
9 #include "us_settings.h"
10 #include "us_gui_settings.h"
11 
13 // the class US_Dcdt.
14 
15 int main( int argc, char* argv[] )
16 {
17  QApplication application( argc, argv );
18 
19  #include "main1.inc"
20 
21  // License is OK. Start up.
22 
23  US_Dcdt w;
24  w.show();
25  return application.exec();
26 }
27 
29 {
30  setWindowTitle( tr( "Time Derivative - dC/dt Analysis" ) );
31 
32  te_results = NULL;
33  sMax = 1000.0;
34 
35  //QLabel* lb_aux = us_banner( tr( "dC/dt Auxiliary Controls" ) );
36  QLabel* lb_sValue = us_label( tr( "S-value Cutoff:" ) );
37  ct_sValue = us_counter( 3, 0, 20, 20 );
38  ct_sValue->setStep( 0.1 );
39  connect( ct_sValue, SIGNAL( valueChanged ( double ) ),
40  SLOT ( sMaxChanged ( double ) ) );
41 
42  QLabel* lb_graph = us_label( tr( "Graph Selection" ) );
43  lb_graph->setAlignment ( Qt::AlignCenter );
44 
45  QGridLayout* rb_layout1 = us_radiobutton( tr( "x:radius" ), rb_radius,false);
46  QGridLayout* rb_layout2 = us_radiobutton( tr( "x:S" ), rb_sed, false );
47  QGridLayout* rb_layout3 = us_radiobutton( tr( "Average S" ), rb_avg, true );
48 
49  QButtonGroup* group = new QButtonGroup( this );
50  group->addButton( rb_radius, 0 );
51  group->addButton( rb_sed , 1 );
52  group->addButton( rb_avg , 2 );
53  graphType = 2;
54  connect( group, SIGNAL( buttonClicked( int ) ), SLOT( set_graph( int ) ) );
55 
56  QBoxLayout* rb_layout0 = new QHBoxLayout();
57  rb_layout0->addLayout( rb_layout1 );
58  rb_layout0->addLayout( rb_layout2 );
59  rb_layout0->addLayout( rb_layout3 );
60 
61  int row = 5;
62  QLabel* lb_bPos = us_label( tr( "Boundary Pos (%):" ) );
63  controlsLayout->addWidget( lb_bPos, row, 0, 1, 1 );
64  controlsLayout->addWidget( ct_boundaryPos, row++, 1, 1, 1 );
65 
66  //controlsLayout->addWidget( lb_aux, row++, 0, 1, 2 );
67 
68  controlsLayout->addWidget( lb_sValue, row, 0, 1, 1 );
69  controlsLayout->addWidget( ct_sValue, row++, 1, 1, 1 );
70 
71  controlsLayout->addWidget( lb_graph, row++, 0, 1, 3 );
72  controlsLayout->addLayout( rb_layout0, row++, 0, 1, 3 );
73 
74  connect( pb_help, SIGNAL( clicked() ), SLOT( help() ) );
75  connect( pb_view, SIGNAL( clicked() ), SLOT( view() ) );
76  connect( pb_save, SIGNAL( clicked() ), SLOT( save() ) );
77 
78  ct_boundaryPos ->disconnect();
79  ct_boundaryPos ->setMaxValue( 90.0 );
80  ct_boundaryPos ->setValue ( 0.0 );
81  ct_boundaryPercent->disconnect();
82  ct_boundaryPercent->setValue ( 100.0 );
83  ct_boundaryPercent->setEnabled ( true );
84  ct_boundaryPercent->setVisible ( false );
85  connect( ct_boundaryPos, SIGNAL ( valueChanged ( double ) ),
86  SLOT ( boundary_pos ( double ) ) );
87  connect( this, SIGNAL ( dataAreLoaded( void ) ),
88  this, SLOT ( smooth10 ( void ) ) );
89  qApp->processEvents();
90 }
91 
92 void US_Dcdt::smooth10( void )
93 {
94  ct_smoothing->setValue( 10 );
95 }
96 
97 void US_Dcdt::exclude( void )
98 {
99  if ( ! dataLoaded ) return;
101 
102  data_plot();
103 }
104 
106 {
107  if ( ! dataLoaded ) return;
109 
110  data_plot();
111 }
112 
113 void US_Dcdt::reset( void )
114 {
115  if ( ! dataLoaded ) return;
116 
118 
119  ct_sValue->disconnect();
120  ct_sValue->setValue( sMax );
121  rb_avg ->click();
122  connect( ct_sValue, SIGNAL( valueChanged ( double ) ),
123  SLOT ( sMaxChanged ( double ) ) );
124  qApp->processEvents();
125 }
126 
127 void US_Dcdt::sMaxChanged( double /* value */ )
128 {
129  data_plot();
130 }
131 
132 void US_Dcdt::set_graph( int button )
133 {
134  if ( graphType == button ) return;
135 
136  graphType = button;
137  data_plot();
138 }
139 
140 void US_Dcdt::data_plot( void )
141 {
142  // Set upper limit so plot marks plateaus
143  ct_boundaryPos ->disconnect();
144  ct_boundaryPercent->disconnect();
145  ct_boundaryPercent->setMaxValue( 100.0 );
146  ct_boundaryPercent->setValue ( 100.0 - ct_boundaryPos->value() );
147 
148  // Do scans plot
150 
151  // Restore boundary limits
152  ct_boundaryPercent->setMaxValue( 100.0 );
153  ct_boundaryPercent->setValue ( 100.0 - ct_boundaryPos->value() );
154  ct_boundaryPos ->setMaxValue( 100.0 );
155  connect( ct_boundaryPos, SIGNAL ( valueChanged ( double ) ),
156  SLOT ( boundary_pos ( double ) ) );
157 
158  // Start setting up dcdt plot
159  int index = lw_triples->currentRow();
160  US_DataIO::EditedData* d = &dataList[ index ];
161 
162  int scanCount = d->scanData.size();
163  int skipped = 0;
164  double positionPct = ct_boundaryPos ->value() / 100.0;
165  double baseline = calc_baseline();
166 
167  le_skipped->setText( QString::number( skipped ) );
168 
169  if ( scanCount - skipped - excludedScans.count() < 4 )
170  {
171  QMessageBox::warning( this,
172  tr( "Attention" ),
173  tr( "You must have at least four non-skipped scans "
174  "for this analysis." ) );
175  return;
176  }
177 
178  int points = d->pointCount();
179 
180  // Create the new arrays
181  dcdt .clear();
182  sValues.clear();
183  avgDcdt.fill( 0.0, arrayLength );
184  avgS .fill( 0.0, arrayLength );
185 
186  previousScanCount = scanCount;
187 
188  for ( int i = 0; i < scanCount; i++ )
189  {
190  dcdt << QVector< double >( points );
191  sValues << QVector< double >( points );
192  }
193 
194  arraySizes.fill( 0, scanCount );
195  arrayStart.fill( 0, scanCount );
196 
197  // Calculate dcdt and sValues
198  while ( excludedScans.contains( skipped ) ) skipped++;
199 
200  int previous = skipped;
201  int count = 0;
202  double s_max = 0.0;
203 
204  for ( int i = skipped + 1; i < scanCount; i++ )
205  {
206  if ( excludedScans.contains( i ) ) continue;
207 
208  US_DataIO::Scan* thisScan = &d->scanData[ i ];
209  US_DataIO::Scan* prevScan = &d->scanData[ previous ];
210 
211  // These limits are for thisScan only:
212  double range = thisScan->plateau - baseline;
213  double lower_limit = baseline + range * positionPct;
214  double dt = thisScan->seconds - prevScan->seconds;
215  double plateau = thisScan->plateau;
216  double prevPlateau = prevScan->plateau;
217 
218  double meniscus = d->meniscus;
219  double omega = thisScan->rpm * M_PI / 30.0;
220 
221  int size = 0;
222  bool started = false;
223 
224  for ( int j = 0; j < points; j++ )
225  {
226  double currentV = thisScan->rvalues[ j ];
227  double previousV = prevScan->rvalues[ j ];
228 
229  if ( currentV < lower_limit ) continue;
230 
231  if ( ! started )
232  {
233  started = true;
234  arrayStart[ count ] = j;
235  }
236 
237  double dC = previousV / prevPlateau - currentV / plateau;
238  dcdt[ count ][ size ] = dC / dt;
239  double radius = d->radius( j );
240 
241  sValues[ count ][ size ] =
242  solution.s20w_correction * 1.0e13 * log( radius / meniscus ) /
243  ( sq( omega ) * ( prevScan->seconds + dt / 2.0 ) );
244 
245  s_max = max( s_max, sValues[ count ][ size ] );
246 
247  size++;
248  }
249 
250  arraySizes[ count ] = size;
251 
252  count++;
253  previous = i;
254  }
255 
256  ct_sValue->setMaxValue( ceil( s_max ) );
257 
258  if ( s_max < sMax )
259  {
260  if ( ct_sValue->value() > s_max )
261  {
262  ct_sValue->disconnect();
263  ct_sValue->setValue( ceil( s_max ) );
264 
265  connect( ct_sValue, SIGNAL( valueChanged ( double ) ),
266  SLOT ( sMaxChanged ( double ) ) );
267  qApp->processEvents();
268  }
269 
270  sMax = s_max;
271  }
272 
273  double s_min = 9.9e10;
274 
275  for ( int i = 0; i < count; i++ )
276  {
277  for ( int j = 0; j < arraySizes[ i ]; j++ )
278  {
279  s_min = min( s_min, sValues[ i ][ j ] );
280  }
281  }
282 
283  // Figure the total points to plot
284  double increment = ( s_max - s_min ) / arrayLength;
285 
286  // Assign new equally spaced s-values for the x-axis
287  for ( int i = 0; i < arrayLength; i++ )
288  avgS[ i ] = s_min + i * increment;
289 
290  // For each new s value, find the corresponding g*(s) through
291  // linear interpolation and add up, then average by the number of
292  // total scans included.
293 
294  for ( int j = 0; j < arrayLength; j++ )
295  {
296  avgDcdt[ j ] = 0.0;
297 
298  for ( int i = 0; i < count; i++ )
299  {
300  int k = 1;
301 
302  // Find index where svalue for the scan first exceeds
303  // the point on the x-axis
304  while ( sValues[ i ][ k ] < avgS[ j ] )
305  {
306  k++;
307  if ( k >= arraySizes[ i ] ) goto next; // Skip rest of scans
308  }
309 
310  // Interpolate and apply y = mx + b
311  double m = ( dcdt [ i ][ k ] - dcdt [ i ][ k - 1 ] ) /
312  ( sValues[ i ][ k ] - sValues[ i ][ k - 1 ] );
313 
314  double b = dcdt[ i ][ k ] - m * sValues[ i ][ k ];
315 
316  avgDcdt[ j ] += m * avgS[ j ] + b;
317  }
318 
319 next: avgDcdt[ j ] /= ( count - 1 );
320  }
321 
322  // Draw plot
323  data_plot1->clear();
324  us_grid( data_plot1 );
325 
326  data_plot1->setTitle( tr( "Time Derivative (dC/dt)\n" ) +
327  tr( "Run " ) + d->runID + tr( ": Cell " ) + d->cell
328  + " (" + d->wavelength + tr( " nm)" ) );
329 
330  data_plot1->setAxisTitle( QwtPlot::yLeft , tr( "g<sup>*</sup>(s)" ) );
331 
332  QVector< double > x( points );
333  QwtPlotCurve* curve;
334  QwtText title = QwtText( tr( "<b>Sedimentation Coefficient x "
335  "10<sup>13</sup> sec</b>" ) );
336 
337  data_plot1->setAxisTitle( QwtPlot::xBottom, title );
338 
339  // Draw the desired plot
340  switch ( graphType )
341  {
342  case 0: // Radius Plot
343  data_plot1->setAxisTitle( QwtPlot::xBottom, tr( "Radius (cm)" ) );
344 
345  for ( int i = 0; i < count; i++ )
346  {
347  int size = 0;
348  int end = arraySizes[ i ] + arrayStart[ i ];
349 
350  for ( int j = arrayStart[ i ]; j < end; j++ )
351  x[ size++ ] = d->xvalues[ j ];
352 
353  curve = us_curve( data_plot1,
354  tr( "Scan " ) + QString::number( i + 1 ) );
355 
356  curve->setData( x.data(), dcdt[ i ].data(), arraySizes[ i ] );
357  }
358 
359  data_plot1->setAxisAutoScale( QwtPlot::xBottom );
360  break;
361 
362  case 1: // S-value plot
363  for ( int i = 0; i < count; i++ )
364  {
365  curve = us_curve( data_plot1,
366  tr( "Scan " ) + QString::number( i + 1 ) );
367 
368  curve->setData( sValues[ i ].data(), dcdt[ i ].data(),
369  arraySizes[ i ] );
370  }
371 
372  data_plot1->setAxisScale( QwtPlot::xBottom, 0.0, ct_sValue->value() );
373  break;
374 
375  case 2: // Average dC/dt or g*(s) plot
376  data_plot1->setAxisTitle( QwtPlot::yLeft,
377  tr( "Average g<sup>*</sup>(s)" ) );
378 
379  curve = us_curve( data_plot1, tr( "Average g*(s)" ) );
380  curve->setData( avgS.data(), avgDcdt.data(), arrayLength );
381 
382  data_plot1->setAxisScale( QwtPlot::xBottom, 0.0, ct_sValue->value() );
383  break;
384  }
385 
386  qApp->processEvents();
387  data_plot1->replot();
388 }
389 
390 // Write report html to stream
391 void US_Dcdt::write_report( QTextStream& ts )
392 {
393  int index = lw_triples->currentRow();
394  US_DataIO::EditedData* edata = &dataList[ index ];
395 
396  double avgmax = 0.0;
397  int asmxx = 0;
398  int aslox = -1;
399  int ashix = 0;
400 
401  for ( int ii = 0; ii < arrayLength; ii++ )
402  { // Find maximum Y and its index
403  double avgv = avgDcdt[ ii ];
404 
405  if ( avgv > avgmax )
406  {
407  avgmax = avgv;
408  asmxx = ii;
409  }
410  }
411 
412  double avglcu = avgmax * 0.05;
413 //qDebug() << "avgmax asmxx avglcu" << avgmax << asmxx << avglcu;
414 
415  for ( int ii = 0; ii < arrayLength; ii++ )
416  { // Find the start and end of zone where Y is 5% of maximum or more
417  double avgv = avgDcdt[ ii ];
418 
419  if ( avgv > avglcu )
420  {
421  aslox = aslox < 0 ? ii : aslox;
422  ashix = ii;
423  }
424  }
425 //qDebug() << "aslox ashix" << aslox << ashix;
426 
427  // Compose the extra string to pass to the analysis composer
428  QString xastr = table_row( tr( "Average S at Maximum dCdt:" ),
429  QString::number( avgS[ asmxx ], 'f', 3 ) );
430  xastr += table_row( tr( "Average S at interest zone Start:" ),
431  QString::number( avgS[ aslox ], 'f', 3 ) );
432  xastr += table_row( tr( "Average S at interest zone End:" ),
433  QString::number( avgS[ ashix ], 'f', 3 ) );
434  xastr += table_row( tr( "Interest zone percent of maximum:" ),
435  QString( "5% and above" ) );
436 
437  QString title = "US_dCdt";
438  QString head1 = tr( "Time - Derivative (dC/dt) Analysis" );
439 
440  ts << html_header( title, head1, edata );
441  ts << analysis( xastr );
442  ts << indent( 2 ) + "</body>\n</html>\n";
443 }
444 
445 void US_Dcdt::view( void )
446 {
447  // Create report string
448  QString mtext;
449  QTextStream ts( &mtext );
450 
451  write_report( ts );
452 
453  // Create US_Editor and display report
454  if ( te_results == NULL )
455  {
456  te_results = new US_Editor( US_Editor::DEFAULT, true, QString(), this );
457  te_results->resize( 600, 700 );
458  QPoint p = g.global_position();
459  te_results->move( p.x() + 30, p.y() + 30 );
460  te_results->e->setFont( QFont( US_GuiSettings::fontFamily(),
462  }
463 
464  te_results->e->setHtml( mtext );
465  te_results->show();
466 }
467 
468 void US_Dcdt::save( void )
469 {
470  int index = lw_triples->currentRow();
471  US_DataIO::EditedData* d = &dataList[ index ];
472  QString dir = US_Settings::reportDir();
473 
474  if ( ! mkdir( dir, d->runID ) ) return;
475 
476  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
477  // Note: d->runID is both directory and first segment of file name
478  QString filebase = dir + "/" + d->runID + "/dcdt."
479  + QString( triples.at( index ) ).replace( " / ", "" ) + ".";
480 
481  QString htmlFile = filebase + "report.html";
482  QString plot1File = filebase + "velocity.svgz";
483  QString plot2File = filebase + "x-to-radius.svgz";
484  QString plot3File = filebase + "x-to-S.svgz";
485  QString plot4File = filebase + "average-S.svgz";
486  QString textFile = filebase + "average-S.csv";
487  QString dsinfFile = QString( filebase ).replace( "/dcdt.", "/dsinfo." )
488  + "dataset_info.html";
489 
490  // Write a general dataset information file
491  write_dset_report( dsinfFile );
492 
493  // Write main report file
494  // Write report
495  QFile f_rep( htmlFile );
496 
497  if ( ! f_rep.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
498  {
499  QMessageBox::warning( this,
500  tr( "IO Error" ),
501  tr( "Could not open\n" ) + htmlFile + "\n" +
502  tr( "\nfor writing" ) );
503  QApplication::restoreOverrideCursor();
504  return;
505  }
506 
507  QTextStream ts( &f_rep );
508  write_report( ts );
509  f_rep.close();
510 
511  // Save current plot type
512  int saveGraph = graphType;
513 
514  // Write plots, starting with velocity scans
515  data_plot();
516  write_plot( plot1File, data_plot2 );
517 
518  // Set current plot type to radius
519  graphType = 0;
520  data_plot();
521  write_plot( plot2File, data_plot1 );
522 
523  // Set current plot type to Svalues
524  graphType = 1;
525  data_plot();
526  write_plot( plot3File, data_plot1 );
527 
528  // Set current plot type to average-S
529  graphType = 2;
530  data_plot();
531  write_plot( plot4File, data_plot1 );
532 
533  // Restore plot type
534  graphType = saveGraph;
535 
536  // Write dcdt analysis average data
537  QFile dcdt_data( textFile );
538 
539  if ( ! dcdt_data.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
540  {
541  QMessageBox::warning( this,
542  tr( "IO Error" ),
543  tr( "Could not open\n" ) + textFile + "\n" +
544  tr( "\nfor writing" ) );
545  QApplication::restoreOverrideCursor();
546  return;
547  }
548 
549  QTextStream ts_data( &dcdt_data );
550  const QString sep( "\",\"" );
551  const QString quo( "\"" );
552  const QString eln( "\"\n" );
553  ts_data << "\"average.S\",\"average.Dcdt\"\n";
554 
555  for ( int i = 0; i < arrayLength; i++ )
556  {
557  QString strS = QString::number( avgS [ i ], 'f', 6 ).simplified();
558  QString strD = QString::number( avgDcdt[ i ], 'e', 5 ).simplified();
559  ts_data << quo << strS << sep << strD << eln;
560  }
561 
562  dcdt_data.close();
563 
564  QStringList repfiles;
565  update_filelist( repfiles, htmlFile );
566  update_filelist( repfiles, plot1File );
567  update_filelist( repfiles, plot2File );
568  update_filelist( repfiles, plot3File );
569  update_filelist( repfiles, plot4File );
570  update_filelist( repfiles, textFile );
571  update_filelist( repfiles, dsinfFile );
572 
573  // Tell user
574  htmlFile = htmlFile .mid( htmlFile .lastIndexOf( "/" ) + 1 );
575  plot1File = plot1File.mid( plot1File.lastIndexOf( "/" ) + 1 );
576  plot2File = plot2File.mid( plot2File.lastIndexOf( "/" ) + 1 );
577  plot3File = plot3File.mid( plot3File.lastIndexOf( "/" ) + 1 );
578  plot4File = plot4File.mid( plot4File.lastIndexOf( "/" ) + 1 );
579  textFile = textFile .mid( textFile .lastIndexOf( "/" ) + 1 );
580  dsinfFile = dsinfFile.mid( dsinfFile.lastIndexOf( "/" ) + 1 );
581 
582  QString wmsg = tr( "Wrote:\n " )
583  + htmlFile + "\n " + plot1File + "\n " + plot2File + "\n "
584  + plot3File + "\n " + plot4File + "\n " + textFile + "\n "
585  + dsinfFile;
586 
587  if ( disk_controls->db() )
588  { // Write report files also to the database
589  reportFilesToDB( repfiles );
590 
591  wmsg += tr( "\n\nReport files were also saved to the database." );
592  }
593 
594  QApplication::restoreOverrideCursor();
595  QMessageBox::warning( this, tr( "Success" ), wmsg );
596 }
597