UltraScan III
us_fematch.cpp
Go to the documentation of this file.
1 
3 #include <QApplication>
4 #include <QtSvg>
5 
6 #include "us_fematch.h"
7 #include "us_adv_dmgamc.h"
8 #include "us_thread_worker.h"
9 #include "us_license_t.h"
10 #include "us_license.h"
11 #include "us_settings.h"
12 #include "us_gui_settings.h"
13 #include "us_gui_util.h"
14 #include "us_matrix.h"
15 #include "us_constants.h"
16 #include "us_solution_vals.h"
17 #include "us_solution_gui.h"
18 #include "us_passwd.h"
19 #include "us_data_loader.h"
20 #include "us_util.h"
21 #include "us_investigator.h"
22 #include "us_loadable_noise.h"
23 #include "us_lamm_astfvm.h"
24 #include "us_report.h"
25 #include "us_sleep.h"
26 
27 #define MIN_NTC 25
28 
29 // main program
30 int main( int argc, char* argv[] )
31 {
32  QApplication application( argc, argv );
33 
34  #include "main1.inc"
35 
36  // License is OK. Start up.
37 
38  US_FeMatch w;
39  w.show();
40  return application.exec();
41 }
42 
43 // US_FeMatch class constructor
45 {
46  setObjectName( "US_FeMatch" );
47 
50  // Insure working etc is populated with color maps
51  clean_etc_dir();
52 
53  // set up the GUI
54  setPalette( US_GuiSettings::frameColor() );
55  setWindowTitle(
56  tr( "Compare Experimental Data to Sums of Finite Element Solutions" ) );
57 
58  mainLayout = new QHBoxLayout( this );
59  mainLayout->setSpacing ( 2 );
60  mainLayout->setContentsMargins( 2, 2, 2, 2 );
61 
62  leftLayout = new QVBoxLayout();
63  rightLayout = new QVBoxLayout();
64 
65  analysisLayout = new QGridLayout();
66  runInfoLayout = new QGridLayout();
67  parameterLayout = new QGridLayout();
68  controlsLayout = new QGridLayout();
69  buttonLayout = new QHBoxLayout();
70  progressLayout = new QHBoxLayout();
71 
72  leftLayout->addLayout( analysisLayout );
73  leftLayout->addLayout( runInfoLayout );
74  leftLayout->addLayout( parameterLayout );
75  leftLayout->addLayout( controlsLayout );
76  leftLayout->addStretch();
77  leftLayout->addLayout( buttonLayout );
78  leftLayout->addLayout( progressLayout );
79 
80  // Analysis buttons
81  dkdb_cntrls = new US_Disk_DB_Controls( local );
82  pb_load = us_pushbutton( tr( "Load Experiment" ) );
83  pb_details = us_pushbutton( tr( "Run Details" ) );
84  QLayout* lo_edit
85  = us_checkbox( tr( "Latest Data Edit" ), ck_edit, true );
86  pb_distrib = us_pushbutton( tr( "s20,W Distribution" ) );
87  pb_loadmodel = us_pushbutton( tr( "Load Model" ) );
88  pb_simumodel = us_pushbutton( tr( "Simulate Model" ) );
89  pb_view = us_pushbutton( tr( "View Data Report" ) );
90  pb_save = us_pushbutton( tr( "Save Data" ) );
91  //ck_edit ->setChecked( true );
92 
93  connect( dkdb_cntrls, SIGNAL( changed( bool ) ),
94  this, SLOT( update_disk_db( bool ) ) );
95  connect( pb_load, SIGNAL( clicked() ),
96  this, SLOT( load() ) );
97  connect( pb_details, SIGNAL( clicked() ),
98  this, SLOT( details() ) );
99  connect( pb_distrib, SIGNAL( clicked() ),
100  this, SLOT( distrib_type() ) );
101  connect( pb_loadmodel, SIGNAL( clicked() ),
102  this, SLOT( load_model() ) );
103  connect( pb_simumodel, SIGNAL( clicked() ),
104  this, SLOT( simulate_model() ) );
105  connect( pb_view, SIGNAL( clicked() ),
106  this, SLOT( view_report() ) );
107  connect( pb_save, SIGNAL( clicked() ),
108  this, SLOT( save_data() ) );
109 
110  pb_load ->setEnabled( true );
111  pb_details ->setEnabled( false );
112  ck_edit ->setEnabled( true );
113  pb_distrib ->setEnabled( false );
114  pb_loadmodel->setEnabled( false );
115  pb_simumodel->setEnabled( false );
116  pb_view ->setEnabled( false );
117  pb_save ->setEnabled( false );
118 
119  QLabel* lb_splot = us_label ( tr( "Simulation Plot:" ) );
120  int row = 0;
121  analysisLayout->addWidget( pb_load, row, 0 );
122  analysisLayout->addWidget( pb_details, row++, 1 );
123  analysisLayout->addLayout( lo_edit, row, 0 );
124  analysisLayout->addLayout( dkdb_cntrls, row++, 1 );
125  analysisLayout->addWidget( lb_splot, row, 0 );
126  analysisLayout->addWidget( pb_distrib, row++, 1 );
127  analysisLayout->addWidget( pb_loadmodel, row, 0 );
128  analysisLayout->addWidget( pb_simumodel, row++, 1 );
129  analysisLayout->addWidget( pb_view, row, 0 );
130  analysisLayout->addWidget( pb_save, row++, 1 );
131 
132  // Run info
133  QLabel* lb_info = us_banner( tr( "Information for this Run" ) );
134  QLabel* lb_triples = us_banner( tr( "Cell / Channel / Wavelength" ) );
135  QLabel* lb_id = us_label ( tr( "Run ID / Edit ID:" ) );
136  QLabel* lb_temp = us_label ( tr( "Average Temperature:" ) );
137 
138  le_id = us_lineedit( "", -1, true );
139  le_temp = us_lineedit( "", -1, true );
140 
142  QFontMetrics fm( font );
143  int fontHeight = fm.lineSpacing();
144 
145  te_desc = us_textedit();
146  te_desc->setMaximumHeight( fontHeight * 1 + 12 ); // Add for border
147  us_setReadOnly( te_desc, true );
148 
150  lw_triples->setMaximumHeight( fontHeight * 2 + 12 );
151 
152  row = 0;
153  runInfoLayout->addWidget( lb_info , row++, 0, 1, 4 );
154  runInfoLayout->addWidget( lb_id , row, 0, 1, 1 );
155  runInfoLayout->addWidget( le_id , row++, 1, 1, 3 );
156  runInfoLayout->addWidget( lb_temp , row, 0, 1, 1 );
157  runInfoLayout->addWidget( le_temp , row++, 1, 1, 3 );
158  runInfoLayout->addWidget( te_desc , row, 0, 2, 4 ); row += 2;
159  runInfoLayout->addWidget( lb_triples, row++, 0, 1, 4 );
160  runInfoLayout->addWidget( lw_triples, row++, 0, 5, 4 );
161 
162  // Parameters
163 
164  density = DENS_20W;
166  compress = 0.0;
167  manual = false;
168  pb_solution = us_pushbutton( tr( "Solution" ) );
169  QLabel* lb_density = us_label( tr( "Density" ) );
170  QLabel* lb_viscosity = us_label( tr( "Viscosity" ) );
171  QLabel* lb_vbar = us_label( tr( "Vbar" ) );
172  QLabel* lb_compress = us_label( tr( "Compressibility" ) );
173  le_solution = us_lineedit( tr( "(Experiment's solution)" ) );
174  le_density = us_lineedit( QString::number( density, 'f', 6 ) );
175  le_viscosity = us_lineedit( QString::number( viscosity, 'f', 5 ) );
176  le_vbar = us_lineedit( "0.7200" );
177  le_compress = us_lineedit( "0.0" );
178  lb_rmsd = us_label ( tr( "RMSD:" ) );
179  le_rmsd = us_lineedit( "0.0", -1, true );
180  le_variance = us_lineedit( "0.0", -1, true );
181  QFontMetrics fme( lb_compress->font() );
182  int pwid = fme.width( lb_compress->text() + 6 );
183  int lwid = pwid * 3 / 4;
184  pb_solution->setEnabled( false );
185  lb_vbar ->setMinimumWidth( pwid );
186  le_vbar ->setMinimumWidth( lwid );
187  lb_compress->setMinimumWidth( pwid );
188  le_compress->setMinimumWidth( lwid );
189 
190  QLabel* lb_experiment = us_banner( tr( "Experimental Parameters (at 20" )
191  + DEGC + "):" );
192  QLabel* lb_variance = us_label ( tr( "Variance:" ) );
193 
194  connect( pb_solution, SIGNAL( clicked() ),
195  this, SLOT( get_solution() ) );
196 
197  pb_advanced = us_pushbutton( tr( "Advanced Analysis Controls" ) );
198  pb_adv_dmga = us_pushbutton( tr( "Advanced DMGA-MC Controls" ) );
199  pb_plot3d = us_pushbutton( tr( "3D Plot" ) );
200  pb_plotres = us_pushbutton( tr( "Residual Plot" ) );
201  pb_advanced->setEnabled( false );
202  pb_adv_dmga->setEnabled( false );
203 
204  connect( pb_advanced, SIGNAL( clicked() ),
205  this, SLOT( advanced() ) );
206  connect( pb_adv_dmga, SIGNAL( clicked() ),
207  this, SLOT( adv_dmga() ) );
208  connect( pb_plot3d, SIGNAL( clicked() ),
209  this, SLOT( plot3d() ) );
210  connect( pb_plotres, SIGNAL( clicked() ),
211  this, SLOT( plotres() ) );
212 
213  pb_plot3d ->setEnabled( false );
214  pb_plotres->setEnabled( false );
215 
216  density = DENS_20W;
218  vbar = TYPICAL_VBAR;
219  compress = 0.0;
220  manual = false;
221 //Hardwire compressibility to zero and make read-only, for now
222 le_compress->setText( "0.0" );
223 us_setReadOnly( le_compress, true );
224 
225  row = 0;
226  parameterLayout->addWidget( lb_experiment , row++, 0, 1, 4 );
227  parameterLayout->addWidget( pb_solution , row, 0, 1, 1 );
228  parameterLayout->addWidget( le_solution , row++, 1, 1, 3 );
229  parameterLayout->addWidget( lb_density , row, 0, 1, 1 );
230  parameterLayout->addWidget( le_density , row, 1, 1, 1 );
231  parameterLayout->addWidget( lb_viscosity , row, 2, 1, 1 );
232  parameterLayout->addWidget( le_viscosity , row++, 3, 1, 1 );
233  parameterLayout->addWidget( lb_vbar , row, 0, 1, 1 );
234  parameterLayout->addWidget( le_vbar , row, 1, 1, 1 );
235  parameterLayout->addWidget( lb_compress , row, 2, 1, 1 );
236  parameterLayout->addWidget( le_compress , row++, 3, 1, 1 );
237  parameterLayout->addWidget( lb_variance , row, 0, 1, 1 );
238  parameterLayout->addWidget( le_variance , row, 1, 1, 1 );
239  parameterLayout->addWidget( lb_rmsd , row, 2, 1, 1 );
240  parameterLayout->addWidget( le_rmsd , row++, 3, 1, 1 );
241  parameterLayout->addWidget( pb_advanced , row, 0, 1, 2 );
242  parameterLayout->addWidget( pb_adv_dmga , row++, 2, 1, 2 );
243  parameterLayout->addWidget( pb_plot3d , row, 0, 1, 2 );
244  parameterLayout->addWidget( pb_plotres , row++, 2, 1, 2 );
245 
246  // Scan Controls
247 
248  QLabel* lb_scan = us_banner( tr( "Scan Control" ) );
249  QLabel* lb_from = us_label ( tr( "Scan focus from:" ) );
250  ct_from = us_counter( 3, 0, 500, 1 );
251  QLabel* lb_to = us_label ( tr( "to:" ) );
252  ct_to = us_counter( 3, 0, 500, 1 );
253  pb_exclude = us_pushbutton( tr( "Exclude Scan Range" ) );
254  pb_reset_exclude = us_pushbutton( tr( "Reset Scan Range" ) );
255  ct_from->setValue( 0 );
256  ct_to ->setValue( 0 );
257 
258  pb_exclude ->setEnabled( false );
259  pb_reset_exclude->setEnabled( false );
260 
261  connect( ct_from, SIGNAL( valueChanged( double ) ),
262  this, SLOT ( exclude_from( double ) ) );
263  connect( ct_to, SIGNAL( valueChanged( double ) ),
264  this, SLOT ( exclude_to ( double ) ) );
265  connect( pb_exclude, SIGNAL( clicked() ),
266  this, SLOT ( exclude() ) );
267  connect( pb_reset_exclude, SIGNAL( clicked() ),
268  this, SLOT ( reset_excludes() ) );
269 
270  row = 0;
271  controlsLayout->addWidget( lb_scan , row++, 0, 1, 4 );
272  controlsLayout->addWidget( lb_from , row, 0, 1, 2 );
273  controlsLayout->addWidget( ct_from , row++, 2, 1, 2 );
274  controlsLayout->addWidget( lb_to , row, 0, 1, 2 );
275  controlsLayout->addWidget( ct_to , row++, 2, 1, 2 );
276  controlsLayout->addWidget( pb_exclude , row, 0, 1, 2 );
277  controlsLayout->addWidget( pb_reset_exclude , row++, 2, 1, 2 );
278 
279  // Plots
281  tr( "Residuals" ),
282  tr( "Radius (cm)" ),
283  tr( "OD Difference" ) );
284 
286  tr( "Velocity Data" ),
287  tr( "Radius (cm)" ),
288  tr( "Absorbance" ) );
289 
290  data_plot1->setCanvasBackground( Qt::black );
291  data_plot2->setCanvasBackground( Qt::black );
292  data_plot1->setMinimumSize( 560, 240 );
293  data_plot2->setMinimumSize( 560, 240 );
294 
295  // Standard buttons
296  pb_reset = us_pushbutton( tr( "Reset" ) );
297  pb_help = us_pushbutton( tr( "Help" ) );
298  pb_close = us_pushbutton( tr( "Close" ) );
299 
300  buttonLayout->addWidget( pb_reset );
301  buttonLayout->addWidget( pb_help );
302  buttonLayout->addWidget( pb_close );
303 
304  connect( pb_reset, SIGNAL( clicked() ),
305  this, SLOT( reset() ) );
306  connect( pb_close, SIGNAL( clicked() ),
307  this, SLOT( close_all() ) );
308  connect( pb_help, SIGNAL( clicked() ),
309  this, SLOT( help() ) );
310 
311  // Progress label and bar
312  QLabel* lb_progress = us_label( tr( "% Complete:" ) );
313  progress = us_progressBar( 0, 100, 0 );
314  lb_progress->setAlignment( Qt::AlignCenter );
315 
316  progressLayout->addWidget( lb_progress );
317  progressLayout->addWidget( progress );
318 
319  // Optional MC controls
320  QVBoxLayout* mcctlLayout = new QVBoxLayout();
321  QHBoxLayout* mosbox = new QHBoxLayout();
322  rb_curmod = new QRadioButton();
323  rb_mean = new QRadioButton();
324  rb_median = new QRadioButton();
325  rb_mode = new QRadioButton();
326  QGridLayout* rl_curmod = us_radiobutton( tr("Current Model"), rb_curmod );
327  QGridLayout* rl_mean = us_radiobutton( tr( "Mean" ), rb_mean );
328  QGridLayout* rl_median = us_radiobutton( tr( "Median" ), rb_median );
329  QGridLayout* rl_mode = us_radiobutton( tr( "Mode" ), rb_mode );
330  QGroupBox* gb_msim = new QGroupBox();
331  mcctlLayout->setContentsMargins( 0, 0, 0, 0 );
332  gb_msim ->setContentsMargins( 0, 2, 0, 2 );
333  mosbox ->setContentsMargins( 0, 0, 0, 0 );
334  mosbox ->setSpacing( 2 );
335  gb_msim ->setFlat( true );
336  gb_msim ->setPalette( US_GuiSettings::frameColor() );
337  gb_msim ->setAutoFillBackground( true );
338  pb_nextm = us_pushbutton( tr( "Next Model" ) );
339  ct_model = us_counter( 2, 1, 200, 1 );
340  ct_model->setStep( 1 );
341  ct_model->setFixedWidth( pwid );
342  gb_msim->setLayout( mosbox );
343  mosbox ->addLayout( rl_curmod );
344  mosbox ->addLayout( rl_mode );
345  mosbox ->addLayout( rl_mean );
346  mosbox ->addLayout( rl_median );
347  mosbox ->addWidget( pb_nextm );
348  mosbox ->addWidget( ct_model );
349  mcctlLayout->addWidget( gb_msim );
350 
351  rb_mean ->setChecked( true );
352  pb_nextm->setEnabled( false );
353  ct_model->setEnabled( false );
354  gb_msim ->setVisible( false );
355 
356  connect( rb_curmod, SIGNAL( toggled ( bool ) ),
357  this, SLOT( curmod_clicked ( bool ) ) );
358  connect( rb_mean, SIGNAL( toggled ( bool ) ),
359  this, SLOT( modbtn_clicked ( bool ) ) );
360  connect( rb_median, SIGNAL( toggled ( bool ) ),
361  this, SLOT( modbtn_clicked ( bool ) ) );
362  connect( rb_mode, SIGNAL( toggled ( bool ) ),
363  this, SLOT( modbtn_clicked ( bool ) ) );
364  connect( pb_nextm, SIGNAL( clicked () ),
365  this, SLOT( next_model () ) );
366  connect( ct_model, SIGNAL( valueChanged ( double ) ),
367  this, SLOT( update_mc_model() ) );
368 
369  // Lay out the right side (plots and provisional MC controls
370  rightLayout->addLayout( plotLayout1 );
371  rightLayout->addLayout( mcctlLayout );
372  rightLayout->addLayout( plotLayout2 );
373  rightLayout->setStretchFactor( plotLayout1, 2 );
374  rightLayout->setStretchFactor( mcctlLayout, 0 );
375  rightLayout->setStretchFactor( plotLayout2, 3 );
376 
377  // Lay out the main window
378  mainLayout->addLayout( leftLayout );
379  mainLayout->addLayout( rightLayout );
380  mainLayout->setStretchFactor( leftLayout, 3 );
381  mainLayout->setStretchFactor( rightLayout, 5 );
382 
383  dataLoaded = false;
384  buffLoaded = false;
385  haveSim = false;
386  mfilter = "";
387  resids.clear();
388  rbmapd = 0;
389  eplotcd = 0;
390  resplotd = 0;
391  bmd_pos = this->pos() + QPoint( 100, 100 );
392  epd_pos = this->pos() + QPoint( 200, 200 );
393  rpd_pos = this->pos() + QPoint( 300, 400 );
394 
395  ti_noise.count = 0;
396  ri_noise.count = 0;
397 
398  adv_vals[ "simpoints" ] = "200";
399  adv_vals[ "bndvolume" ] = "0.015";
400  adv_vals[ "parameter" ] = "0";
401  adv_vals[ "modelnbr" ] = "0";
402  adv_vals[ "meshtype" ] = "ASTFEM";
403  adv_vals[ "gridtype" ] = "Moving";
404  adv_vals[ "modelsim" ] = "mean";
405 
406  sdata = &wsdata;
407 
408  setMaximumSize( qApp->desktop()->size() - QSize( 40, 40 ) );
409 }
410 
411 // public function to get pointer to edit data
413 
414 // public function to get pointer to list of excluded scans
415 QList< int >* US_FeMatch::fem_excllist() { return &excludedScans;}
416 
417 // public function to get pointer to sim data
419 
420 // public function to get pointer to load model
422 
423 // public function to get pointer to TI noise
425 
426 // public function to get pointer to RI noise
428 
429 // public function to get pointer to resid bitmap diag
430 QPointer< US_ResidsBitmap > US_FeMatch::fem_resbmap() { return rbmapd; }
431 
432 // Load data
433 void US_FeMatch::load( void )
434 {
435  QString file;
436  QStringList files;
437  QStringList parts;
438  lw_triples-> disconnect();
439  lw_triples-> clear();
440  dataList. clear();
441  rawList. clear();
442  excludedScans.clear();
443  triples. clear();
444  speed_steps .clear();
445 
446  dataLoaded = false;
447  buffLoaded = false;
448  haveSim = false;
449  dataLatest = ck_edit->isChecked();
450  int local = dkdb_cntrls->db() ? US_Disk_DB_Controls::DB
452 
453 DbgLv(1) << "LD: open dialog";
454  US_DataLoader* dialog =
456  triples, workingDir, QString( "velocity" ) );
457 
458  connect( dialog, SIGNAL( changed( bool ) ),
459  this, SLOT( update_disk_db( bool ) ) );
460  connect( dialog, SIGNAL( progress( const QString ) ),
461  this, SLOT( set_progress( const QString ) ) );
462 
463 DbgLv(1) << "LD: exec dialog";
464  if ( dialog->exec() != QDialog::Accepted ) return;
465 DbgLv(1) << "LD: local" << local << US_Disk_DB_Controls::DB;
466 
467  edata = &dataList[ 0 ];
468  runID = edata->runID;
469 
470  // Get speed steps from disk or DB experiment
471  if ( local == US_Disk_DB_Controls::DB )
472  { // Fetch the speed steps for the experiment from the database
473  workingDir = tr( "(database)" );
474 DbgLv(1) << "LD: DB IN runID" << runID;
475 
476  US_Passwd pw;
477  US_DB2* dbP = new US_DB2( pw.getPasswd() );
478  QStringList query;
479  QString expID;
480  int idExp = 0;
481  query << "get_experiment_info_by_runID"
482  << runID
483  << QString::number( US_Settings::us_inv_ID() );
484  dbP->query( query );
485 
486  if ( dbP->lastErrno() == US_DB2::OK )
487  {
488  dbP->next();
489  idExp = dbP->value( 1 ).toInt();
491 DbgLv(1) << "SS: ss count" << speed_steps.count() << "idExp" << idExp;
492 if (speed_steps.count()>0 )
493 DbgLv(1) << "SS: ss0 w2tfirst w2tlast timefirst timelast"
494  << speed_steps[0].w2t_first << speed_steps[0].w2t_last
495  << speed_steps[0].time_first << speed_steps[0].time_last;
496  }
497  }
498 
499  else
500  { // Read run experiment file and parse out speed steps
501  workingDir = workingDir.section( workingDir.left( 1 ), 4, 4 );
502  workingDir = workingDir.left( workingDir.lastIndexOf( "/" ) );
503 DbgLv(1) << "LD: Disk IN runID" << runID << "workingDir" << workingDir;
504 
505  QString expfpath = workingDir + "/" + runID + "."
506  + edata->dataType + ".xml";
507 DbgLv(1) << "LD: expf path" << expfpath;
508  QFile xfi( expfpath )
509  ;
510  if ( xfi.open( QIODevice::ReadOnly ) )
511  { // Read and parse "<speedstep>" lines in the XML
512  QXmlStreamReader xmli( &xfi );
513 
514  while ( ! xmli.atEnd() )
515  {
516  xmli.readNext();
517 
518  if ( xmli.isStartElement() && xmli.name() == "speedstep" )
519  {
520  SP_SPEEDPROFILE sp;
522  speed_steps << sp;
523 DbgLv(1) << "LD: sp: rotspeed" << sp.rotorspeed << "t1" << sp.time_first;
524  }
525  }
526 
527  xfi.close();
528  }
529  }
530 
531  exp_steps = ( speed_steps.count() > 0 ); // Flag any multi-step experiment
532 
533  qApp->processEvents();
534 
536  QFontMetrics fm( font );
537  int fontHeight = fm.lineSpacing();
538  int ntriples = triples.size();
539  lw_triples->setMaximumHeight( fontHeight * min( ntriples, 4 ) + 12 );
540 
541  for ( int ii = 0; ii < ntriples; ii++ )
542  lw_triples->addItem( triples.at( ii ) );
543 
544  allExcls.fill( excludedScans, ntriples );
545 
546  scanCount = edata->scanData.size();
547  double avgTemp = edata->average_temperature();
548 
549  // set ID, description, and avg temperature text
550  le_id ->setText( edata->runID + " / " + edata->editID );
551  te_desc->setText( edata->description );
552  le_temp->setText( QString::number( avgTemp, 'f', 1 ) + " " + DEGC );
553 
554  lw_triples->setCurrentRow( 0 );
555  connect( lw_triples, SIGNAL( currentRowChanged( int ) ),
556  SLOT( new_triple( int ) ) );
557 
558  dataLoaded = true;
559  haveSim = false;
560 
561  update( 0 );
562 
563  pb_solution ->setEnabled( true );
564  pb_details ->setEnabled( true );
565  pb_loadmodel->setEnabled( true );
566  pb_exclude ->setEnabled( true );
567  mfilter = QString( "=e" );
568 
569  ct_from->disconnect();
570  ct_from->setValue( 0 );
571 
572  connect( ct_from, SIGNAL( valueChanged( double ) ),
573  this, SLOT( exclude_from( double ) ) );
574 
575  bmd_pos = this->pos() + QPoint( 100, 100 );
576  epd_pos = this->pos() + QPoint( 200, 200 );
577  rpd_pos = this->pos() + QPoint( 300, 300 );
578 
579 }
580 
581 // Details
583 {
584  US_RunDetails2* dialog
586 
587  dialog->move( this->pos() + QPoint( 100, 100 ) );
588  dialog->exec();
589  qApp->processEvents();
590 
591  delete dialog;
592 }
593 
594 // Update based on selected triples row
595 void US_FeMatch::update( int drow )
596 {
597  edata = &dataList[ drow ];
598  scanCount = edata->scanData.size();
599  runID = edata->runID;
600  haveSim = false;
601  le_id-> setText( runID + " / " + edata->editID );
602 
603  double avgTemp = edata->average_temperature();
604  le_temp->setText( QString::number( avgTemp, 'f', 1 )
605  + " " + DEGC );
606  te_desc->setText( edata->description );
607 
608  excludedScans = allExcls[ drow ];
609 
610  ct_from->setMaxValue( scanCount - excludedScans.size() );
611  ct_to ->setMaxValue( scanCount - excludedScans.size() );
612  ct_from->setStep( 1.0 );
613  ct_to ->setStep( 1.0 );
614 
615  // Set up solution values implied from experimental data
616  QString solID;
617  QString bufid;
618  QString bguid;
619  QString bdesc;
620  QString bdens = le_density ->text();
621  QString bvisc = le_viscosity->text();
622  QString bcomp = le_compress ->text();
623  QString bmanu = manual ? "1" : "0";
624  QString svbar = le_vbar ->text();
625  bool bufvl = false;
626 DbgLv(1) << "Fem:Upd: (0)svbar" << svbar;
627 
628  QString errmsg;
629  US_Passwd pw;
630  US_DB2* dbP = dkdb_cntrls->db() ?
631  new US_DB2( pw.getPasswd() ) : 0;
632  bufvl = US_SolutionVals::values( dbP, edata, solID, svbar, bdens,
633  bvisc, bcomp, bmanu, errmsg );
634 //Hardwire compressibility to zero, for now
635 bcomp="0.0";
636 
637 DbgLv(1) << "Fem:Upd: bufvl" << bufvl << "svbar" << svbar;
638  if ( bufvl )
639  {
640  buffLoaded = false;
641  bcomp = QString::number( bcomp.toDouble() );
642  le_density ->setText( bdens );
643  le_viscosity->setText( bvisc );
644  le_compress ->setText( bcomp );
645  buffLoaded = true;
646  density = bdens.toDouble();
647  viscosity = bvisc.toDouble();
648  compress = bcomp.toDouble();
649  manual = ( !bmanu.isEmpty() && bmanu == "1" );
650 DbgLv(1) << "Fem:Upd: solID" << solID;
651 
652  if ( solID.isEmpty() )
653  {
654  QMessageBox::warning( this, tr( "Solution/Buffer Fetch" ),
655  tr( "Empty solution ID value!" ) );
656  }
657 
658  else if ( solID.length() < 36 && dbP != NULL )
659  {
660  solution_rec.readFromDB( solID.toInt(), dbP );
661 DbgLv(1) << "Fem:Upd: (1)svbar" << svbar;
662  }
663 
664  else
665  {
666  solution_rec.readFromDisk( solID );
667  }
668 
671  svbar = QString::number( vbar );
672  le_vbar ->setText( svbar );
673 DbgLv(1) << "Fem:Upd: (9)svbar" << svbar;
674  }
675 
676  else
677  {
678  QMessageBox::warning( this, tr( "Solution/Buffer Fetch" ),
679  errmsg );
681  le_solution ->setText( tr( "( ***Undefined*** )" ) );
682  }
683 
684  ti_noise.count = 0;
685  ri_noise.count = 0;
686 
687  data_plot();
688 
689  if ( dbP != NULL )
690  {
691  delete dbP;
692  dbP = NULL;
693  }
694 
695  pb_view ->setEnabled( false );
696  pb_save ->setEnabled( false );
697  pb_simumodel->setEnabled( false );
698  pb_distrib ->setEnabled( false );
699  pb_advanced ->setEnabled( false );
700  pb_adv_dmga ->setEnabled( false );
701  pb_plot3d ->setEnabled( false );
702  pb_plotres ->setEnabled( false );
703  pb_distrib ->setText ( tr( "s20,W Distribution" ) );
704 DbgLv(1) << "Fem:Upd: manual" << manual << solution_rec.buffer.manual;
705 
706  if ( eplotcd != 0 )
707  {
708  epd_pos = eplotcd->pos();
709  eplotcd->close();
710  eplotcd = 0;
711  }
712 
713  if ( resplotd != 0 )
714  {
715  rpd_pos = resplotd->pos();
716  resplotd->close();
717  resplotd = 0;
718  }
719 
720  if ( rbmapd != 0 )
721  {
722  bmd_pos = rbmapd->pos();
723  rbmapd->close();
724  rbmapd = 0;
725  }
726 }
727 
728 
729 // Data plot
731 {
732  data_plot2->detachItems();
733 
734  if ( !dataLoaded )
735  return;
736 
737  int drow = lw_triples->currentRow();
738  edata = &dataList[ drow ];
739  QString triple = QString( triples.at( drow ) ).replace( " / ", "/" );
740  QString dataType = tr( "Absorbance" );
741  if ( edata->dataType == "RI" ) dataType = tr( "Intensity" );
742  if ( edata->dataType == "WI" ) dataType = tr( "Intensity" );
743  if ( edata->dataType == "IP" ) dataType = tr( "Interference" );
744  if ( edata->dataType == "FI" ) dataType = tr( "Fluourescence" );
745  data_plot2->setTitle(
746  tr( "Velocity Data for " ) + triple + " of\n" + edata->runID );
747  data_plot2->setAxisTitle( QwtPlot::yLeft,
748  dataType + tr( " at " ) + edata->wavelength + tr( " nm" ) );
749  data_plot2->setAxisTitle( QwtPlot::xBottom,
750  tr( "Radius (cm)" ) );
751 
752  data_plot2->clear();
753  us_grid( data_plot2 );
754 
755  int scan_nbr = 0;
756  int from = (int)ct_from->value();
757  int to = (int)ct_to ->value();
758 
759  int points = edata->pointCount();
760  int count = points;
761 
762  if ( haveSim )
763  {
764  count = sdata->pointCount();
765  count = points > count ? points : count;
766  }
767 
768  QVector< double > vecr( count );
769  QVector< double > vecv( count );
770  double* rr = vecr.data();
771  double* vv = vecv.data();
772 
773  QString title;
774  QwtPlotCurve* cc;
775  QPen pen_red( Qt::red );
776  QPen pen_plot( US_GuiSettings::plotCurve() );
777 
778  // Calculate basic parameters for other functions
779  double avgTemp = edata->average_temperature();
780  solution.density = le_density ->text().toDouble();
781  solution.viscosity = le_viscosity->text().toDouble();
783  solution.vbar20 = le_vbar ->text().toDouble();
785 
787 
788  dscan = &edata->scanData.last();
789  int point = US_DataIO::index( dataList[ drow ].xvalues,
790  dataList[ drow ].baseline );
791  point = ( point < 5 ) ? 5 : point;
792  double baseline = 0.0;
793 
794  for ( int jj = point - 5; jj < point + 6; jj++ )
795  baseline += dscan->rvalues[ jj ];
796 
797  baseline /= 11.0;
798 
799  // Draw curves
800  for ( int ii = 0; ii < scanCount; ii++ )
801  {
802  if ( excludedScans.contains( ii ) ) continue;
803 
804  scan_nbr++;
805  bool highlight = ( scan_nbr >= from && scan_nbr <= to );
806 
807  dscan = &edata->scanData[ ii ];
808 
809  for ( int jj = 0; jj < points; jj++ )
810  {
811  rr[ jj ] = edata->radius( jj );
812  vv[ jj ] = edata->value ( ii, jj );
813  }
814 
815  title = tr( "Curve " ) + QString::number( ii ) + tr( " in range" );
816  cc = us_curve( data_plot2, title );
817 
818  if ( highlight )
819  cc->setPen( pen_red );
820  else
821  cc->setPen( pen_plot );
822 
823  cc->setData( rr, vv, points );
824  }
825 
826  // Plot simulation
827  if ( haveSim )
828  {
829  double rl = edata->radius( 0 );
830  double vh = edata->value( scanCount - 1, points - 1 );
831  rl -= 0.05;
832  vh += ( vh - edata->value( 0, 0 ) ) * 0.05;
833  double rnoi = 0.0;
834  double tnoi = 0.0;
835  bool have_ri = ri_noise.count > 0;
836  bool have_ti = ti_noise.count > 0;
837 DbgLv(1) << " RL" << rl << " VH" << vh;
838 int nscan=scanCount;
839 int nconc=sdata->pointCount();
840 DbgLv(1) << " sdata ns nc " << nscan << nconc;
841 DbgLv(1) << " sdata->x0" << sdata->radius(0);
842 DbgLv(1) << " sdata->xN" << sdata->radius(nconc-1);
843 DbgLv(1) << " sdata->c00" << sdata->value(0,0);
844 DbgLv(1) << " sdata->c0N" << sdata->value(0,nconc-1);
845 DbgLv(1) << " sdata->cM0" << sdata->value(nscan-1,0);
846 DbgLv(1) << " sdata->cMN" << sdata->value(nscan-1,nconc-1);
847  double rmsd = 0.0;
848  int kpts = 0;
849 
850  for ( int ii = 0; ii < scanCount; ii++ )
851  {
852  if ( excludedScans.contains( ii ) ) continue;
853 
854  points = sdata->pointCount();
855 DbgLv(2) << " II POINTS" << ii << points;
856  count = 0;
857  double rp = 0.0;
858  double vp = 0.0;
859  double da = 0.0;
860  rnoi = have_ri ? ri_noise.values[ ii ] : 0.0;
861 
862  for ( int jj = 0; jj < points; jj++ )
863  { // accumulate coordinates of simulation curve
864  tnoi = have_ti ? ti_noise.values[ jj ] : 0.0;
865  rp = sdata->radius( jj );
866  vp = sdata->value( ii, jj ) + rnoi + tnoi;
867  da = edata->value( ii, jj );
868  rmsd += sq( da - vp );
869  kpts++;
870 DbgLv(3) << " JJ rr vv" << jj << rr << vv;
871 
872  if ( rp > rl )
873  {
874  rr[ count ] = rp;
875  vv[ count++ ] = vp;
876  }
877  }
878  title = "SimCurve " + QString::number( ii );
879  cc = us_curve( data_plot2, title );
880  cc->setPen( pen_red );
881  cc->setData( rr, vv, count );
882 DbgLv(1) << "Sim plot scan count" << ii << count
883  << " r0 v0 rN vN" << rr[0] << vv[0 ] << rr[count-1] << vv[count-1];
884  }
885 
886  rmsd /= (double)kpts;
887  le_variance->setText( QString::number( rmsd ) );
888  rmsd = sqrt( rmsd );
889 DbgLv(1) << " Sim plot rmsd kpts" << rmsd << kpts;
890  le_rmsd ->setText( QString::number( rmsd ) );
891 //*DEBUG
892 double vsum=0.0;
893 for(int ii=0; ii<scanCount; ii++)
894 {
895  if ( excludedScans.contains( ii ) ) continue;
896  for(int jj=0; jj<points; jj++)
897  vsum += sq(sdata->value(ii,jj));
898 }
899 DbgLv(1) << "VSUM=" << vsum;
900 int hh=sdata->pointCount()/2;
901 int ss=sdata->scanCount();
902 DbgLv(1) << "SDAT 0-3"
903  << sdata->value(0,hh)
904  << sdata->value(1,hh)
905  << sdata->value(2,hh)
906  << sdata->value(3,hh);
907 DbgLv(1) << "SDAT *-n"
908  << sdata->value(ss-4,hh)
909  << sdata->value(ss-3,hh)
910  << sdata->value(ss-2,hh)
911  << sdata->value(ss-1,hh);
912 DbgLv(1) << "MDL comp 1 s,k,w,D,f"
913  << model.components[0].s
914  << model.components[0].f_f0
915  << model.components[0].mw
916  << model.components[0].D
917  << model.components[0].f;
918 if(model.components.size()>1)
919 DbgLv(1) << "MDL comp 2 s,k,w,D,f"
920  << model.components[1].s
921  << model.components[1].f_f0
922  << model.components[1].mw
923  << model.components[1].D
924  << model.components[1].f;
925 if(model.associations.size()>0)
926 DbgLv(1) << "MDL asoc 1 Kd,koff"
927  << model.associations[0].k_d
928  << model.associations[0].k_off;
929 DbgLv(1) << "DS dens visc manual temp"
930  << density
931  << viscosity
932  << manual
935 DbgLv(1) << "SIMP simpt mType gType rreso menis"
936  << simpar->simpoints
937  << simpar->meshType
938  << simpar->gridType
939  << simpar->radial_resolution
940  << simpar->meniscus;
941 DbgLv(1) << "SIMP bott temp rnoise tinoise rinoise"
942  << simpar->bottom
943  << simpar->temperature
944  << simpar->rnoise
945  << simpar->tinoise
946  << simpar->rinoise;
947 DbgLv(1) << "SIMP bform bvol bottpos rcoeffs"
948  << simpar->band_forming
949  << simpar->band_volume
950  << simpar->bottom_position
951  << simpar->rotorcoeffs[0]
952  << simpar->rotorcoeffs[1];
954 DbgLv(1) << "STEP0 durmin dlymin w2tf w2tl timf timl"
955  << spstep->duration_minutes
956  << spstep->delay_minutes
957  << spstep->w2t_first
958  << spstep->w2t_last
959  << spstep->time_first
960  << spstep->time_last;
961 DbgLv(1) << "STEP0 speed accel accelf"
962  << spstep->rotorspeed
963  << spstep->acceleration
964  << spstep->acceleration_flag;
965 //*DEBUG
966  }
967 
968  else
969  { // No simulation exists yet
970  data_plot1->detachItems();
971  data_plot1->clear();
972  data_plot1->setTitle( tr( "Residuals" ) );
973  data_plot1->replot();
974  }
975 
976  data_plot2->replot();
977 
978  return;
979 }
980 
981 // Save the report and image data
983 {
984  if ( eplotcd == 0 || eplotcd->data_3dplot() == 0 )
985  { // if no 3d plot control up, ask what user wants for 3d plot save
986  if ( eplotcd == 0 )
987  eplotcd = new US_PlotControlFem( this, &model );
988  eplotcd->move( epd_pos );
989  eplotcd->show();
990  eplotcd->do_3dplot();
991 
992  QMessageBox mBox;
993  mBox.addButton( tr( "Save" ), QMessageBox::RejectRole );
994  QPushButton* bSkip = mBox.addButton( tr( "Skip" ),
995  QMessageBox::NoRole );
996  QPushButton* bEdit = mBox.addButton( tr( "Edit" ),
997  QMessageBox::YesRole );
998  mBox.setDefaultButton( bEdit );
999  mBox.setWindowTitle ( tr( "3D Plot Save?" ) );
1000  mBox.setTextFormat ( Qt::RichText );
1001  mBox.setText ( tr(
1002  "You selected Save Data without a 3D Plot already displayed.<br>"
1003  "Do you want a 3D Plot to be saved with the other reports?<ul>"
1004  "<li><b>Save</b> to include the 3D Plot in the report (AS IS);</li>"
1005  "<li><b>Skip</b> to exclude the 3D Plot from report saves;</li>"
1006  "<li><b>Edit</b> to abort save for now so that you can edit<br>"
1007  "the 3D Plot and re-select Save Data when ready.</li></ul>" ) );
1008  mBox.setIcon ( QMessageBox::Information );
1009 
1010  mBox.exec();
1011 
1012  if ( mBox.clickedButton() == bEdit )
1013  {
1014  QMessageBox::information( this, tr( "Edit 3D Plot" ),
1015  tr( "You may now edit the 3D Plot.\n"
1016  "When it is ready, re-select 'Save Data'" ) );
1017  eplotcd->do_3dplot();
1018  return;
1019  }
1020 
1021  else if ( mBox.clickedButton() == bSkip )
1022  {
1023  eplotcd->close();
1024  eplotcd = NULL;
1025  }
1026  }
1027  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1028 QDateTime time0=QDateTime::currentDateTime();
1029 
1030  QStringList files;
1031  int drow = lw_triples->currentRow();
1033  QString tripnode = QString( triples.at( drow ) ).replace( " / ", "" );
1034  QString basename = US_Settings::reportDir() + "/" + edata->runID + "/"
1035  + text_model( model, 0 ) + "." + tripnode + ".";
1036  QString htmlFile = basename + "report.html";
1037  QFile rep_f( htmlFile );
1038  if ( ! rep_f.open( QIODevice::WriteOnly | QIODevice::Text ) )
1039  return;
1040 
1041  QTextStream ts( &rep_f );
1042 
1043  // save the report to a file
1044  write_report( ts );
1045 
1046  rep_f.close();
1047  update_filelist( files, htmlFile );
1048 
1049  const QString svgext( ".svgz" );
1050  const QString pngext( ".png" );
1051  const QString csvext( ".csv" );
1052  QString img01File = basename + "velocity" + svgext;
1053  QString img02File = basename + "residuals" + pngext;
1054  QString img03File = basename + "s_distrib" + svgext;
1055  QString img04File = basename + "mw_distrib" + svgext;
1056  QString img05File = basename + "D_distrib" + svgext;
1057  QString img06File = basename + "ff0_vs_s" + svgext;
1058  QString img07File = basename + "ff0_vs_mw" + svgext;
1059  QString img08File = basename + "D_vs_s" + svgext;
1060  QString img09File = basename + "D_vs_mw" + svgext;
1061  QString img10File = basename + "3dplot" + pngext;
1062  QString img11File = basename + "rbitmap" + pngext;
1063  QString img12File = basename + "tinoise" + svgext;
1064  QString img13File = basename + "rinoise" + svgext;
1065  QString mdistFile = basename + "mdistr_tab" + csvext;
1066 
1067  if ( !cnstvb )
1068  {
1069  img06File.replace( "ff0", "vbar" );
1070  img07File.replace( "ff0", "vbar" );
1071  }
1072 DbgLv(1) << "cnstvb" << cnstvb << "img06File" << img06File;
1073 
1074  // Save image files from main window
1075  write_plot( img01File, data_plot2 );
1076  update_filelist( files, img01File );
1077 
1078  int p1type = type_distrib() - 1;
1079  p1type = ( p1type < 0 ) ? 9 : p1type;
1080  QString p1file = img02File;
1081  if ( p1type == 0 ) p1file = img03File;
1082  else if ( p1type == 1 ) p1file = img04File;
1083  else if ( p1type == 2 ) p1file = img05File;
1084  else if ( p1type == 3 ) p1file = img06File;
1085  else if ( p1type == 4 ) p1file = img07File;
1086  else if ( p1type == 5 ) p1file = img06File;
1087  else if ( p1type == 6 ) p1file = img07File;
1088  else if ( p1type == 7 ) p1file = img08File;
1089  else if ( p1type == 8 ) p1file = img09File;
1090 DbgLv(1) << "p1type" << p1type << "p1file" << p1file;
1091 
1092  write_plot( p1file, data_plot1 );
1093 
1094  if ( p1type != 9 )
1095  {
1097  write_plot( img02File, data_plot1 );
1098  }
1099  update_filelist( files, img02File );
1100 
1101  if ( p1type != 0 )
1102  {
1103  distrib_plot_stick( 0 );
1104  write_plot( img03File, data_plot1 );
1105  }
1106  update_filelist( files, img03File );
1107 
1108  if ( p1type != 1 )
1109  {
1110  distrib_plot_stick( 1 );
1111  write_plot( img04File, data_plot1 );
1112  }
1113  update_filelist( files, img04File );
1114 
1115  if ( p1type != 2 )
1116  {
1117  distrib_plot_stick( 2 );
1118  write_plot( img05File, data_plot1 );
1119  }
1120  update_filelist( files, img05File );
1121 
1122  if ( p1type != 3 && p1type != 5 )
1123  {
1124  distrib_plot_2d( ( cnstvb ? 3 : 5 ) );
1125  write_plot( img06File, data_plot1 );
1126  }
1127  update_filelist( files, img06File );
1128 
1129  if ( p1type != 4 && p1type != 6 )
1130  {
1131  distrib_plot_2d( ( cnstvb ? 4 : 6 ) );
1132  write_plot( img07File, data_plot1 );
1133  }
1134  update_filelist( files, img07File );
1135 
1136  if ( p1type != 7 )
1137  {
1138  distrib_plot_2d( 7 );
1139  write_plot( img08File, data_plot1 );
1140  }
1141  update_filelist( files, img08File );
1142 
1143  if ( p1type != 8 )
1144  {
1145  distrib_plot_2d( 8 );
1146  write_plot( img09File, data_plot1 );
1147  }
1148  update_filelist( files, img09File );
1149 
1150 DbgLv(1) << "(9)p1type" << p1type;
1151  // Restore upper plot displayed before saves
1152  if ( p1type == 9 ) distrib_plot_resids();
1153  else if ( p1type < 3 ) distrib_plot_stick( p1type );
1154  else distrib_plot_2d( p1type );
1155 
1156  // Save 3-d plot
1157  if ( eplotcd != 0 )
1158  {
1159  write_plot( img10File, NULL );
1160  update_filelist( files, img10File );
1161  }
1162 
1163  // save residual bitmap
1164  write_plot( img11File, NULL );
1165  update_filelist( files, img11File );
1166 
1167  // save any noise plots
1168  if ( ti_noise.count > 0 )
1169  {
1170  if ( resplotd == 0 )
1171  {
1172  resplotd = new US_ResidPlotFem( this );
1173  resplotd->move( rpd_pos );
1174  resplotd->show();
1175  }
1176 
1177  resplotd->set_plot( 1 );
1178  QwtPlot* nois_plot = resplotd->rp_data_plot2();
1179  write_plot( img12File, nois_plot );
1180  update_filelist( files, img12File );
1181  }
1182 
1183  if ( ri_noise.count > 0 )
1184  {
1185  if ( resplotd == 0 )
1186  {
1187  resplotd = new US_ResidPlotFem( this );
1188  resplotd->move( rpd_pos );
1189  resplotd->show();
1190  }
1191 
1192  resplotd->set_plot( 2 );
1193  QwtPlot* nois_plot = resplotd->rp_data_plot2();
1194  write_plot( img13File, nois_plot );
1195  update_filelist( files, img13File );
1196  }
1197 
1198  // Create the model distributions table CSV
1199  model_table( mdistFile );
1200  update_filelist( files, mdistFile );
1201 
1202  // report the files created
1203  QString umsg = tr( "In directory " )
1204  + basename.left( basename.lastIndexOf( "/" ) )
1205  + tr( " ,\nwrote:\n" );
1206 
1207  for ( int ii = 0; ii < files.length(); ii++ )
1208  {
1209  QString fname = files[ ii ];
1210  umsg = umsg + " "
1211  + fname.mid( fname.lastIndexOf( "/" ) + 1 ) + "\n";
1212  }
1213 
1214 QDateTime time1=QDateTime::currentDateTime();
1215  if ( dkdb_cntrls->db() )
1216  { // Copy report files to the database
1217  reportFilesToDB( files );
1218 
1219  umsg = umsg + tr( "\nFiles were also saved to the database.\n" );
1220  }
1221 QDateTime time2=QDateTime::currentDateTime();
1222 int etim1=time0.msecsTo(time1);
1223 int etim2=time1.msecsTo(time2);
1224 int etimt=etim1+etim2;
1225 int et1pc=(etim1*100)/etimt;
1226 int et2pc=(etim2*100)/etimt;
1227 DbgLv(1) << "SAVE-FILES: local ms" << etim1 << "=" << et1pc << "%";
1228 DbgLv(1) << "SAVE-FILES: DB ms" << etim2 << "=" << et2pc << "%";
1229 
1230  QApplication::restoreOverrideCursor();
1231  QMessageBox::information( this, tr( "Successfully Written" ), umsg );
1232 }
1233 
1234 // View the report text
1236 {
1237  QString mtext;
1238  QTextStream ts( &mtext );
1239 
1240  // generate the report file
1241  write_report( ts );
1242 
1243  // display the report dialog
1244  US_Editor* editd = new US_Editor( US_Editor::DEFAULT, true, "", this );
1245  editd->setWindowTitle( tr( "Report: FE Match Model Simulation" ) );
1246  editd->move( this->pos() + QPoint( 100, 100 ) );
1247  editd->resize( 800, 700 );
1248  editd->e->setFont( QFont( US_GuiSettings::fontFamily(),
1250  editd->e->setHtml( mtext );
1251  editd->show();
1252 }
1253 
1254 // Slot to handle a change in Exclude-From
1255 void US_FeMatch::exclude_from( double from )
1256 {
1257  double to = ct_to->value();
1258 
1259  if ( to < from )
1260  {
1261  ct_to->disconnect();
1262  ct_to->setValue( from );
1263 
1264  connect( ct_to, SIGNAL( valueChanged( double ) ),
1265  this, SLOT ( exclude_to ( double ) ) );
1266  }
1267 
1268  data_plot();
1269 }
1270 
1271 // Slot to handle a change in Exclude-To
1272 void US_FeMatch::exclude_to( double to )
1273 {
1274  double from = ct_from->value();
1275 
1276  if ( from > to )
1277  {
1278  ct_from->disconnect();
1279  ct_from->setValue( to );
1280 
1281  connect( ct_from, SIGNAL( valueChanged( double ) ),
1282  this, SLOT ( exclude_from( double ) ) );
1283  }
1284 
1285  data_plot();
1286 }
1287 
1288 // Exclude scans
1290 {
1291  double from = ct_from->value();
1292  double to = ct_to ->value();
1293  int displayedScan = 1;
1294  int drow = lw_triples->currentRow();
1295  edata = &dataList[ drow ];
1296  int totalScans = edata->scanData.size();
1297 
1298  for ( int ii = 0; ii < totalScans; ii++ )
1299  {
1300  if ( excludedScans.contains( ii ) )
1301  continue;
1302 
1303  if ( displayedScan >= from && displayedScan <= to )
1304  excludedScans << ii;
1305 
1306  displayedScan++;
1307  }
1308 
1309  ct_to->setValue( 0 ); // resets both counters and replots
1310 
1311  ct_from->setMaxValue( totalScans - excludedScans.size() );
1312  ct_to ->setMaxValue( totalScans - excludedScans.size() );
1313 
1314  allExcls[ drow ] = excludedScans;
1315 
1316  data_plot();
1317 
1318  pb_reset_exclude->setEnabled( true );
1319 }
1320 
1321 // Respond to click of current type of distribution plot
1323 {
1324  const char* dptyp[] =
1325  {
1326  "s20,w distribution",
1327  "MW distribution",
1328  "D20,w distribution",
1329  "f_f0 vs s20,w",
1330  "f_f0 vs MW",
1331  "vbar vs s20,w",
1332  "vbar vs MW",
1333  "D20,w vs s20,w",
1334  "D20,w vs MW",
1335  "Residuals"
1336  };
1337  const int ndptyp = sizeof( dptyp ) / sizeof( dptyp[0] );
1338 
1339  int itype = type_distrib();
1340 
1341  // get pointer to data for use by plot routines
1342  edata = &dataList[ lw_triples->currentRow() ];
1343 
1344  // set push button text to next type
1345  int ii = itype + 1;
1346  ii = ( ii == ndptyp ) ? 0 : ii;
1347  ii = ( cnstvb && ii == 5 ) ? 7 : ii;
1348  ii = ( !cnstvb && ii == 3 ) ? 5 : ii;
1349  pb_distrib->setText( QString( dptyp[ ii ] ) );
1350 
1351  switch( itype )
1352  {
1353  case 0: // s20,w distribution
1354  case 1: // MW distribution
1355  case 2: // D20,w distribution
1356  distrib_plot_stick( itype ); // bar (1-d) plot
1357  break;
1358  case 3: // f_f0 vs s20,w
1359  case 4: // f_f0 vs MW
1360  case 5: // vbar vs s20,w
1361  case 6: // vbar vs MW
1362  case 7: // D20,w vs s20,w
1363  case 8: // D20,w vs MW
1364  distrib_plot_2d( itype ); // 2-d plot
1365  break;
1366  case 9: // Residuals
1367  distrib_plot_resids(); // residuals plot
1368  break;
1369  }
1370 }
1371 
1372 // Do stick type distribution plot
1374 {
1375  QString pltitle = tr( "Run " ) + edata->runID + tr( " :\nCell " )
1376  + edata->cell + " (" + edata->wavelength + " nm)";
1377  QString xatitle;
1378  QString yatitle = tr( "Rel. Concentr." );
1379 
1380  if ( type == 0 )
1381  {
1382  pltitle = pltitle + tr( "\ns20,W Distribution" );
1383  xatitle = tr( "Corrected Sedimentation Coefficient" );
1384  }
1385 
1386  else if ( type == 1 )
1387  {
1388  pltitle = pltitle + tr( "\nMW Distribution" );
1389  xatitle = tr( "Molecular Weight (Dalton)" );
1390  }
1391 
1392  else if ( type == 2 )
1393  {
1394  pltitle = pltitle + tr( "\nD20,W Distribution" );
1395  xatitle = tr( "D20,W (cm^2/sec)" );
1396  }
1397 
1398  data_plot1->detachItems();
1399 
1400  data_plot1->setTitle( pltitle );
1401  data_plot1->setAxisTitle( QwtPlot::yLeft, yatitle );
1402  data_plot1->setAxisTitle( QwtPlot::xBottom, xatitle );
1403  data_plot1->clear();
1404 
1405  QwtPlotGrid* data_grid = us_grid( data_plot1 );
1406  QwtPlotCurve* data_curv = us_curve( data_plot1, "distro" );
1407 
1408  int dsize = model_used.components.size();
1409  QVector< double > vecx( dsize );
1410  QVector< double > vecy( dsize );
1411  double* xx = vecx.data();
1412  double* yy = vecy.data();
1413  double xmin = 1.0e30;
1414  double xmax = -1.0e30;
1415  double ymin = 1.0e30;
1416  double ymax = -1.0e30;
1417  double xval;
1418  double yval;
1419  double rdif;
1420 
1421  for ( int jj = 0; jj < dsize; jj++ )
1422  {
1423  xval = ( type == 0 ) ? model_used.components[ jj ].s :
1424  ( ( type == 1 ) ? model_used.components[ jj ].mw :
1425  model_used.components[ jj ].D );
1426  yval = model_used.components[ jj ].signal_concentration;
1427  xx[ jj ] = xval;
1428  yy[ jj ] = yval;
1429  xmin = min( xval, xmin );
1430  xmax = max( xval, xmax );
1431  ymin = min( yval, ymin );
1432  ymax = max( yval, ymax );
1433  }
1434 
1435  rdif = ( xmax - xmin ) / 20.0;
1436  xmin -= rdif;
1437  xmax += rdif;
1438  rdif = ( ymax - ymin ) / 20.0;
1439  ymin -= rdif;
1440  ymax += rdif;
1441  xmin = ( type == 0 ) ? xmin : max( xmin, 0.0 );
1442  ymin = max( ymin, 0.0 );
1443 
1444  data_grid->enableYMin( true );
1445  data_grid->enableY( true );
1446  data_grid->setMajPen(
1447  QPen( US_GuiSettings::plotMajGrid(), 0, Qt::DashLine ) );
1448 
1449  data_curv->setData( xx, yy, dsize );
1450  data_curv->setPen( QPen( Qt::yellow, 3, Qt::SolidLine ) );
1451  data_curv->setStyle( QwtPlotCurve::Sticks );
1452 
1453  data_plot1->setAxisAutoScale( QwtPlot::xBottom );
1454  data_plot1->setAxisAutoScale( QwtPlot::yLeft );
1455  data_plot1->setAxisScale( QwtPlot::xBottom, xmin, xmax );
1456  data_plot1->setAxisScale( QwtPlot::yLeft, ymin, ymax );
1457 
1458  data_plot1->replot();
1459 }
1460 
1461 // Do 2d type distribution plot
1463 {
1464  QString pltitle = tr( "Run " ) + edata->runID + tr( " :\nCell " )
1465  + edata->cell + " (" + edata->wavelength + " nm)";
1466  QString yatitle;
1467  QString xatitle;
1468 
1469  if ( type == 3 )
1470  {
1471  pltitle = pltitle + tr( "\nf/f0 vs Sed. Coeff." );
1472  yatitle = tr( "Frictional Ratio f/f0" );
1473  xatitle = tr( "Sedimentation Coefficient s20,W" );
1474  }
1475 
1476  else if ( type == 4 )
1477  {
1478  pltitle = pltitle + tr( "\nf/f0 vs Mol. Weight" );
1479  yatitle = tr( "Frictional Ratio f/f0" );
1480  xatitle = tr( "Molecular Weight" );
1481  }
1482 
1483  else if ( type == 5 )
1484  {
1485  pltitle = pltitle + tr( "\nVbar vs Sed. Coeff." );
1486  yatitle = tr( "Vbar at 20" ) + DEGC;
1487  xatitle = tr( "Sedimentation Coefficient s20,W" );
1488  }
1489 
1490  else if ( type == 6 )
1491  {
1492  pltitle = pltitle + tr( "\nVbar vs Mol. Weight" );
1493  yatitle = tr( "Vbar at 20" ) + DEGC;
1494  xatitle = tr( "Molecular Weight" );
1495  }
1496 
1497  else if ( type == 7 )
1498  {
1499  pltitle = pltitle + tr( "\nDiff. Coeff. vs Sed. Coeff." );
1500  yatitle = tr( "Diff. Coeff. D20,W" );
1501  xatitle = tr( "Sedimentation Coefficient s20,W" );
1502  }
1503 
1504  else if ( type == 8 )
1505  {
1506  pltitle = pltitle + tr( "\nDiff. Coeff. vs Molecular Weight" );
1507  yatitle = tr( "Diff. Coeff. D20,W" );
1508  xatitle = tr( "Molecular Weight" );
1509  }
1510 
1511  data_plot1->setTitle( pltitle );
1512  data_plot1->setAxisTitle( QwtPlot::yLeft, yatitle );
1513  data_plot1->setAxisTitle( QwtPlot::xBottom, xatitle );
1514 
1515  data_plot1->clear();
1516  data_plot1->detachItems();
1517 
1518  QwtPlotGrid* data_grid = us_grid( data_plot1 );
1519  QwtPlotCurve* data_curv = us_curve( data_plot1, "distro" );
1520  QwtSymbol symbol;
1521 
1522  int dsize = model_used.components.size();
1523  QVector< double > vecx( dsize );
1524  QVector< double > vecy( dsize );
1525  double* xx = vecx.data();
1526  double* yy = vecy.data();
1527  double xmin = 1.0e30;
1528  double xmax = -1.0e30;
1529  double ymin = 1.0e30;
1530  double ymax = -1.0e30;
1531  double xval;
1532  double yval;
1533  double rdif;
1534 
1535  for ( int jj = 0; jj < dsize; jj++ )
1536  {
1537  xval = ( ( type & 1 ) == 1 ) ? model_used.components[ jj ].s :
1538  model_used.components[ jj ].mw;
1539 
1540  if ( type < 5 ) yval = model_used.components[ jj ].f_f0;
1541  else if ( type < 7 ) yval = model_used.components[ jj ].vbar20;
1542  else yval = model_used.components[ jj ].D;
1543 
1544  xx[ jj ] = xval;
1545  yy[ jj ] = yval;
1546  xmin = min( xval, xmin );
1547  xmax = max( xval, xmax );
1548  ymin = min( yval, ymin );
1549  ymax = max( yval, ymax );
1550  }
1551 
1552  rdif = ( xmax - xmin ) / 20.0;
1553  xmin -= rdif;
1554  xmax += rdif;
1555  rdif = ( ymax - ymin ) / 20.0;
1556  ymin -= rdif;
1557  ymax += rdif;
1558  xmin = ( type & 1 ) == 1 ? xmin : max( xmin, 0.0 );
1559  ymin = max( ymin, 0.0 );
1560 
1561  data_grid->enableYMin( true );
1562  data_grid->enableY( true );
1563  data_grid->setMajPen(
1564  QPen( US_GuiSettings::plotMajGrid(), 0, Qt::DashLine ) );
1565 
1566  symbol.setStyle( QwtSymbol::Ellipse );
1567  symbol.setPen( QPen( Qt::red ) );
1568  symbol.setBrush( QBrush( Qt::yellow ) );
1569  if ( dsize > 100 )
1570  symbol.setSize( 5 );
1571  else if ( dsize > 50 )
1572  symbol.setSize( 8 );
1573  else if ( dsize > 20 )
1574  symbol.setSize( 10 );
1575  else
1576  symbol.setSize( 12 );
1577 
1578  data_curv->setStyle( QwtPlotCurve::NoCurve );
1579  data_curv->setSymbol( symbol );
1580  data_curv->setData( xx, yy, dsize );
1581 
1582  data_plot1->setAxisAutoScale( QwtPlot::xBottom );
1583  data_plot1->setAxisAutoScale( QwtPlot::yLeft );
1584  data_plot1->setAxisScale( QwtPlot::xBottom, xmin, xmax );
1585  data_plot1->setAxisScale( QwtPlot::yLeft, ymin, ymax );
1586 
1587  data_plot1->replot();
1588 }
1589 
1590 // Do residuals type distribution plot
1592 {
1593  QString pltitle = tr( "Run " ) + edata->runID + tr( " :\nCell " )
1594  + edata->cell + " (" + edata->wavelength + " nm)" + tr( "\nResiduals" );
1595  QString yatitle = tr( "OD Difference" );
1596  QString xatitle = tr( "Radius (cm)" );
1597 
1598  data_plot1->setTitle( pltitle );
1599  data_plot1->setAxisTitle( QwtPlot::yLeft, yatitle );
1600  data_plot1->setAxisTitle( QwtPlot::xBottom, xatitle );
1601 
1602  data_plot1->clear();
1603  data_plot1->detachItems();
1604 
1605  QwtPlotGrid* data_grid = us_grid( data_plot1 );
1606  QwtPlotCurve* data_curv;
1607  QwtPlotCurve* line_curv = us_curve( data_plot1, "resids zline" );
1608 
1609  int dsize = edata->pointCount();
1610  QVector< double > vecx( dsize );
1611  QVector< double > vecy( dsize );
1612  double* xx = vecx.data();
1613  double* yy = vecy.data();
1614  double zx[ 2 ];
1615  double zy[ 2 ];
1616  double xmin = 1.0e30;
1617  double xmax = -1.0e30;
1618  double ymin = 1.0e30;
1619  double ymax = -1.0e30;
1620  double xval;
1621  double yval;
1622  double rdif;
1623 
1624  for ( int jj = 0; jj < dsize; jj++ )
1625  { // accumulate x (radius) values and min,max
1626  xval = edata->radius( jj );
1627  xmin = min( xval, xmin );
1628  xmax = max( xval, xmax );
1629  xx[ jj ] = xval;
1630  }
1631 
1632  rdif = ( xmax - xmin ) / 20.0; // expand grid range slightly
1633  xmin -= rdif;
1634  xmax += rdif;
1635  xmin = max( xmin, 0.0 );
1636 
1637  for ( int ii = 0; ii < scanCount; ii++ )
1638  { // accumulate min,max y (residual) values
1639  for ( int jj = 0; jj < dsize; jj++ )
1640  {
1641  yval = resids[ ii ][ jj ];
1642  ymin = min( yval, ymin );
1643  ymax = max( yval, ymax );
1644  }
1645  }
1646 
1647  rdif = ( ymax - ymin ) / 20.0;
1648  ymin -= rdif;
1649  ymax += rdif;
1650 
1651  data_grid->enableYMin( true );
1652  data_grid->enableY( true );
1653  data_grid->setMajPen(
1654  QPen( US_GuiSettings::plotMajGrid(), 0, Qt::DashLine ) );
1655 
1656  data_plot1->setAxisAutoScale( QwtPlot::xBottom );
1657  data_plot1->setAxisAutoScale( QwtPlot::yLeft );
1658  data_plot1->setAxisScale( QwtPlot::xBottom, xmin, xmax );
1659  data_plot1->setAxisScale( QwtPlot::yLeft, ymin, ymax );
1660 
1661  // draw the red zero line
1662  zx[ 0 ] = xmin;
1663  zx[ 1 ] = xmax;
1664  zy[ 0 ] = 0.0;
1665  zy[ 1 ] = 0.0;
1666  line_curv->setPen( QPen( Qt::red ) );
1667  line_curv->setData( zx, zy, 2 );
1668 
1669  for ( int ii = 0; ii < scanCount; ii++ )
1670  { // draw residual dots a scan at a time
1671 
1672  for ( int jj = 0; jj < dsize; jj++ )
1673  { // get residuals for this scan
1674  yy[ jj ] = resids[ ii ][ jj ];
1675  }
1676 
1677  // plot the residual scatter for this scan
1678  data_curv = us_curve( data_plot1, "resids " + QString::number( ii ) );
1679  data_curv->setPen( QPen( Qt::yellow ) );
1680  data_curv->setStyle( QwtPlotCurve::Dots );
1681  data_curv->setData( xx, yy, dsize );
1682  }
1683 
1684  data_plot1->replot();
1685 }
1686 
1687 // Open a dialog with advanced analysis parameters
1689 {
1691 
1693  (QWidget*)this );
1694  advdiag->show();
1695 }
1696 
1697 // Open a dialog with advanced DMGA-MC analysis parameters
1699 {
1700  US_AdvDmgaMc* advddiag;
1702 
1703  advddiag = new US_AdvDmgaMc( &model_used, imodels, adv_vals,
1704  (QWidget*)this );
1705  advddiag->show();
1706 }
1707 
1708 // open 3d plot dialog
1710 {
1711  if ( eplotcd != 0 )
1712  {
1713  epd_pos = eplotcd->pos();
1714  eplotcd->close();
1715  }
1716 
1717  eplotcd = new US_PlotControlFem( this, &model_used );
1718  eplotcd->move( epd_pos );
1719  eplotcd->show();
1720 }
1721 
1722 // Open a residual plot dialog
1724 {
1725  if ( resplotd != 0 )
1726  {
1727  rpd_pos = resplotd->pos();
1728  resplotd->close();
1729  }
1730 
1731  resplotd = new US_ResidPlotFem( this );
1732  resplotd->move( rpd_pos );
1733  resplotd->show();
1734 }
1735 
1736 // Load the model data and detect if it is RA
1738 {
1739  int drow = lw_triples->currentRow();
1740  QString mdesc;
1741  pb_simumodel->setEnabled( false );
1742  progress->reset();
1743 
1744  // load model
1745  bool loadDB = dkdb_cntrls->db();
1746 
1747 #if 0
1748  if ( dataList[ drow ].channel == "S" )
1749  { // Set up for "manual" model list option for simulated data
1750  if ( ! mfilter.contains( "=m" ) )
1751  mfilter = "=m " + mfilter.replace( "=e", "" ).simplified();
1752  }
1753 #endif
1754 
1755  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
1756 
1757  US_ModelLoader dialog( loadDB, mfilter, model,
1758  mdesc, dataList[ drow ].editGUID );
1759 
1760  connect( &dialog, SIGNAL( changed( bool ) ),
1761  this, SLOT( update_disk_db( bool ) ) );
1762 
1763  dialog.move( this->pos() + QPoint( 200, 200 ) );
1764  QApplication::restoreOverrideCursor();
1765 
1766  if ( dialog.exec() != QDialog::Accepted )
1767  return; // Cancel: bail out now
1768 
1769  qApp->processEvents();
1770 
1771 DbgLv(1) << "post-Load m,e,r GUIDs" << model.modelGUID << model.editGUID
1772  << model.requestGUID;
1773 DbgLv(1) << "post-Load loadDB" << dkdb_cntrls->db();
1774 
1775  model_loaded = model; // Save model exactly as loaded
1776  model_used = model; // Make that the working model
1777  is_dmga_mc = ( model.monteCarlo &&
1778  model.description.contains( "DMGA" ) &&
1779  model.description.contains( "_mcN" ) );
1780 
1781  if ( model.components.size() == 0 )
1782  {
1783  QMessageBox::critical( this, tr( "Empty Model" ),
1784  tr( "Loaded model has ZERO components!" ) );
1785  return;
1786  }
1787 
1788  ti_noise.count = 0;
1789  ri_noise.count = 0;
1790 
1791  // see if there are any noise files to load
1792  if ( ! model_used.editGUID.isEmpty() )
1793  load_noise();
1794 
1795  pb_advanced ->setEnabled( true );
1796  pb_adv_dmga ->setEnabled( is_dmga_mc );
1797  pb_simumodel->setEnabled( true );
1798 
1799  if ( is_dmga_mc )
1800  { // For DMGA-MC loaded model, build the vector of iteration models
1802  // Default the used model to a "Mean" model
1804  }
1805 }
1806 
1807 // Adjust model components based on buffer, vbar, and temperature
1809 {
1810  model = model_used;
1811 
1812  // build model component correction factors
1813  double avgTemp = edata->average_temperature();
1814  double vbar20 = le_vbar ->text().toDouble();
1815 
1816  solution.density = le_density ->text().toDouble();
1817  solution.viscosity = le_viscosity->text().toDouble();
1819  solution.vbar20 = vbar20;
1821 DbgLv(1) << "Fem:Adj: manual" << manual << solution.manual << solution_rec.buffer.manual;
1822 
1823  US_Math2::data_correction( avgTemp, solution );
1824 
1825  double scorrec = solution.s20w_correction;
1826  double dcorrec = solution.D20w_correction;
1827  double mc_vbar = model.components[ 0 ].vbar20;
1828  // Set constant-vbar and constant-ff0 flags
1831 
1833  sd.density = solution.density;
1835  sd.vbar20 = solution.vbar20;
1836  sd.vbar = solution.vbar;
1837  sd.manual = solution.manual;
1838 
1839  if ( cnstvb && mc_vbar != sd.vbar20 && mc_vbar != 0.0 )
1840  { // Use vbar from the model component, instead of from the solution
1841  sd.vbar20 = mc_vbar;
1842  sd.vbar = US_Math2::adjust_vbar20( sd.vbar20, avgTemp );
1843 DbgLv(1) << "Fem:Adj: avgT" << avgTemp << "vb20 vb" << sd.vbar20 << sd.vbar;
1844  US_Math2::data_correction( avgTemp, sd );
1845  scorrec = sd.s20w_correction;
1846  dcorrec = sd.D20w_correction;
1847  }
1848 DbgLv(1) << "Fem:Adj: vbars (t,s,m,c,20) "
1849  << vbar20 << solution.vbar20 << mc_vbar << sd.vbar << sd.vbar20
1850  << "scorr dcorr" << scorrec << dcorrec;
1851 
1852  // Convert to experiment space: adjust s,D based on buffer
1853 
1854  for ( int jj = 0; jj < model.components.size(); jj++ )
1855  {
1857 
1858  sc->vbar20 = ( sc->vbar20 == 0.0 ) ? vbar20 : sc->vbar20;
1859 
1860  if ( ! cnstvb )
1861  { // Set s,D corrections based on component vbar
1862  sd.vbar20 = sc->vbar20;
1863  sd.vbar = US_Math2::adjust_vbar20( sd.vbar20, avgTemp );
1864  US_Math2::data_correction( avgTemp, sd );
1865  scorrec = sd.s20w_correction;
1866  dcorrec = sd.D20w_correction;
1867  }
1868 
1869  sc->s /= scorrec;
1870  sc->D /= dcorrec;
1871 
1872  if ( sc->extinction > 0.0 )
1874  }
1875 
1876 }
1877 
1878 // Load noise record(s) if there are any and user so chooses
1880 {
1881  QStringList mieGUIDs; // list of GUIDs of models-in-edit
1882  QStringList nieGUIDs; // list of GUIDS:type:index of noises-in-edit
1883  QString editGUID = edata->editGUID; // loaded edit GUID
1884  QString modelGUID = model.modelGUID; // loaded model GUID
1885 DbgLv(1) << "editGUID " << editGUID;
1886 DbgLv(1) << "modelGUID " << modelGUID;
1887 
1888  te_desc->setText( tr( "<b>Scanning noise for %1 ...</b>" )
1889  .arg( triples[ lw_triples->currentRow() ] ) );
1890  qApp->processEvents();
1891  US_LoadableNoise lnoise;
1892  bool loadDB = dkdb_cntrls->db();
1893  int nenois = lnoise.count_noise( !loadDB, edata, &model,
1894  mieGUIDs, nieGUIDs );
1895 
1896 for (int jj=0;jj<nenois;jj++)
1897  DbgLv(1) << " jj nieG" << jj << nieGUIDs.at(jj);
1898 
1899  if ( nenois > 0 )
1900  { // There is/are noise(s): ask user if she wants to load
1901  US_Passwd pw;
1902  US_DB2* dbP = loadDB ? new US_DB2( pw.getPasswd() ) : NULL;
1903 
1904  if ( nenois > 1 )
1905  { // more than 1: get choice from noise loader dialog
1906  US_NoiseLoader* nldiag = new US_NoiseLoader( dbP,
1907  mieGUIDs, nieGUIDs, ti_noise, ri_noise, edata );
1908  nldiag->move( this->pos() + QPoint( 200, 200 ) );
1909  nldiag->exec();
1910  qApp->processEvents();
1911 
1912  delete nldiag;
1913  }
1914 
1915  else
1916  { // only 1: just load it
1917  QString noiID = nieGUIDs.at( 0 );
1918  QString typen = noiID.section( ":", 1, 1 );
1919  noiID = noiID.section( ":", 0, 0 );
1920 
1921  if ( typen == "ti" )
1922  ti_noise.load( loadDB, noiID, dbP );
1923 
1924  else
1925  ri_noise.load( loadDB, noiID, dbP );
1926  }
1927 
1928  // noise loaded: insure that counts jive with data
1929  int ntinois = ti_noise.values.size();
1930  int nrinois = ri_noise.values.size();
1931  int nscans = edata->scanCount();
1932  int npoints = edata->pointCount();
1933  int npadded = 0;
1934 
1935  if ( ntinois > 0 && ntinois < npoints )
1936  { // pad out ti noise values to radius count
1937  int jj = ntinois;
1938  while ( jj++ < npoints )
1939  ti_noise.values << 0.0;
1940  ti_noise.count = ti_noise.values.size();
1941  npadded++;
1942  }
1943 
1944  if ( nrinois > 0 && nrinois < nscans )
1945  { // pad out ri noise values to scan count
1946  int jj = nrinois;
1947  while ( jj++ < nscans )
1948  ri_noise.values << 0.0;
1949  ri_noise.count = ri_noise.values.size();
1950  npadded++;
1951  }
1952 
1953  if ( npadded > 0 )
1954  { // let user know that padding occurred
1955  QString pmsg;
1956 
1957  if ( npadded == 1 )
1958  pmsg = tr( "The noise file was padded out with zeroes\n"
1959  "in order to match the data range." );
1960  else
1961  pmsg = tr( "The noise files were padded out with zeroes\n"
1962  "in order to match the data ranges." );
1963 
1964  QMessageBox::information( this, tr( "Noise Padded Out" ), pmsg );
1965  }
1966 
1967  if ( dbP != NULL )
1968  {
1969  delete dbP;
1970  dbP = NULL;
1971  }
1972  }
1973  te_desc->setText( edata->description );
1974  qApp->processEvents();
1975 }
1976 
1977 // Do model simulation
1979 {
1980  double buoy = 1.0 - vbar * DENS_20W;
1981 
1982  if ( buoy <= 0.0 )
1983  {
1984  QMessageBox::critical( this, tr( "Negative Buoyancy Implied" ),
1985  tr( "The current Vbar value (%1) implies a buoyancy\n"
1986  "value (%2) that is non-positive.\n\n"
1987  "Simulate Model cannot proceed with this value. Click on\n"
1988  "the <Solution> button and change the Vbar value.\n"
1989  "Note that the Solution may be accepted without being saved." )
1990  .arg( vbar ).arg( buoy ) );
1991  return;
1992  }
1993 
1994  int drow = lw_triples->currentRow();
1995  US_DataIO::RawData* rdata = &rawList[ drow ];
1996  US_DataIO::EditedData* edata = &dataList[ drow ];
1997  int kscan = rdata->scanCount();
1998  int nscan = edata->scanCount();
1999  int nconc = edata->pointCount();
2000  double radlo = edata->radius( 0 );
2001  double radhi = edata->radius( nconc - 1 );
2002 DbgLv(1) << " kscan nscan nconc" << kscan << nscan << nconc;
2003 DbgLv(1) << " radlo radhi" << radlo << radhi;
2004 DbgLv(1) << " baseline plateau" << edata->baseline << edata->plateau;
2005 
2006  QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
2007  adjust_model();
2008 
2009  // Initialize simulation parameters using edited data information
2010  US_Passwd pw;
2011  US_DB2* dbP = dkdb_cntrls->db() ? new US_DB2( pw.getPasswd() ) : NULL;
2012 
2013  simparams.initFromData( dbP, *edata, !exp_steps );
2014 DbgLv(1) << " initFrDat rotorCalID coeffs" << simparams.rotorCalID
2016 simparams.simpoints = adv_vals[ "simpoints" ].toInt();
2017 DbgLv(1) << " simulation points" << simparams.simpoints;
2018 
2021  simparams.radial_resolution = ( radhi - radlo ) / (double)( nconc - 1 );
2023  if ( exp_steps )
2025 
2026  QString mtyp = adv_vals[ "meshtype" ];
2027  QString gtyp = adv_vals[ "gridtype" ];
2028  QString bvol = adv_vals[ "bndvolume" ];
2029 DbgLv(1) << " meshtype" << mtyp;
2030 
2031  if ( mtyp.contains( "Claverie" ) )
2033  else if ( mtyp.contains( "Moving Hat" ) )
2035  else if ( mtyp.contains( "File:" ) )
2037  else if ( mtyp.contains( "ASTFVM" ) )
2039 
2040  if ( gtyp.contains( "Constant" ) )
2042 
2044  double concval1 = 0.0;
2045 
2046  if ( simparams.band_forming )
2047  {
2048  simparams.band_volume = bvol.toDouble();
2049  //concval1 = 1.0;
2050  //simparams.firstScanIsConcentration = true;
2051  }
2052  else
2053  simparams.band_volume = 0.0;
2054 DbgLv(1) << " duration_hours " << simparams.speed_step[0].duration_hours;
2055 DbgLv(1) << " duration_minutes" << simparams.speed_step[0].duration_minutes;
2056 DbgLv(1) << " delay_hours " << simparams.speed_step[0].delay_hours;
2057 DbgLv(1) << " delay_minutes" << simparams.speed_step[0].delay_minutes;
2058 
2059  // Make a simulation copy of the experimental data without actual readings
2060 
2061 // US_AstfemMath::initSimData( *sdata, *edata, 0.0 );
2062  US_AstfemMath::initSimData( *sdata, *edata, concval1 );
2063 
2064  sdata->cell = rdata->cell;
2065  sdata->channel = rdata->channel;
2066  sdata->description = rdata->description;
2067 DbgLv(1) << " sdata->description" << sdata->description;
2068 DbgLv(1) << " sdata->x0" << sdata->radius(0);
2069 DbgLv(1) << " sdata->xN" << sdata->radius(nconc-1);
2070 DbgLv(1) << " rdata->c0" << rdata->value(0,0);
2071 DbgLv(1) << " rdata->cN" << rdata->value(0,nconc-1);
2072 DbgLv(1) << " edata->c0" << edata->value(0,0);
2073 DbgLv(1) << " edata->cN" << edata->value(0,nconc-1);
2074 DbgLv(1) << " sdata->c00" << sdata->value(0,0);
2075 DbgLv(1) << " sdata->c0N" << sdata->value(0,nconc-1);
2076 DbgLv(1) << " sdata->cM0" << sdata->value(nscan-1,0);
2077 DbgLv(1) << " sdata->cMN" << sdata->value(nscan-1,nconc-1);
2078 DbgLv(1) << " afrsa init";
2079 if ( dbg_level > 1 )
2080  simparams.save_simparms( US_Settings::etcDir() + "/sp_fematch.xml" );
2081 
2082  start_time = QDateTime::currentDateTime();
2083  int ncomp = model.components.size();
2084  compress = le_compress->text().toDouble();
2085  progress->setMaximum( ncomp );
2086  progress->reset();
2087 
2089  int ntc = ( ncomp + nthread - 1 ) / nthread;
2090  nthread = ( ntc > MIN_NTC ) ? nthread : 1;
2091 DbgLv(1) << " nthread ntc ncomp" << nthread << ntc << ncomp;
2092 
2093  // Do simulation by several possible ways: 1-/Multi-thread, ASTFEM/ASTFVM
2094  if ( nthread < 2 )
2095  { // Do a single-thread calculation
2096  if ( model.components[ 0 ].sigma == 0.0 &&
2097  model.components[ 0 ].delta == 0.0 &&
2098  model.coSedSolute < 0.0 &&
2099  compress == 0.0 )
2100  { // ASTFEM
2101  US_Astfem_RSA* astfem_rsa = new US_Astfem_RSA( model, simparams );
2102 
2103  connect( astfem_rsa, SIGNAL( current_component( int ) ),
2104  this, SLOT( update_progress( int ) ) );
2105 DbgLv(1) << " afrsa calc";
2106 //astfem_rsa->setTimeInterpolation( true );
2107 //astfem_rsa->setTimeCorrection( false );
2108  astfem_rsa->set_debug_flag( dbg_level );
2109 //astfem_rsa->set_debug_flag(2);
2110 
2111  astfem_rsa->calculate( *sdata );
2112  }
2113 
2114  else
2115  { // ASTFVM
2116 DbgLv(1) << " afvm calc: sigma delta coSed compress"
2117  << model.components[ 0 ].sigma << model.components[ 0 ].delta
2118  << model.coSedSolute << compress;
2119  US_LammAstfvm *astfvm = new US_LammAstfvm( model, simparams );
2120 
2121  connect( astfvm, SIGNAL( comp_progress( int ) ),
2122  this, SLOT( update_progress( int ) ) );
2123 
2126  astfvm->set_buffer( solution_rec.buffer );
2127  astfvm->calculate( *sdata );
2128  }
2129 
2130  show_results();
2131  }
2132 
2133  else
2134  { // Do multi-thread calculations
2135 DbgLv(1) << " USING THREADING";
2138  tsimdats.clear();
2139  tmodels .clear();
2140  kcomps .clear();
2141  QList< ThreadWorker* > tworkers;
2142  QList< QThreadEx* > wthreads;
2143 
2144  // Build models for each thread
2145  for ( int ii = 0; ii < ncomp; ii++ )
2146  {
2147  if ( ii < nthread )
2148  { // First time through per thread: get initial model and sim data
2149  tmodels << model;
2150  tmodels[ ii ].components.clear();
2151  US_DataIO::RawData sdat = *sdata;
2152  tsimdats << sdat;
2153  kcomps << 0;
2154  }
2155 
2156  // Partition thread models from round-robin fetch of components
2157  int jj = ii % nthread;
2158  tmodels[ jj ].components << model.components[ ii ];
2159  }
2160 
2161  thrdone = 0;
2163 
2164  // Build worker threads and begin running
2165  for ( int ii = 0; ii < nthread; ii++ )
2166  {
2167 DbgLv(1) << "Thr-Bld ii" << ii << "model comps"
2168  << tmodels[ii].components.size();
2169  ThreadWorker* tworker = new ThreadWorker( tmodels[ ii ], simparams,
2170  tsimdats[ ii ], solution_rec.buffer, ii );
2171  QThreadEx* wthread = new QThreadEx();
2172 
2173  tworker->moveToThread( wthread );
2174  tworkers << tworker;
2175  wthreads << wthread;
2176 
2177  connect( wthread, SIGNAL( started() ),
2178  tworker, SLOT ( calc_simulation() ) );
2179 
2180  connect( tworker, SIGNAL( work_progress ( int, int ) ),
2181  this, SLOT( thread_progress( int, int ) ) );
2182  connect( tworker, SIGNAL( work_complete ( int ) ),
2183  this, SLOT( thread_complete( int ) ) );
2184 
2185  wthread->start();
2186  }
2187 DbgLv(1) << " +++End Of Thr-St loop";
2188 
2189  }
2190 }
2191 
2192 // Show simulation and residual when the simulation is complete
2194 {
2195  long dur_calc = start_time.msecsTo( QDateTime::currentDateTime() );
2196  DbgLv(0) << dur_calc << "Msecs. for calculations using"
2197  << nthread << "thread(s)";
2198 
2199  progress->setValue( progress->maximum() );
2200 
2201  int nscan = sdata->scanCount();
2202  int nconc = sdata->pointCount();
2203 DbgLv(1) << " afrsa done M N" << nscan << nconc;
2204 DbgLv(1) << " sdata->x0" << sdata->radius(0);
2205 DbgLv(1) << " sdata->xN" << sdata->radius(nconc-1);
2206 DbgLv(1) << " sdata->c00" << sdata->value(0,0);
2207 DbgLv(1) << " sdata->c0N" << sdata->value(0,nconc-1);
2208 DbgLv(1) << " sdata->cM0" << sdata->value(nscan-1,0);
2209 DbgLv(1) << " sdata->cMN" << sdata->value(nscan-1,nconc-1);
2210 
2211  double rmsd = 0.0;
2213  le_variance->setText( QString::number( rmsd ) );
2214  rmsd = sqrt( rmsd );
2215  le_rmsd ->setText( QString::number( rmsd ) );
2216 
2217  haveSim = true;
2218  pb_distrib->setEnabled( true );
2219  pb_view ->setEnabled( true );
2220  pb_save ->setEnabled( true );
2221  pb_plot3d ->setEnabled( true );
2222  pb_plotres->setEnabled( true );
2223 
2224  calc_residuals(); // calculate residuals
2225 
2226  distrib_plot_resids(); // plot residuals
2227 
2228  data_plot(); // re-plot data+simulation
2229 
2230  if ( rbmapd != 0 )
2231  {
2232  bmd_pos = rbmapd->pos();
2233  rbmapd->close();
2234  }
2235 
2236  rbmapd = new US_ResidsBitmap( resids );
2237  rbmapd->move( bmd_pos );
2238  rbmapd->show();
2239 
2240  plot3d();
2241 
2242  plotres();
2243  QApplication::restoreOverrideCursor();
2244 }
2245 
2246 // Pare down files list by including only the last-edit versions
2247 QStringList US_FeMatch::last_edit_files( QStringList files )
2248 {
2249  QStringList ofiles;
2250  QStringList part;
2251  QString file;
2252  QString test;
2253  QString pfile;
2254  QString ptest;
2255  int nfi = files.size();
2256 
2257  // if only one in list, we need do no more
2258  if ( nfi < 2 )
2259  {
2260  return files;
2261  }
2262 
2263  // make sure files list is in ascending alphabetical order
2264  files.sort();
2265 
2266  // get first file name and its non-editID parts
2267  file = files[ 0 ];
2268  part = file.split( "." );
2269  test = part[ 0 ] + part[ 3 ] + part[ 4 ] + part[ 5 ];
2270 
2271  // skip all but last of any duplicates (differ only in editID)
2272  for ( int ii = 1; ii < nfi; ii++ )
2273  {
2274  pfile = file;
2275  ptest = test;
2276  file = files[ ii ];
2277  part = file.split( "." );
2278  test = part[ 0 ] + part[ 3 ] + part[ 4 ] + part[ 5 ];
2279 
2280  if ( QString::compare( test, ptest ) != 0 )
2281  { // differs by more than just edit, so output previous
2282  ofiles.append( pfile );
2283  }
2284  }
2285 
2286  // output the final
2287  ofiles.append( file );
2288 
2289  return ofiles;
2290 }
2291 
2292 // Set values for component at index
2293 void US_FeMatch::component_values( int /*index*/ )
2294 {
2295 #if 0
2296  le_sedcoeff->setText( QString::number( model.components[ index ].s ) );
2297  le_difcoeff->setText( QString::number( model.components[ index ].D ) );
2298  le_partconc->setText(
2299  QString::number( model.components[ index ].signal_concentration ) );
2300  le_moweight->setText(
2301  QString( "%1 kD, %2" ).arg( model.components[ index ].mw / 1000.0 )
2302  .arg( model.components[ index ].f_f0 ) );
2303 #endif
2304 }
2305 
2306 // Component number changed
2307 void US_FeMatch::comp_number( double cnbr )
2308 {
2309  component_values( (int)cnbr - 1 );
2310 }
2311 
2312 // Interpolate an sdata y (readings) value for a given x (radius)
2313 double US_FeMatch::interp_sval( double xv, double* sx, double* sy, int ssize )
2314 {
2315  for ( int jj = 1; jj < ssize; jj++ )
2316  {
2317  if ( xv == sx[ jj ] )
2318  {
2319  return sy[ jj ];
2320  }
2321 
2322  if ( xv < sx[ jj ] )
2323  { // given x lower than array x: interpolate between point and previous
2324  int ii = jj - 1;
2325  double dx = sx[ jj ] - sx[ ii ];
2326  double dy = sy[ jj ] - sy[ ii ];
2327  return ( sy[ ii ] + ( xv - sx[ ii ] ) * dy / dx );
2328  }
2329  }
2330 
2331  // given x position not found: interpolate using last two points
2332  int jj = ssize - 1;
2333  int ii = jj - 1;
2334  double dx = sx[ jj ] - sx[ ii ];
2335  double dy = sy[ jj ] - sy[ ii ];
2336  return ( sy[ ii ] + ( xv - sx[ ii ] ) * dy / dx );
2337 }
2338 
2339 // Write the report HTML text stream
2340 void US_FeMatch::write_report( QTextStream& ts )
2341 {
2342  ts << html_header( "US_Fematch", text_model( model, 2 ), edata );
2343 // ts << data_details();
2344 // ts << hydrodynamics();
2345 // ts << scan_info();
2346  ts << distrib_info();
2347  ts << " </body>\n</html>\n";
2348 }
2349 
2350 // Calculate average baseline absorbance
2351 double US_FeMatch::calc_baseline( int drow ) const
2352 {
2353  const US_DataIO::EditedData* dd = &dataList[ drow ];
2354  const US_DataIO::Scan* ss = &dd->scanData.last();
2355  int nn = US_DataIO::index( dd->xvalues, dd->baseline );
2356  double bl = 0.0;
2357 
2358  for ( int jj = nn - 5; jj < nn + 6; jj++ )
2359  bl += ss->rvalues[ jj ];
2360 
2361  return ( bl / 11.0 );
2362 }
2363 
2364 // Model type text string
2365 QString US_FeMatch::text_model( US_Model model, int width )
2366 {
2367  QString stitle = model.typeText();
2368  QString title = stitle;
2369 
2370  if ( width != 0 )
2371  { // long title: add any suffixes and check need to center
2372  switch ( (int)model.analysis )
2373  {
2374  case (int)US_Model::TWODSA:
2375  case (int)US_Model::TWODSA_MW:
2376  title = tr( "2-Dimensional Spectrum Analysis" );
2377  break;
2378 
2379  case (int)US_Model::GA:
2380  case (int)US_Model::GA_MW:
2381  title = tr( "Genetic Algorithm Analysis" );
2382  break;
2383 
2384  case (int)US_Model::COFS:
2385  title = tr( "C(s) Analysis" );
2386  break;
2387 
2388  case (int)US_Model::FE:
2389  title = tr( "Finite Element Analysis" );
2390  break;
2391 
2392  case (int)US_Model::PCSA:
2393  title = tr( "Parametrically Constrained Spectrum Analysis\n" );
2394 
2395  if ( stitle.contains( "-SL" ) )
2396  title += tr( "(Straight Line)" );
2397 
2398  else if ( stitle.contains( "-IS" ) )
2399  title += tr( "(Increasing Sigmoid)" );
2400 
2401  else if ( stitle.contains( "-DS" ) )
2402  title += tr( "(Decreasing Sigmoid)" );
2403 
2404  else if ( stitle.contains( "-HL" ) )
2405  title += tr( "(Horizontal Line)" );
2406 
2407  break;
2408 
2409  case (int)US_Model::MANUAL:
2410  default:
2411  title = tr( "2-Dimensional Spectrum Analysis" );
2412  break;
2413  }
2414 
2415  if ( model.associations.size() > 1 )
2416  title = title + " (RA)";
2417 
2418  if ( model.global == US_Model::MENISCUS )
2419  title = title + " (Menisc.)";
2420 
2421  else if ( model.global == US_Model::GLOBAL )
2422  title = title + " (Global)";
2423 
2424  else if ( model.global == US_Model::SUPERGLOBAL )
2425  title = title + " (S.Glob.)";
2426 
2427  if ( model.monteCarlo )
2428  title = title + " (MC)";
2429 
2430  if ( width > title.length() )
2431  { // long title centered: center it in fixed-length string
2432  int lent = title.length();
2433  int lenl = ( width - lent ) / 2;
2434  int lenr = width - lent - lenl;
2435  title = QString( " " ).repeated( lenl ) + title
2436  + QString( " " ).repeated( lenr );
2437  }
2438  }
2439 
2440  return title;
2441 }
2442 
2443 
2444 // Calculate residual absorbance values (data - sim - noise)
2446 {
2447  int dsize = edata->pointCount();
2448  int ssize = sdata->pointCount();
2449  QVector< double > vecxx( dsize );
2450  QVector< double > vecsx( ssize );
2451  QVector< double > vecsy( ssize );
2452  double* xx = vecxx.data();
2453  double* sx = vecsx.data();
2454  double* sy = vecsy.data();
2455  double yval;
2456  double sval;
2457  //double rl = edata->radius( 0 );
2458  //double vh = edata->value( scanCount - 1, dsize - 1 );
2459  double rmsd = 0.0;
2460  double tnoi = 0.0;
2461  double rnoi = 0.0;
2462  bool ftin = ti_noise.count > 0;
2463  bool frin = ri_noise.count > 0;
2464  bool matchd = ( dsize == ssize );
2465  int kpts = 0;
2466 
2467  QVector< double > resscan;
2468 
2469  resids.clear();
2470  resscan.resize( dsize );
2471 
2472  for ( int jj = 0; jj < dsize; jj++ )
2473  {
2474  xx[ jj ] = edata->radius( jj );
2475  }
2476 
2477  for ( int jj = 0; jj < ssize; jj++ )
2478  {
2479  sx[ jj ] = sdata->radius( jj );
2480  if ( sx[ jj ] != xx[ jj ] ) matchd = false;
2481  }
2482 
2483  for ( int ii = 0; ii < scanCount; ii++ )
2484  {
2485  bool usescan = !excludedScans.contains( ii );
2486 
2487  rnoi = frin ? ri_noise.values[ ii ] : 0.0;
2488 
2489  for ( int jj = 0; jj < ssize; jj++ )
2490  {
2491  sy[ jj ] = sdata->value( ii, jj );
2492  }
2493 
2494  for ( int jj = 0; jj < dsize; jj++ )
2495  { // Calculate the residuals and the RMSD
2496  tnoi = ftin ? ti_noise.values[ jj ] : 0.0;
2497 
2498  if ( matchd )
2499  sval = sy[ jj ];
2500  else
2501  sval = interp_sval( xx[ jj ], sx, sy, ssize );
2502 
2503  yval = edata->value( ii, jj ) - sval - rnoi - tnoi;
2504  //if ( xx[ jj ] < rl )
2505  // yval = 0.0;
2506 
2507  if ( usescan )
2508  {
2509  rmsd += sq( yval );
2510  kpts++;
2511  }
2512 
2513  resscan[ jj ] = yval;
2514  }
2515 
2516  resids.append( resscan );
2517  }
2518 
2519  rmsd /= (double)( kpts );
2520  le_variance->setText( QString::number( rmsd ) );
2521  rmsd = sqrt( rmsd );
2522  le_rmsd ->setText( QString::number( rmsd ) );
2523 }
2524 
2525 // Slot to make sure all windows and dialogs get closed
2527 {
2528  if ( rbmapd )
2529  rbmapd->close();
2530 
2531  if ( eplotcd )
2532  eplotcd->close();
2533 
2534  close();
2535 }
2536 
2537 // String to accomplish line indentation
2538 QString US_FeMatch::indent( const int spaces ) const
2539 {
2540  return ( QString( " " ).leftJustified( spaces, ' ' ) );
2541 }
2542 
2543 // Table row HTML with 2 columns
2544 QString US_FeMatch::table_row( const QString& s1, const QString& s2 ) const
2545 {
2546  return( indent( 6 ) + "<tr><td>" + s1 + "</td><td>" + s2 + "</td></tr>\n" );
2547 }
2548 
2549 // Table row HTML with 3 columns
2550 QString US_FeMatch::table_row( const QString& s1, const QString& s2,
2551  const QString& s3 ) const
2552 {
2553  return ( indent( 6 ) + "<tr><td>" + s1 + "</td><td>" + s2 + "</td><td>" + s3
2554  + "</td></tr>\n" );
2555 }
2556 
2557 // Table row HTML with 5 columns
2558 QString US_FeMatch::table_row( const QString& s1, const QString& s2,
2559  const QString& s3, const QString& s4,
2560  const QString& s5 ) const
2561 {
2562  return ( indent( 6 ) + "<tr><td>" + s1 + "</td><td>" + s2 + "</td><td>" + s3
2563  + "</td><td>" + s4 + "</td><td>" + s5 + "</td></tr>\n" );
2564 }
2565 
2566 // Table row HTML with 7 columns
2567 QString US_FeMatch::table_row( const QString& s1, const QString& s2,
2568  const QString& s3, const QString& s4,
2569  const QString& s5, const QString& s6,
2570  const QString& s7 ) const
2571 {
2572  return ( indent( 6 ) + "<tr><td>" + s1 + "</td><td>" + s2 + "</td><td>"
2573  + s3 + "</td><td>" + s4 + "</td><td>" + s5 + "</td><td>"
2574  + s6 + "</td><td>" + s7 + "</td></tr>\n" );
2575 }
2576 
2577 // Compose a report HTML header
2578 QString US_FeMatch::html_header( QString title, QString head1,
2579  US_DataIO::EditedData* edata )
2580 {
2581  QString s = QString( "<?xml version=\"1.0\"?>\n" );
2582  s += "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"\n";
2583  s += " \"http://www.w3.org/TR/xhtml1/DTD"
2584  "/xhtml1-strict.dtd\">\n";
2585  s += "<html xmlns=\"http://www.w3.org/1999/xhtml\""
2586  " xml:lang=\"en\" lang=\"en\">\n";
2587  s += " <head>\n";
2588  s += " <title> " + title + " </title>\n";
2589  s += " <meta http-equiv=\"Content-Type\" content="
2590  "\"text/html; charset=iso-8859-1\"/>\n";
2591  s += " <style type=\"text/css\" >\n";
2592  s += " td { padding-right: 1em; }\n";
2593  s += " body { background-color: white; }\n";
2594  s += " </style>\n";
2595  s += " </head>\n <body>\n";
2596  s += " <h1>" + head1 + "</h1>\n";
2597  s += indent( 4 ) + tr( "<h2>Data Report for Run \"" ) + edata->runID;
2598  s += "\",<br/>\n" + indent( 4 ) + "&nbsp;" + tr( " Cell " ) + edata->cell;
2599  s += tr( ", Channel " ) + edata->channel;
2600  s += tr( ", Wavelength " ) + edata->wavelength;
2601  s += ",<br/>\n" + indent( 4 ) + "&nbsp;" + tr( " Edited Dataset " );
2602  s += edata->editID + "</h2>\n";
2603 
2604  return s;
2605 }
2606 
2607 // Compose data details text
2608 QString US_FeMatch::data_details( void ) const
2609 {
2610  int drow = lw_triples->currentRow();
2611  const US_DataIO::EditedData* d = &dataList[ drow ];
2612  double baseline = calc_baseline( drow );
2613  QString dataType = tr( "Absorbance" );
2614  if ( d->dataType == "RI" ) dataType = tr( "Intensity" );
2615  if ( d->dataType == "WI" ) dataType = tr( "Intensity" );
2616  if ( d->dataType == "IP" ) dataType = tr( "Interference" );
2617  if ( d->dataType == "FI" ) dataType = tr( "Fluourescence" );
2618 
2619  QString s =
2620  "\n" + indent( 4 ) + tr( "<h3>Detailed Run Information:</h3>\n" )
2621  + indent( 4 ) + "<table>\n"
2622  + table_row( tr( "Cell Description:" ), d->description )
2623  + table_row( tr( "Data Directory:" ), workingDir )
2624  + table_row( tr( "Rotor Speed:" ),
2625  QString::number( (int)d->scanData[ 0 ].rpm ) + " rpm" );
2626 
2627  // Temperature data
2628  double sum = 0.0;
2629  double maxTemp = -1.0e99;
2630  double minTemp = 1.0e99;
2631 
2632  for ( int i = 0; i < d->scanData.size(); i++ )
2633  {
2634  double t = d->scanData[ i ].temperature;
2635  sum += t;
2636  maxTemp = max( maxTemp, t );
2637  minTemp = min( minTemp, t );
2638  }
2639 
2640  QString average = QString::number( sum / d->scanData.size(), 'f', 1 );
2641 
2642  s += table_row( tr( "Average Temperature:" ), average + " " + MLDEGC );
2643 
2644  if ( maxTemp - minTemp <= US_Settings::tempTolerance() )
2645  s += table_row( tr( "Temperature Variation:" ), tr( "Within tolerance" ) );
2646  else
2647  s += table_row( tr( "Temperature Variation:" ),
2648  tr( "(!) OUTSIDE TOLERANCE (!)" ) );
2649 
2650  // Time data
2651  double tcorrec = US_Math2::time_correction( dataList );
2652  int minutes = (int)tcorrec / 60;
2653  int seconds = (int)tcorrec % 60;
2654 
2655  QString m = ( minutes == 1 ) ? tr( " minute " ) : tr( " minutes " );
2656  QString sec = ( seconds == 1 ) ? tr( " second" ) : tr( " seconds" );
2657  s += table_row( tr( "Time Correction:" ),
2658  QString::number( minutes ) + m +
2659  QString::number( seconds ) + sec );
2660 
2661  double duration = rawList.last().scanData.last().seconds;
2662 
2663  int hours = (int) duration / 3600;
2664  minutes = (int) duration / 60 - hours * 60;
2665  seconds = (int) duration % 60;
2666 
2667  QString h;
2668  h = ( hours == 1 ) ? tr( " hour " ) : tr( " hours " );
2669  m = ( minutes == 1 ) ? tr( " minute " ) : tr( " minutes " );
2670  sec = ( seconds == 1 ) ? tr( " second " ) : tr( " seconds " );
2671 
2672  s += table_row( tr( "Run Duration:" ),
2673  QString::number( hours ) + h +
2674  QString::number( minutes ) + m +
2675  QString::number( seconds ) + sec );
2676 
2677  // Wavelength, baseline, meniscus, range
2678  s += table_row( tr( "Wavelength:" ), d->wavelength + " nm" ) +
2679  table_row( tr( "Baseline " ) + dataType + ":",
2680  QString::number( baseline, 'f', 6 ) + " OD" ) +
2681  table_row( tr( "Meniscus Position: " ),
2682  QString::number( d->meniscus, 'f', 3 ) + " cm" );
2683 
2684  int rrx = d->xvalues.size() - 1;
2685  double left = d->xvalues[ 0 ];
2686  double right = d->xvalues[ rrx ];
2687 
2688  s += table_row( tr( "Edited Data starts at: " ),
2689  QString::number( left, 'f', 3 ) + " cm" ) +
2690  table_row( tr( "Edited Data stops at: " ),
2691  QString::number( right, 'f', 3 ) + " cm" );
2692  s += indent( 4 ) + "</table>\n";
2693  return s;
2694 }
2695 
2696 // Compose hydrodynamics portion of report text
2697 QString US_FeMatch::hydrodynamics( void ) const
2698 {
2699  // set up hydrodynamics values
2700  double avgTemp = le_temp ->text().section( " ", 0, 0 ).toDouble();
2702  solution.density = le_density ->text().toDouble();
2703  solution.viscosity = le_viscosity->text().toDouble();
2704  solution.manual = manual;
2705  solution.vbar20 = le_vbar ->text().toDouble();
2706  solution.vbar = US_Math2::adjust_vbar20( solution.vbar20, avgTemp );
2707 
2708  US_Math2::data_correction( avgTemp, solution );
2709 
2710  QString s = "\n" + indent( 4 ) + tr( "<h3>Hydrodynamic Settings:</h3>\n" )
2711  + indent( 4 ) + "<table>\n";
2712 
2713  s += table_row( tr( "Viscosity corrected:" ),
2714  QString::number( solution.viscosity, 'f', 5 ) ) +
2715  table_row( tr( "Viscosity (absolute):" ),
2716  QString::number( solution.viscosity_tb, 'f', 5 ) ) +
2717  table_row( tr( "Density corrected:" ),
2718  QString::number( solution.density, 'f', 6 ) + " g/ccm" ) +
2719  table_row( tr( "Density (absolute):" ),
2720  QString::number( solution.density_tb, 'f', 6 ) + " g/ccm" ) +
2721  table_row( tr( "Vbar:" ),
2722  QString::number( solution.vbar, 'f', 6 ) + " ccm/g" ) +
2723  table_row( tr( "Vbar corrected for 20 " ) + MLDEGC + ":",
2724  QString::number( solution.vbar20, 'f', 6 ) + " ccm/g" ) +
2725  table_row( tr( "Buoyancy (Water, 20 " ) + MLDEGC + "): ",
2726  QString::number( solution.buoyancyw, 'f', 6 ) ) +
2727  table_row( tr( "Buoyancy (absolute)" ),
2728  QString::number( solution.buoyancyb, 'f', 6 ) ) +
2729  table_row( tr( "Correction Factor (s):" ),
2730  QString::number( solution.s20w_correction, 'f', 6 ) ) +
2731  table_row( tr( "Correction Factor (D):" ),
2732  QString::number( solution.D20w_correction, 'f', 6 ) ) +
2733  indent( 4 ) + "</table>\n";
2734 
2735  return s;
2736 }
2737 
2738 // Compose scan information portion of report text
2739 QString US_FeMatch::scan_info( void ) const
2740 {
2741  int drow = lw_triples->currentRow();
2742  const US_DataIO::EditedData* d = &dataList[ drow ];
2743  double time_correction = US_Math2::time_correction( dataList );
2744 
2745  QString s = "\n" + indent( 4 ) + tr( "<h3>Scan Information:</h3>\n" )
2746  + indent( 4 ) + "<table>\n";
2747 
2748  s += table_row( tr( "Scan" ), tr( "Corrected Time" ),
2749  tr( "Plateau Concentration" ) );
2750 
2751  for ( int i = 0; i < d->scanData.size(); i++ )
2752  {
2753  QString s1;
2754  QString s2;
2755  QString s3;
2756 
2757  double od = d->scanData[ i ].plateau;
2758  int time = (int)( d->scanData[ i ].seconds - time_correction );
2759 
2760  s1 = s1.sprintf( "%4d", i + 1 );
2761  s2 = s2.sprintf( "%4d min %2d sec", time / 60, time % 60 );
2762  s3 = s3.sprintf( "%.6f OD", od );
2763 
2764  s += table_row( s1, s2, s3 );
2765  }
2766 
2767  s += indent( 4 ) + "</table>\n";
2768 
2769  return s;
2770 }
2771 
2772 // Distribution information HTML string
2774 {
2775  int ncomp = model_used.components.size();
2776  double vari_m = model_used.variance;
2777  double rmsd_m = ( vari_m == 0.0 ) ? 0.0 : sqrt( vari_m );
2778 
2779  if ( ncomp == 0 )
2780  return "";
2781 
2782  QString msim = adv_vals[ "modelsim" ];
2783 
2784  if ( is_dmga_mc )
2785  {
2786 
2787  if ( msim == "model" )
2788  { // Use DMGA-MC single-iteration model
2789  msim = "<b>&nbsp;&nbsp;( single iteration )</b>";
2790  }
2791  else
2792  { // Use mean|median|mode model
2793  msim = "<b>&nbsp;&nbsp;( " + msim + " )</b>";
2794  }
2795  }
2796  else
2797  { // Normal non-DMGA-MC model
2798  msim = "";
2799  if ( model_used.monteCarlo &&
2800  ! model_used.description.contains( "_mcN" ) )
2801  msim = "<b>&nbsp;&nbsp;( single iteration )</b>";
2802  }
2803 
2804  QString mdla = model_used.description
2805  .section( ".", -2, -2 ).section( "_", 1, -1 );
2806  if ( mdla.isEmpty() )
2807  mdla = model_used.description.section( ".", 0, -2 );
2808 
2809  QString mstr = "\n" + indent( 4 )
2810  + tr( "<h3>Data Analysis Settings:</h3>\n" )
2811  + indent( 4 ) + "<table>\n";
2812 
2813  mstr += table_row( tr( "Model Analysis:" ), mdla + msim );
2814  mstr += table_row( tr( "Number of Components:" ),
2815  QString::number( ncomp ) );
2816  mstr += table_row( tr( "Residual RMS Deviation:" ),
2817  le_rmsd->text() );
2818  mstr += table_row( tr( "Model-reported RMSD:" ),
2819  ( rmsd_m > 0.0 ) ? QString::number( rmsd_m ) : "(none)" );
2820 
2821  double sum_mw = 0.0;
2822  double sum_s = 0.0;
2823  double sum_D = 0.0;
2824  double sum_c = 0.0;
2825  double sum_v = 0.0;
2826  double sum_k = 0.0;
2827  double mink = 1e+99;
2828  double maxk = -1e+99;
2829  double minv = 1e+99;
2830  double maxv = -1e+99;
2831 
2832  for ( int ii = 0; ii < ncomp; ii++ )
2833  {
2834  double conc = model_used.components[ ii ].signal_concentration;
2835  double kval = model_used.components[ ii ].f_f0;
2836  double vval = model_used.components[ ii ].vbar20;
2837  sum_c += conc;
2838  sum_mw += ( model_used.components[ ii ].mw * conc );
2839  sum_s += ( model_used.components[ ii ].s * conc );
2840  sum_D += ( model_used.components[ ii ].D * conc );
2841  sum_v += ( vval * conc );
2842  sum_k += ( kval * conc );
2843  mink = qMin( kval, mink );
2844  maxk = qMax( kval, maxk );
2845  minv = qMin( vval, minv );
2846  maxv = qMax( vval, maxv );
2847  }
2848 
2849  mstr += table_row( tr( "Weight Average s20,W:" ),
2850  QString().sprintf( "%6.4e", ( sum_s / sum_c ) ) );
2851  mstr += table_row( tr( "Weight Average D20,W:" ),
2852  QString().sprintf( "%6.4e", ( sum_D / sum_c ) ) );
2853  mstr += table_row( tr( "W.A. Molecular Weight:" ),
2854  QString().sprintf( "%6.4e", ( sum_mw / sum_c ) ) );
2855  if ( ! cnstff )
2856  mstr += table_row( tr( "Weight Average f/f0:" ),
2857  QString::number( ( sum_k / sum_c ) ) );
2858  if ( ! cnstvb )
2859  mstr += table_row( tr( "Weight Average vbar20:" ),
2860  QString::number( ( sum_v / sum_c ) ) );
2861  mstr += table_row( tr( "Total Concentration:" ),
2862  QString().sprintf( "%6.4e", sum_c ) );
2863 
2864  if ( cnstvb )
2865  mstr += table_row( tr( "Constant vbar20:" ),
2866  QString::number( minv ) );
2867  else if ( cnstff )
2868  mstr += table_row( tr( "Constant f/f0:" ),
2869  QString::number( mink ) );
2870  mstr += indent( 4 ) + "</table>\n";
2871 
2872  mstr += "\n" + indent( 4 ) + tr( "<h3>Distribution Information:</h3>\n" );
2873  mstr += indent( 4 ) + "<table>\n";
2874 
2875  if ( cnstvb )
2876  { // Normal constant-vbar distribution
2877  mstr += table_row( tr( "Molec. Wt." ), tr( "S Apparent" ),
2878  tr( "S 20,W" ), tr( "D Apparent" ),
2879  tr( "D 20,W" ), tr( "f / f0" ),
2880  tr( "Concentration" ) );
2881 
2882  for ( int ii = 0; ii < ncomp; ii++ )
2883  {
2884  double conc = model_used.components[ ii ].signal_concentration;
2885  double perc = 100.0 * conc / sum_c;
2886  mstr += table_row(
2887  QString().sprintf( "%10.4e",
2888  model_used.components[ ii ].mw ),
2889  QString().sprintf( "%10.4e",
2890  model .components[ ii ].s ),
2891  QString().sprintf( "%10.4e",
2892  model_used.components[ ii ].s ),
2893  QString().sprintf( "%10.4e",
2894  model .components[ ii ].D ),
2895  QString().sprintf( "%10.4e",
2896  model_used.components[ ii ].D ),
2897  QString().sprintf( "%10.4e",
2898  model_used.components[ ii ].f_f0 ),
2899  QString().sprintf( "%10.4e (%5.2f %%)", conc, perc ) );
2900  }
2901  }
2902 
2903  else if ( cnstff )
2904  { // Constant-f/f0, varying vbar
2905  mstr += table_row( tr( "Molec. Wt." ), tr( "S Apparent" ),
2906  tr( "S 20,W" ), tr( "D Apparent" ),
2907  tr( "D 20,W" ), tr( "Vbar20" ),
2908  tr( "Concentration" ) );
2909 
2910  for ( int ii = 0; ii < ncomp; ii++ )
2911  {
2912  double conc = model_used.components[ ii ].signal_concentration;
2913  double perc = 100.0 * conc / sum_c;
2914  mstr += table_row(
2915  QString().sprintf( "%10.4e",
2916  model_used.components[ ii ].mw ),
2917  QString().sprintf( "%10.4e",
2918  model .components[ ii ].s ),
2919  QString().sprintf( "%10.4e",
2920  model_used.components[ ii ].s ),
2921  QString().sprintf( "%10.4e",
2922  model .components[ ii ].D ),
2923  QString().sprintf( "%10.4e",
2924  model_used.components[ ii ].D ),
2925  QString().sprintf( "%10.4e",
2926  model_used.components[ ii ].vbar20 ),
2927  QString().sprintf( "%10.4e (%5.2f %%)", conc, perc ) );
2928  }
2929  }
2930 
2931  else
2932  { // Neither vbar nor f/f0 are constant
2933  mstr += table_row( tr( "Molec. Wt." ), tr( "S Apparent" ),
2934  tr( "S 20,W" ), tr( "D 20,W" ),
2935  tr( "f / f0" ), tr( "Vbar20" ),
2936  tr( "Concentration" ) );
2937 
2938  for ( int ii = 0; ii < ncomp; ii++ )
2939  {
2940  double conc = model_used.components[ ii ].signal_concentration;
2941  double perc = 100.0 * conc / sum_c;
2942  mstr += table_row(
2943  QString().sprintf( "%10.4e",
2944  model_used.components[ ii ].mw ),
2945  QString().sprintf( "%10.4e",
2946  model .components[ ii ].s ),
2947  QString().sprintf( "%10.4e",
2948  model_used.components[ ii ].s ),
2949  QString().sprintf( "%10.4e",
2950  model_used.components[ ii ].D ),
2951  QString().sprintf( "%10.4e",
2952  model_used.components[ ii ].f_f0 ),
2953  QString().sprintf( "%10.4e",
2954  model_used.components[ ii ].vbar20 ),
2955  QString().sprintf( "%10.4e (%5.2f %%)", conc, perc ) );
2956  }
2957  }
2958 
2959  // Show associations information if present
2960  if ( model_used.associations.size() > 0 )
2961  {
2962  mstr += indent( 4 ) + "</table>\n" + indent( 4 );
2963  mstr += tr( "<h3>Reversible Associations Information:</h3>\n" );
2964  mstr += indent( 4 ) + "<table>\n";
2965  mstr += table_row( tr( "Reactant 1" ), tr( "Reactant 2" ),
2966  tr( "Product" ), tr( "K_dissociation" ),
2967  tr( "k_off Rate" ) );
2968 
2969  for ( int ii = 0; ii < model_used.associations.size(); ii++ )
2970  {
2972  double k_d = as1.k_d;
2973  double k_off = as1.k_off;
2974  QString reac1 = tr( "component %1" ).arg( as1.rcomps[ 0 ] + 1 );
2975  QString reac2 = tr( "(none)" );
2976  QString prod = tr( "component %1" ).arg( as1.rcomps[ 1 ] + 1 );
2977  if ( as1.rcomps.size() > 2 )
2978  {
2979  reac2 = prod;
2980  prod = tr( "component %1" ).arg( as1.rcomps[ 2 ] + 1 );
2981  }
2982 
2983  mstr += table_row( reac1, reac2, prod,
2984  QString().sprintf( "%10.4e", k_d ),
2985  QString().sprintf( "%10.4e", k_off ) );
2986  }
2987  }
2988 
2989  mstr += indent( 4 ) + "</table>\n";
2990 
2991  if ( is_dmga_mc )
2992  { // Compute DMGA-MC statistics and add them to the report
2993  QVector< double > rstats;
2994  QVector< QVector< double > > mstats;
2995  int niters = imodels.size();
2996  int ncomp = imodels[ 0 ].components .size();
2997  int nreac = imodels[ 0 ].associations.size();
2998 
2999  // Build RMSD statistics across iterations
3000  US_DmgaMcStats::build_rmsd_stats( niters, imodels, rstats );
3001 
3002  // Build statistics across iterations
3003  int ntatt = US_DmgaMcStats::build_model_stats( niters, imodels, mstats );
3004 
3005  // Compose the summary statistics chart
3006  mstr += indent( 4 );
3007  mstr += tr( "<h3>Discrete Model GA-MC Summary Statistics:</h3>\n" );
3008  mstr += indent( 4 ) + "<table>\n";
3009  mstr += table_row( tr( "Component" ), tr( "Attribute" ),
3010  tr( "Mean_Value" ), tr( "95%_Confidence(low)" ),
3011  tr( "95%_Confidence(high)" ) );
3012  int kd = 0;
3013  QString fixd = tr( "(Fixed)" );
3014  QString blnk( "" );
3015  QStringList atitl;
3016  QStringList rtitl;
3017  atitl << tr( "Concentration" )
3018  << tr( "Vbar20" )
3019  << tr( "Molecular Weight" )
3020  << tr( "Sedimentation Coefficient" )
3021  << tr( "Diffusion Coefficient" )
3022  << tr( "Frictional Ratio" );
3023  rtitl << tr( "K_dissociation" )
3024  << tr( "K_off Rate" );
3025 
3026  // Show summary of RMSDs
3027  mstr += table_row( tr( "(All)" ), tr( "RMSD" ),
3028  QString().sprintf( "%10.4e", rstats[ 2 ] ),
3029  QString().sprintf( "%10.4e", rstats[ 9 ] ),
3030  QString().sprintf( "%10.4e", rstats[ 10 ] ) );
3031 
3032  // Show summary of component attributes
3033  for ( int ii = 0; ii < ncomp; ii++ )
3034  {
3035  QString compnum = QString().sprintf( "%2d", ii + 1 );
3036  for ( int jj = 0; jj < 6; jj++ )
3037  {
3038  bool is_fixed = ( mstats[ kd ][ 0 ] == mstats[ kd ][ 1 ] );
3039  QString strclo = is_fixed ? fixd :
3040  QString().sprintf( "%10.4e", mstats[ kd ][ 9 ] );
3041  QString strchi = is_fixed ? blnk :
3042  QString().sprintf( "%10.4e", mstats[ kd ][ 10 ] );
3043  mstr += table_row( compnum, atitl[ jj ],
3044  QString().sprintf( "%10.4e", mstats[ kd ][ 2 ] ),
3045  strclo, strchi );
3046  kd++;
3047  }
3048  }
3049 
3050  mstr += indent( 4 ) + "</table>\n";
3051  mstr += indent( 4 ) + "<table>\n";
3052  mstr += table_row( tr( "Reaction" ), tr( "Attribute" ),
3053  tr( "Mean_Value" ), tr( "95%_Confidence(low)" ),
3054  tr( "95%_Confidence(high)" ) );
3055  // Show summary of reaction attributes;
3056  for ( int ii = 0; ii < nreac; ii++ )
3057  {
3058  QString reacnum = QString().sprintf( "%2d", ii + 1 );
3059  bool is_fixed = ( mstats[ kd ][ 0 ] == mstats[ kd ][ 1 ] );
3060  QString strclo = is_fixed ? fixd :
3061  QString().sprintf( "%10.4e", mstats[ kd ][ 9 ] );
3062  QString strchi = is_fixed ? blnk :
3063  QString().sprintf( "%10.4e", mstats[ kd ][ 10 ] );
3064  mstr += table_row( reacnum, tr( "K_dissociation" ),
3065  QString().sprintf( "%10.4e", mstats[ kd ][ 2 ] ),
3066  strclo, strchi );
3067  kd++;
3068  is_fixed = ( mstats[ kd ][ 0 ] == mstats[ kd ][ 1 ] );
3069  strclo = is_fixed ? fixd :
3070  QString().sprintf( "%10.4e", mstats[ kd ][ 9 ] );
3071  strchi = is_fixed ? blnk :
3072  QString().sprintf( "%10.4e", mstats[ kd ][ 10 ] );
3073  mstr += table_row( reacnum, tr( "K_off Rate" ),
3074  QString().sprintf( "%10.4e", mstats[ kd ][ 2 ] ),
3075  strclo, strchi );
3076  kd++;
3077  }
3078 
3079  mstr += indent( 4 ) + "</table>\n";
3080 
3081  // Compose the details statistics entries
3082  mstr += indent( 4 );
3083  mstr += tr( "<h3>Discrete Model GA-MC Detailed Statistics:</h3>\n" );
3084  int icomp = 1;
3085  int ireac = 1;
3086  int kdmax = 6;
3087  int kk = 0;
3088 
3089  // First, the RMSDs
3090  mstr += indent( 4 ) + tr( "<h4>Details for MC Iteration RMSDs:</h4>\n" );
3091  mstr += indent( 4 ) + "<table>\n";
3092  mstr += table_row( tr( "Minimum:" ),
3093  QString().sprintf( "%10.4e", rstats[ 0 ] ) );
3094  mstr += table_row( tr( "Maximum:" ),
3095  QString().sprintf( "%10.4e", rstats[ 1 ] ) );
3096  mstr += table_row( tr( "Mean:" ),
3097  QString().sprintf( "%10.4e", rstats[ 2 ] ) );
3098  mstr += table_row( tr( "Median:" ),
3099  QString().sprintf( "%10.4e", rstats[ 3 ] ) );
3100  mstr += table_row( tr( "Skew:" ),
3101  QString().sprintf( "%10.4e", rstats[ 4 ] ) );
3102  mstr += table_row( tr( "Kurtosis:" ),
3103  QString().sprintf( "%10.4e", rstats[ 5 ] ) );
3104  mstr += table_row( tr( "Lower Mode:" ),
3105  QString().sprintf( "%10.4e", rstats[ 6 ] ) );
3106  mstr += table_row( tr( "Upper Mode:" ),
3107  QString().sprintf( "%10.4e", rstats[ 7 ] ) );
3108  mstr += table_row( tr( "Mode Center:" ),
3109  QString().sprintf( "%10.4e", rstats[ 8 ] ) );
3110  mstr += table_row( tr( "95% Confidence Interval Low:" ),
3111  QString().sprintf( "%10.4e", rstats[ 9 ] ) );
3112  mstr += table_row( tr( "95% Confidence Interval High:" ),
3113  QString().sprintf( "%10.4e", rstats[ 10 ] ) );
3114  mstr += table_row( tr( "99% Confidence Interval Low:" ),
3115  QString().sprintf( "%10.4e", rstats[ 11 ] ) );
3116  mstr += table_row( tr( "99% Confidence Interval High:" ),
3117  QString().sprintf( "%10.4e", rstats[ 12 ] ) );
3118  mstr += table_row( tr( "Standard Deviation:" ),
3119  QString().sprintf( "%10.4e", rstats[ 13 ] ) );
3120  mstr += table_row( tr( "Standard Error:" ),
3121  QString().sprintf( "%10.4e", rstats[ 14 ] ) );
3122  mstr += table_row( tr( "Variance:" ),
3123  QString().sprintf( "%10.4e", rstats[ 15 ] ) );
3124  mstr += table_row( tr( "Correlation Coefficient:" ),
3125  QString().sprintf( "%10.4e", rstats[ 16 ] ) );
3126  mstr += table_row( tr( "Number of Bins:" ),
3127  QString().sprintf( "%10.0f", rstats[ 17 ] ) );
3128  mstr += table_row( tr( "Distribution Area:" ),
3129  QString().sprintf( "%10.4e", rstats[ 18 ] ) );
3130  mstr += table_row( tr( "95% Confidence Limit Low:" ),
3131  QString().sprintf( "%10.4e", rstats[ 19 ] ) );
3132  mstr += table_row( tr( "95% Confidence Limit High:" ),
3133  QString().sprintf( "%10.4e", rstats[ 20 ] ) );
3134  mstr += table_row( tr( "99% Confidence Limit Low:" ),
3135  QString().sprintf( "%10.4e", rstats[ 21 ] ) );
3136  mstr += table_row( tr( "99% Confidence Limit High:" ),
3137  QString().sprintf( "%10.4e", rstats[ 22 ] ) );
3138  mstr += indent( 4 ) + "</table>\n";
3139 
3140  // Then, components and attributes
3141  for ( kd = 0; kd < ntatt; kd++ )
3142  {
3143  QString compnum = tr( "Component %1 " ).arg( icomp );
3144  QString reacnum = tr( "Reaction %1 " ).arg( ireac );
3145  QString attrib = compnum + atitl[ kk ];
3146  mstr += indent( 4 ) + "<h4>" + tr( "Details for " );
3147 
3148  if ( icomp <= ncomp )
3149  { // Title for component detail
3150  if ( imodels[ 0 ].is_product( icomp - 1 ) && kk == 0 )
3151  mstr += compnum + tr( "(Product) Total Concentration" )
3152  + ":</h4>\n";
3153  else
3154  mstr += compnum + atitl[ kk ] + ":</h4>\n";
3155  }
3156  else
3157  { // Title for reaction detail
3158  mstr += reacnum + rtitl[ kk ] + ":</h4>\n";
3159  }
3160 
3161  mstr += indent( 4 ) + "<table>\n";
3162  bool is_fixed = ( mstats[ kd ][ 0 ] == mstats[ kd ][ 1 ] );
3163 
3164  if ( is_fixed )
3165  { // Fixed has limited lines
3166  mstr += table_row( tr( "Minimum:" ),
3167  QString().sprintf( "%10.4e", mstats[ kd ][ 0 ] ) );
3168  mstr += table_row( tr( "Maximum:" ),
3169  QString().sprintf( "%10.4e", mstats[ kd ][ 1 ] ) );
3170  mstr += table_row( tr( "Mean:" ),
3171  QString().sprintf( "%10.4e", mstats[ kd ][ 2 ] ) );
3172  mstr += table_row( tr( "Median (Fixed)" ),
3173  QString().sprintf( "%10.4e", mstats[ kd ][ 3 ] ) );
3174  }
3175 
3176  else
3177  { // Float has full set of statistics details
3178  mstr += table_row( tr( "Minimum:" ),
3179  QString().sprintf( "%10.4e", mstats[ kd ][ 0 ] ) );
3180  mstr += table_row( tr( "Maximum:" ),
3181  QString().sprintf( "%10.4e", mstats[ kd ][ 1 ] ) );
3182  mstr += table_row( tr( "Mean:" ),
3183  QString().sprintf( "%10.4e", mstats[ kd ][ 2 ] ) );
3184  mstr += table_row( tr( "Median:" ),
3185  QString().sprintf( "%10.4e", mstats[ kd ][ 3 ] ) );
3186  mstr += table_row( tr( "Skew:" ),
3187  QString().sprintf( "%10.4e", mstats[ kd ][ 4 ] ) );
3188  mstr += table_row( tr( "Kurtosis:" ),
3189  QString().sprintf( "%10.4e", mstats[ kd ][ 5 ] ) );
3190  mstr += table_row( tr( "Lower Mode:" ),
3191  QString().sprintf( "%10.4e", mstats[ kd ][ 6 ] ) );
3192  mstr += table_row( tr( "Upper Mode:" ),
3193  QString().sprintf( "%10.4e", mstats[ kd ][ 7 ] ) );
3194  mstr += table_row( tr( "Mode Center:" ),
3195  QString().sprintf( "%10.4e", mstats[ kd ][ 8 ] ) );
3196  mstr += table_row( tr( "95% Confidence Interval Low:" ),
3197  QString().sprintf( "%10.4e", mstats[ kd ][ 9 ] ) );
3198  mstr += table_row( tr( "95% Confidence Interval High:" ),
3199  QString().sprintf( "%10.4e", mstats[ kd ][ 10 ] ) );
3200  mstr += table_row( tr( "99% Confidence Interval Low:" ),
3201  QString().sprintf( "%10.4e", mstats[ kd ][ 11 ] ) );
3202  mstr += table_row( tr( "99% Confidence Interval High:" ),
3203  QString().sprintf( "%10.4e", mstats[ kd ][ 12 ] ) );
3204  mstr += table_row( tr( "Standard Deviation:" ),
3205  QString().sprintf( "%10.4e", mstats[ kd ][ 13 ] ) );
3206  mstr += table_row( tr( "Standard Error:" ),
3207  QString().sprintf( "%10.4e", mstats[ kd ][ 14 ] ) );
3208  mstr += table_row( tr( "Variance:" ),
3209  QString().sprintf( "%10.4e", mstats[ kd ][ 15 ] ) );
3210  mstr += table_row( tr( "Correlation Coefficient:" ),
3211  QString().sprintf( "%10.4e", mstats[ kd ][ 16 ] ) );
3212  mstr += table_row( tr( "Number of Bins:" ),
3213  QString().sprintf( "%10.0f", mstats[ kd ][ 17 ] ) );
3214  mstr += table_row( tr( "Distribution Area:" ),
3215  QString().sprintf( "%10.4e", mstats[ kd ][ 18 ] ) );
3216  mstr += table_row( tr( "95% Confidence Limit Low:" ),
3217  QString().sprintf( "%10.4e", mstats[ kd ][ 19 ] ) );
3218  mstr += table_row( tr( "95% Confidence Limit High:" ),
3219  QString().sprintf( "%10.4e", mstats[ kd ][ 20 ] ) );
3220  mstr += table_row( tr( "99% Confidence Limit Low:" ),
3221  QString().sprintf( "%10.4e", mstats[ kd ][ 21 ] ) );
3222  mstr += table_row( tr( "99% Confidence Limit High:" ),
3223  QString().sprintf( "%10.4e", mstats[ kd ][ 22 ] ) );
3224  }
3225 
3226  mstr += indent( 4 ) + "</table>\n";
3227 
3228  if ( (++kk) >= kdmax )
3229  {
3230  kk = 0;
3231  icomp++;
3232  if ( icomp > ncomp )
3233  {
3234  ireac = icomp - ncomp;
3235  kdmax = 2;
3236  }
3237  }
3238  }
3239  }
3240 
3241  return mstr;
3242 }
3243 
3244 // Write out a plot
3245 void US_FeMatch::write_plot( const QString& filename, const QwtPlot* plot )
3246 {
3247  if ( filename.contains( ".svg" ) )
3248  { // Save an SVG file and a PNG copy
3249  if ( US_GuiUtil::save_plot( filename, plot ) != 0 )
3250  QMessageBox::warning( this, tr( "File Write Error" ),
3251  tr( "Unable to write file" ) + filename );
3252  }
3253 
3254  else if ( filename.endsWith( "rbitmap.png" ) )
3255  { // Special case of rbitmap PNG
3256  if ( rbmapd == 0 )
3257  { // if it is not currently displayed, display it
3258  rbmapd = new US_ResidsBitmap( resids );
3259  rbmapd->move( bmd_pos );
3260  rbmapd->show();
3261  rbmapd->raise();
3262  }
3263 
3264  else
3265  { // if already displayed, replot and re-activate
3266  rbmapd->replot( resids );
3267  rbmapd->raise();
3268  rbmapd->activateWindow();
3269  }
3270 
3271  QPixmap pixmap = QPixmap::grabWidget( rbmapd, 0, 0,
3272  rbmapd->width(), rbmapd->height() );
3273 
3274  if ( ! pixmap.save( filename ) )
3275  QMessageBox::warning( this, tr( "File Write Error" ),
3276  tr( "Unable to write file" ) + filename );
3277  }
3278 
3279  else if ( filename.endsWith( "3dplot.png" ) )
3280  { // Special case of 3dplot PNG
3281  if ( eplotcd == 0 )
3282  { // if no 3d plot control up, create it now
3283  eplotcd = new US_PlotControlFem( this, &model );
3284  eplotcd->move( epd_pos );
3285  eplotcd->show();
3286  eplotcd->do_3dplot();
3287  }
3288 
3289 #if defined(Q_WS_WIN) || defined(Q_WS_MAC)
3290  US_Plot3D* widgw = eplotcd->widget_3dplot();
3291  bool ok = widgw->save_plot( filename, QString( "png" ) );
3292 #else
3293  QGLWidget* dataw = eplotcd->data_3dplot();
3294  QPixmap pixmap = dataw->renderPixmap( dataw->width(), dataw->height(),
3295  true );
3296  bool ok = pixmap.save( filename );
3297 #endif
3298 
3299  if ( ! ok )
3300  QMessageBox::warning( this, tr( "File Write Error" ),
3301  tr( "Unable to write file" ) + filename );
3302  }
3303 
3304  else if ( filename.endsWith( ".png" ) )
3305  { // General case of PNG
3306  if ( US_GuiUtil::save_png( filename, plot ) != 0 )
3307  QMessageBox::warning( this, tr( "File Write Error" ),
3308  tr( "Unable to write file" ) + filename );
3309  }
3310 }
3311 
3312 // Create a subdirectory if need be
3313 bool US_FeMatch::mkdir( const QString& baseDir, const QString& subdir )
3314 {
3315  QDir folder( baseDir );
3316 
3317  if ( folder.exists( subdir ) ) return true;
3318 
3319  if ( folder.mkdir( subdir ) ) return true;
3320 
3321  QMessageBox::warning( this,
3322  tr( "File error" ),
3323  tr( "Could not create the directory:\n" ) + baseDir + "/" + subdir );
3324 
3325  return false;
3326 }
3327 
3328 // Slot to handle selection of a new triple
3329 void US_FeMatch::new_triple( int trow )
3330 {
3331  haveSim = false;
3332 
3333  update( trow );
3334 
3335  data_plot();
3336 }
3337 
3338 // Set progress text
3339 void US_FeMatch::set_progress( const QString message )
3340 {
3341  te_desc->setText( "<b>" + message + " ...</b>" );
3342 }
3343 
3344 // Update the disk/DB choice element
3346 {
3347  isDB ? dkdb_cntrls->set_db() : dkdb_cntrls->set_disk();
3348 }
3349 
3350 // Get solution parameters via US_SolutionGui
3352 {
3353  if ( ! dataLoaded )
3354  return;
3355 
3356  int dbdisk = ( dkdb_cntrls->db() ) ? US_Disk_DB_Controls::DB
3358  int expID = 0;
3360  QString runID = dataList[ lw_triples->currentRow() ].runID;
3361 
3362  if ( dkdb_cntrls->db() )
3363  {
3364  US_Passwd pw;
3365  US_DB2 db( pw.getPasswd() );
3366  QStringList query( "get_experiment_info_by_runID" );
3367  query << runID << QString::number( US_Settings::us_inv_ID() );
3368  db.query( query );
3369  if ( db.lastErrno() != US_DB2::NOROWS )
3370  {
3371  db.next();
3372  expID = db.value( 1 ).toString().toInt();
3373  }
3374  }
3375 
3376  US_SolutionGui* soluInfo = new US_SolutionGui( expID, 1, true, dbdisk,
3377  solution_rec, false );
3378 
3379  connect( soluInfo, SIGNAL( updateSolutionGuiSelection( US_Solution ) ),
3380  this, SLOT( updateSolution( US_Solution ) ) );
3381 
3382  soluInfo->exec();
3383 }
3384 
3385 // Update solution parameters after user has made selections
3387 {
3388  solution_rec = solution_sel;
3389 
3390  int bufID = solution_rec.buffer.bufferID.toInt();
3391  QString sbufID = QString::number( bufID );
3392  QString bufDesc = solution_rec.buffer.description;
3393  QString bdens = le_density ->text();
3394  QString bvisc = le_viscosity->text();
3395  QString svbar = le_vbar ->text();
3396  QString bcmpr = "";
3397  QString bmanu = solution_rec.buffer.manual ? "1" : "0";
3398  QString errmsg = "";
3399  QString bufGUID = solution_rec.buffer.GUID;
3400 
3401  if ( dkdb_cntrls->db() )
3402  {
3403  US_Passwd pw;
3404  US_DB2 db( pw.getPasswd() );
3405  US_SolutionVals::bufvals_db( &db, sbufID, bufGUID, bufDesc,
3406  bdens, bvisc, bcmpr, bmanu, errmsg );
3407  }
3408 
3409  else
3410  {
3411  US_SolutionVals::bufvals_disk( sbufID, bufGUID, bufDesc,
3412  bdens, bvisc, bcmpr, bmanu, errmsg );
3413  }
3414 
3415  density = bdens.toDouble();
3416  viscosity = bvisc.toDouble();
3418  svbar = QString::number( vbar );
3419  manual = ( !bmanu.isEmpty() && bmanu == "1" );
3421 
3422  le_density ->setText( bdens );
3423  le_viscosity->setText( bvisc );
3424  le_vbar ->setText( svbar );
3425  le_solution ->setText( solution_rec.solutionDesc );
3426 }
3427 
3428 // Update progress bar as each component is completed
3430 {
3431  progress->setValue( icomp );
3432 }
3433 
3434 // Reset scan excludes
3436 {
3437  int index = lw_triples->currentRow();
3438  US_DataIO::EditedData* d = &dataList[ index ];
3439  int totalScans = d->scanData.size();
3440 
3441  excludedScans.clear();
3442 
3443  ct_from->setMaxValue( totalScans );
3444  ct_to ->setMaxValue( totalScans );
3445 
3446  if ( ct_to->value() != 0 )
3447  ct_to ->setValue( 0 );
3448  else
3449  data_plot();
3450 
3451  pb_reset_exclude->setEnabled( false );
3452  allExcls[ index ] = excludedScans;
3453 }
3454 
3455 // Reset data set
3456 void US_FeMatch::reset( void )
3457 {
3458  if ( ! dataLoaded ) return;
3459 
3460  excludedScans.clear();
3461 
3462  density = DENS_20W;
3463  viscosity = VISC_20W;
3464  vbar = TYPICAL_VBAR;
3465 
3466  le_solution ->setText( tr( "(Experiment's solution)" ) );
3467  le_density ->setText( QString::number( density, 'f', 6 ) );
3468  le_viscosity->setText( QString::number( viscosity, 'f', 5 ) );
3469  le_vbar ->setText( QString::number( vbar, 'f', 5 ) );
3470  ct_from ->disconnect();
3471  ct_to ->disconnect();
3472  ct_from ->setValue( 0 );
3473  ct_to ->setValue( 0 );
3474 
3475  connect( ct_from, SIGNAL( valueChanged( double ) ),
3476  SLOT ( exclude_from( double ) ) );
3477 
3478  connect( ct_to, SIGNAL( valueChanged( double ) ),
3479  SLOT ( exclude_to ( double ) ) );
3480 
3481  lw_triples-> disconnect();
3482  lw_triples-> clear();
3483  dataList. clear();
3484  rawList. clear();
3485  excludedScans.clear();
3486  triples. clear();
3487  allExcls. clear();
3488  resids. clear();
3489 
3490  dataLoaded = false;
3491  buffLoaded = false;
3492  haveSim = false;
3493  mfilter = "";
3494 
3495  data_plot1->detachItems();
3496  data_plot2->detachItems();
3497  data_plot1->clear();
3498  data_plot2->clear();
3499  data_plot1->replot();
3500  data_plot2->replot();
3501 
3502  pb_details ->setEnabled( false );
3503  pb_distrib ->setEnabled( false );
3504  pb_loadmodel->setEnabled( false );
3505  pb_simumodel->setEnabled( false );
3506  pb_view ->setEnabled( false );
3507  pb_save ->setEnabled( false );
3508  pb_exclude ->setEnabled( false );
3509  pb_advanced ->setEnabled( false );
3510  pb_adv_dmga ->setEnabled( false );
3511  pb_plot3d ->setEnabled( false );
3512  pb_plotres ->setEnabled( false );
3513  le_id ->setText( "" );
3514  le_temp ->setText( "" );
3515  te_desc ->setText( "" );
3516 }
3517 
3518 // Return index to type of distribution plot currently displayed
3520 {
3521  const char* dptyp[] =
3522  {
3523  "s20,w distribution",
3524  "MW distribution",
3525  "D20,w distribution",
3526  "f_f0 vs s20,w",
3527  "f_f0 vs MW",
3528  "vbar vs s20,w",
3529  "vbar vs MW",
3530  "D20,w vs s20,w",
3531  "D20,w vs MW",
3532  "Residuals"
3533  };
3534  const int ndptyp = sizeof( dptyp ) / sizeof( dptyp[0] );
3535 
3536  QString curtxt = pb_distrib->text();
3537  int itype = 0;
3538 
3539  for ( int ii = 0; ii < ndptyp; ii++ )
3540  { // identify text of current push button
3541  if ( curtxt == QString( dptyp[ ii ] ) )
3542  { // found: save index and break
3543  itype = ii;
3544  break;
3545  }
3546  }
3547 
3548  return itype;
3549 }
3550 
3551 // Copy report files to the database
3552 void US_FeMatch::reportFilesToDB( QStringList& files )
3553 {
3554  US_Passwd pw;
3555  US_DB2 db( pw.getPasswd() );
3556  US_DB2* dbP = &db;
3557  QStringList query;
3558  US_DataIO::EditedData* edata = &dataList[ lw_triples->currentRow() ];
3559  QString tripdesc = edata->description;
3560 
3561  // Get the ID of the EditedData DB record associated with the report
3562  query << "get_editID" << edata->editGUID;
3563  db.query( query );
3564  db.next();
3565  int idEdit = db.value( 0 ).toString().toInt();
3566 
3567  // Parse the plot files directory and set the runID for the report
3568  QString pfdir = files[ 0 ].left( files[ 0 ].lastIndexOf( "/" ) );
3569  US_Report freport;
3570  freport.runID = runID;
3571 
3572  int st = freport.saveFileDocuments( pfdir, files, dbP, idEdit,
3573  tripdesc );
3574 
3575  if ( st != US_DB2::OK )
3576  {
3577  qDebug() << "*ERROR* saveFileDocuments, status" << st;
3578  }
3579 }
3580 
3581 // Update progress when thread reports
3582 void US_FeMatch::thread_progress( int thr, int icomp )
3583 {
3584  int kcomp = 0;
3585  kcomps[ thr ] = icomp;
3586  for ( int ii = 0; ii < nthread; ii++ )
3587  kcomp += kcomps[ ii ];
3588  progress->setValue( kcomp );
3589 DbgLv(1) << "THR PROGR thr icomp" << thr << icomp << "kcomp" << kcomp;
3590 }
3591 
3592 // Update count of threads completed and colate simulations when all are done
3594 {
3595  thrdone++;
3596 DbgLv(1) << "THR COMPL thr" << thr << "thrdone" << thrdone;
3597 
3598  if ( thrdone >= nthread )
3599  { // All threads are done, so sum thread simulation data
3600  for ( int ii = 0; ii < sdata->scanData.size(); ii++ )
3601  {
3602  for ( int jj = 0; jj < sdata->xvalues.size(); jj++ )
3603  {
3604  double conc = 0.0;
3605 
3606  for ( int kk = 0; kk < nthread; kk++ )
3607  conc += tsimdats[ kk ].value( ii, jj );
3608 
3609  sdata->setValue( ii, jj, conc );
3610  }
3611  }
3612 
3613  // Then show the results
3614  show_results();
3615  }
3616 }
3617 
3618 // Write a file containing a model distributions table in CSV format
3619 void US_FeMatch::model_table( QString mdtFile )
3620 {
3621  // Get the total concentration
3622  int ncomp = model_used.components.size();
3623  if ( ncomp == 0 )
3624  return;
3625  QFile mdt_f( mdtFile );
3626  if ( ! mdt_f.open( QIODevice::WriteOnly | QIODevice::Text ) )
3627  return;
3628  double sum_c = 0.0;
3629 
3630  for ( int ii = 0; ii < ncomp; ii++ )
3631  {
3632  double conc = model_used.components[ ii ].signal_concentration;
3633  sum_c += conc;
3634  }
3635 
3636  // Write out the comma-separated-values text fields
3637  QTextStream ts( &mdt_f );
3638  const QString dquote( "\"" );
3639  const QString comma( "," );
3640  const QString endln( "\n" );
3641 
3642  // Write the header line
3643  ts << dquote + "Molec.Weight" + dquote + comma
3644  + dquote + "S_Apparent" + dquote + comma
3645  + dquote + "S_20_W" + dquote + comma
3646  + dquote + "D_Apparent" + dquote + comma
3647  + dquote + "D_20_W" + dquote + comma
3648  + dquote + "f/f0" + dquote + comma
3649  + dquote + "Vbar20" + dquote + comma
3650  + dquote + "Concentration" + dquote + comma
3651  + dquote + "Conc.Percent" + dquote + endln;
3652 
3653  for ( int ii = 0; ii < ncomp; ii++ )
3654  { // Write each component line
3655  double conc = model_used.components[ ii ].signal_concentration;
3656  double perc = 100.0 * conc / sum_c;
3657  ts << dquote + QString().sprintf( "%10.4e",
3658  model_used.components[ ii ].mw ) + dquote + comma +
3659  dquote + QString().sprintf( "%10.4e",
3660  model .components[ ii ].s ) + dquote + comma +
3661  dquote + QString().sprintf( "%10.4e",
3662  model_used.components[ ii ].s ) + dquote + comma +
3663  dquote + QString().sprintf( "%10.4e",
3664  model .components[ ii ].D ) + dquote + comma +
3665  dquote + QString().sprintf( "%10.4e",
3666  model_used.components[ ii ].D ) + dquote + comma +
3667  dquote + QString().sprintf( "%10.4e",
3668  model_used.components[ ii ].f_f0 ) + dquote + comma +
3669  dquote + QString().sprintf( "%10.4e",
3670  model_used.components[ ii ].vbar20 ) + dquote + comma +
3671  dquote + QString().sprintf( "%10.4e",
3672  conc ) + dquote + comma +
3673  dquote + QString().sprintf( "%5.2f %%",
3674  perc ) + dquote + endln;
3675  }
3676 
3677  int nreac = model_used.associations.size();
3678  if ( nreac == 0 )
3679  return;
3680 
3681  // Write the header line
3682  ts << dquote + "Reaction" + dquote + comma
3683  + dquote + "K_dissociation" + dquote + comma
3684  + dquote + "k_off_Rate" + dquote + endln;
3685 
3686  for ( int ii = 0; ii < nreac; ii++ )
3687  { // Write each reaction line
3688  double k_d = model_used.associations[ ii ].k_d;
3689  double k_off = model_used.associations[ ii ].k_off;
3690  ts << dquote + QString().sprintf( "%4d", ii ) + dquote + comma
3691  + dquote + QString().sprintf( "%10.4e", k_d ) + dquote + comma
3692  + dquote + QString().sprintf( "%10.4e", k_off ) + dquote + endln;
3693  }
3694 }
3695 
3696 // Update report file list, including adding PNG for each SVGZ
3697 void US_FeMatch::update_filelist( QStringList& flist, const QString fname )
3698 {
3699  flist << fname;
3700 
3701  if ( fname.contains( ".svg" ) )
3702  flist << QString( fname ).section( ".", 0, -2 ) + ".png";
3703 }
3704 
3705 // Slot to react to Current Model radio button click
3706 void US_FeMatch::curmod_clicked( bool is_on )
3707 {
3708  // Enable/Disable Next button and model index counter
3709  pb_nextm ->setEnabled( is_on );
3710  ct_model ->setEnabled( is_on );
3711 
3712  if ( is_on )
3713  { // Current Model turned on: update model
3714  update_mc_model();
3715  }
3716 }
3717 
3718 // Slot to react to Mean/Median/Mode button clicked
3719 void US_FeMatch::modbtn_clicked( bool is_on )
3720 {
3721  if ( is_on )
3722  { // Button has been turned on: update model
3723  update_mc_model();
3724  }
3725 }
3726 
3727 // Slot to react to the Next button being clicked
3729 {
3730  int mc_iter = (int)ct_model->value() + 1;
3731 
3732  if ( mc_iter <= mc_iters )
3733  {
3734  ct_model->setValue( mc_iter );
3735  }
3736 
3737  else
3738  {
3739  ct_model->setValue( 1 );
3740  }
3741 }
3742 
3743 // Update the MC model based on type of model selected
3745 {
3746  if ( rb_curmod->isChecked() )
3747  { // The model is the currently selected MC iteration model
3748  }
3749 
3750  else if ( rb_mean ->isChecked() )
3751  { // The model is the mean of all iteration models
3752  }
3753 
3754  else if ( rb_median->isChecked() )
3755  { // The model is the median of all iteration models
3756  }
3757 
3758  else if ( rb_mode ->isChecked() )
3759  { // The model is the mode of all iteration models
3760  }
3761 }
3762 
3763 // Public slot that calls the private one to simulate a model
3765 {
3766  simulate_model();
3767 }
3768