UltraScan III
us_globalequil.cpp
Go to the documentation of this file.
1 #include <QApplication>
3 #include "us_images.h"
4 #include "us_license_t.h"
5 #include "us_license.h"
6 #include "us_data_loader.h"
7 #include "us_gui_settings.h"
8 #include "us_run_details2.h"
9 #include "us_settings.h"
10 #include "us_constants.h"
11 #include "us_editor.h"
12 #include "us_math2.h"
13 #include "us_db2.h"
14 #include "us_passwd.h"
15 #include "us_solution_vals.h"
16 #include "us_simparms.h"
17 #include "us_globalequil.h"
18 #include "us_model_select.h"
19 #include "us_eqreporter.h"
20 
21 // main program
22 int main( int argc, char* argv[] )
23 {
24  QApplication application( argc, argv );
25 
26  #include "main1.inc"
27 
28  // License is OK. Start up.
29 
31  w.show();
32  return application.exec();
33 }
34 
35 // US_GlobalEquil class constructor
37 {
38  setWindowTitle( tr( "Global Equilibrium Analysis" ) );
39  setPalette( US_GuiSettings::frameColor() );
41 
42  QBoxLayout* mainLayout = new QHBoxLayout( this );
43  mainLayout->setSpacing ( 2 );
44  mainLayout->setContentsMargins ( 2, 2, 2, 2 );
45 
46  // Layouts
47  QVBoxLayout* leftLayout = new QVBoxLayout;
48  QVBoxLayout* rightLayout = new QVBoxLayout;
49 
50  QGridLayout* dataSelLayout = new QGridLayout;
51  QGridLayout* scnListLayout = new QGridLayout;
52  QGridLayout* modlFitLayout = new QGridLayout;
53  QGridLayout* paramLayout = new QGridLayout;
54  QGridLayout* statusLayout = new QGridLayout;
55 
56  // Data Selection elements
57  QLabel* lb_datasel = us_banner( tr( "Data Selection" ) );
58  QPushButton* pb_loadExp = us_pushbutton( tr( "Load Experiment" ) );
59  pb_details = us_pushbutton( tr( "Run Details" ) );
60  QLayout* lo_edlast = us_checkbox( tr( "Lastest Data Edit" ),
61  ck_edlast, true );
64  pb_view = us_pushbutton( tr( "View Report" ) );
65  pb_unload = us_pushbutton( tr( "Unload all Data" ) );
66  pb_scdiags = us_pushbutton( tr( "Scan Diagnostics" ) );
67  pb_ckscfit = us_pushbutton( tr( "Check Scans for Fit" ) );
68  pb_conchist = us_pushbutton( tr( "Conc. Histogram" ) );
69  pb_resetsl = us_pushbutton( tr( "Reset Scan Limits" ) );
70  QLabel* lb_prjname = us_label( tr( "Project Name:" ) );
72 
73  pb_details ->setEnabled( false );
74  pb_view ->setEnabled( false );
75  pb_unload ->setEnabled( false );
76  pb_scdiags ->setEnabled( false );
77  pb_ckscfit ->setEnabled( false );
78  pb_conchist->setEnabled( false );
79  pb_resetsl ->setEnabled( false );
80  le_prjname ->setText( tr( "SampleFit" ) );
81 
82  connect( pb_loadExp, SIGNAL( clicked() ),
83  SLOT( load() ) );
84  connect( pb_details, SIGNAL( clicked() ),
85  SLOT( details() ) );
86  connect( pb_view, SIGNAL( clicked() ),
87  SLOT( view_report() ) );
88  connect( pb_unload, SIGNAL( clicked() ),
89  SLOT( unload() ) );
90  connect( pb_scdiags, SIGNAL( clicked() ),
91  SLOT( scan_diags() ) );
92  connect( pb_ckscfit, SIGNAL( clicked() ),
93  SLOT( check_scan_fit() ) );
94  connect( pb_conchist, SIGNAL( clicked() ),
95  SLOT( conc_histogram() ) );
96  connect( pb_resetsl, SIGNAL( clicked() ),
97  SLOT( reset_scan_lims() ) );
98  connect( le_prjname, SIGNAL( textChanged( const QString& ) ),
99  SLOT( new_project_name( const QString& ) ) );
100 
101  int row = 0;
102  dataSelLayout->addWidget( lb_datasel, row++, 0, 1, 2 );
103  dataSelLayout->addWidget( pb_loadExp, row, 0, 1, 1 );
104  dataSelLayout->addWidget( pb_details, row++, 1, 1, 1 );
105  dataSelLayout->addLayout( lo_edlast, row, 0, 1, 1 );
106  dataSelLayout->addLayout( dkdb_cntrls, row++, 1, 1, 1 );
107  dataSelLayout->addWidget( pb_unload, row, 0, 1, 1 );
108  dataSelLayout->addWidget( pb_view, row++, 1, 1, 1 );
109  dataSelLayout->addWidget( pb_scdiags, row, 0, 1, 1 );
110  dataSelLayout->addWidget( pb_ckscfit, row++, 1, 1, 1 );
111  dataSelLayout->addWidget( pb_conchist, row, 0, 1, 1 );
112  dataSelLayout->addWidget( pb_resetsl, row++, 1, 1, 1 );
113  dataSelLayout->addWidget( lb_prjname, row, 0, 1, 1 );
114  dataSelLayout->addWidget( le_prjname, row++, 1, 1, 1 );
115 
116  // Equilibrium Scans Table
117  QLabel* lb_equiscns = us_banner( tr( "List of available Equilibrium"
118  " Scans:" ) );
119  tw_equiscns = new QTableWidget( 15, 5, this );
120  tw_equiscns->setPalette( US_GuiSettings::editColor() );
122  QFont::Bold );
123  QFontMetrics fm( font );
124  int rowHgt = fm.lineSpacing();
125  tw_equiscns->setMaximumHeight( rowHgt * 60 + 12 );
126  tw_equiscns->setRowHeight( 0, rowHgt );
127  tw_equiscns->setFont(
129 
130  QPixmap bapix = US_Images::getImage( US_Images::ARROW_BLUE );
131  blue_arrow = QIcon( bapix );
134  iconw = bapix.width();
135 DbgLv(1) << "BlueArrowIcon isNull" << blue_arrow.isNull();
136 DbgLv(1) << " RedArrowIcon isNull" << red_arrow.isNull();
137 
138  row = 0;
139  scnListLayout->addWidget( lb_equiscns, row++, 0, 1, 2 );
140  scnListLayout->addWidget( tw_equiscns, row, 0, 5, 2 );
141  row += 5;
142  lb_equiscns->setMaximumHeight( lb_prjname->height() );
143 
144  // Model Fit elements
145  QLabel* lb_mfitinfo = us_banner( tr( "Model and Fitting"
146  " Information:" ) );
147  pb_selModel = us_pushbutton( tr( "Select Model" ) );
148  pb_modlCtrl = us_pushbutton( tr( "Model Control" ) );
149  pb_fitcntrl = us_pushbutton( tr( "Fitting Control" ) );
150  pb_loadFit = us_pushbutton( tr( "Load Fit" ) );
151  pb_monCarlo = us_pushbutton( tr( "Monte Carlo" ) );
152 
153  pb_selModel->setEnabled( false );
154  pb_modlCtrl->setEnabled( false );
155  pb_fitcntrl->setEnabled( false );
156  pb_monCarlo->setEnabled( false );
157 
158  connect( pb_selModel, SIGNAL( clicked() ),
159  SLOT( select_model() ) );
160  connect( pb_modlCtrl, SIGNAL( clicked() ),
161  SLOT( model_control() ) );
162  connect( pb_fitcntrl, SIGNAL( clicked() ),
163  SLOT( fitting_control() ) );
164  connect( pb_loadFit, SIGNAL( clicked() ),
165  SLOT( load_fit() ) );
166  connect( pb_monCarlo, SIGNAL( clicked() ),
167  SLOT( monte_carlo() ) );
168 
169  row = 0;
170  modlFitLayout->addWidget( lb_mfitinfo, row++, 0, 1, 2 );
171  modlFitLayout->addWidget( pb_selModel, row, 0, 1, 1 );
172  modlFitLayout->addWidget( pb_modlCtrl, row++, 1, 1, 1 );
173  modlFitLayout->addWidget( pb_loadFit, row, 0, 1, 1 );
174  modlFitLayout->addWidget( pb_fitcntrl, row++, 1, 1, 1 );
175  modlFitLayout->addWidget( pb_monCarlo, row, 1, 1, 1 );
176 
177  // Parameter Information elements
178  QLabel* lb_parminfo = us_banner( tr( "Parameter Information:" ) );
179  pb_floatPar = us_pushbutton( tr( "Float Parameters" ) );
180  pb_initPars = us_pushbutton( tr( "Initialize Parameters" ) );
181  QPushButton* pb_help = us_pushbutton( tr( "Help" ) );
182  QPushButton* pb_close = us_pushbutton( tr( "Close" ) );
183  QLabel* lb_scselect = us_label( tr( "Scan Selector:" ) );
184  ct_scselect = us_counter( 2, 0, 50, 1 );
185 
186  pb_floatPar->setEnabled( false );
187  pb_floatPar->setVisible( false );
188  pb_initPars->setEnabled( false );
189  ct_scselect->setStep( 1.0 );
190  ct_scselect->setValue( 0.0 );
191 
192  connect( pb_floatPar, SIGNAL( clicked() ),
193  SLOT( float_params() ) );
194  connect( pb_initPars, SIGNAL( clicked() ),
195  SLOT( init_params() ) );
196  connect( pb_help, SIGNAL( clicked() ),
197  SLOT( help() ) );
198  connect( pb_close, SIGNAL( clicked() ),
199  SLOT( close_all() ) );
200  connect( ct_scselect, SIGNAL( valueChanged( double ) ),
201  SLOT( scan_select( double ) ) );
202 
203  row = 0;
204  paramLayout ->addWidget( lb_parminfo, row++, 0, 1, 4 );
205  paramLayout ->addWidget( pb_floatPar, row, 0, 1, 2 );
206  paramLayout ->addWidget( pb_initPars, row++, 2, 1, 2 );
207  paramLayout ->addWidget( pb_help, row, 0, 1, 2 );
208  paramLayout ->addWidget( pb_close, row++, 2, 1, 2 );
209  paramLayout ->addWidget( lb_scselect, row, 0, 1, 2 );
210  paramLayout ->addWidget( ct_scselect, row++, 2, 1, 2 );
211 
212  leftLayout->addLayout( dataSelLayout );
213  leftLayout->addLayout( scnListLayout );
214  leftLayout->addLayout( modlFitLayout );
215  leftLayout->addLayout( paramLayout );
216  leftLayout->setStretchFactor( scnListLayout, 10 );
217  leftLayout->setStretchFactor( modlFitLayout, 1 );
218 
219  // Equilibrium Data Plot
220  eplot = new US_Plot( equil_plot,
221  tr( "Experiment Equilibrium Data" ),
222  tr( "Radius (cm)" ),
223  tr( "Absorbance (280 nm)" ) );
224  us_grid( equil_plot );
225 
226  equil_plot->setMinimumSize( 600, 400 );
227  equil_plot->setAxisScale( QwtPlot::yLeft , 0.1, 0.601 );
228  equil_plot->setAxisScale( QwtPlot::xBottom, 5.8, 7.2 );
229 
230  // Status elements
231  QLabel* lb_status = us_label( tr( "Status/Information:" ) );
233  te_status->setWordWrapMode( QTextOption::WordWrap );
234  te_status->setText( tr( "Please select an edited Equilibrium"
235  " Dataset with \"Load Experiment\"" ) );
236  QLabel* lb_currmodl = us_label( tr( "Current Model:" ) );
237  le_currmodl = us_lineedit( tr( "-- none selected --" ) );
238  QLabel* lb_mxfringe = us_label( tr( "Max. OD/Fringe:" ) );
239  le_mxfringe = us_lineedit( tr( "0.90" ) );
240  QLabel* lb_mxfnotes = us_label( tr( "(set to zero to inactivate"
241  " high conc. limits)" ) );
242  te_status ->setAlignment( Qt::AlignCenter );
243  le_currmodl->setAlignment( Qt::AlignCenter );
244  us_setReadOnly( te_status, true );
245  us_setReadOnly( le_currmodl, true );
246  te_status ->setMinimumHeight( rowHgt * 2 + 12 );
247  te_status ->setFixedHeight( rowHgt * 2 + 12 );
248 
249  row = 0;
250  statusLayout ->addWidget( lb_status, row, 0, 1, 2 );
251  statusLayout ->addWidget( te_status, row++, 2, 1, 4 );
252  statusLayout ->addWidget( lb_currmodl, row, 0, 1, 2 );
253  statusLayout ->addWidget( le_currmodl, row++, 2, 1, 4 );
254  statusLayout ->addWidget( lb_mxfringe, row, 0, 1, 2 );
255  statusLayout ->addWidget( le_mxfringe, row, 2, 1, 1 );
256  statusLayout ->addWidget( lb_mxfnotes, row++, 3, 1, 3 );
257 
258  connect( le_mxfringe, SIGNAL( textChanged( const QString& ) ),
259  SLOT( od_limit_changed( const QString& ) ) );
260 
261  rightLayout->addLayout( eplot );
262  rightLayout->addLayout( statusLayout );
263  rightLayout->setStretchFactor( eplot, 10 );
264  rightLayout->setStretchFactor( statusLayout, 2 );
265 
266  mainLayout->addLayout( leftLayout );
267  mainLayout->addLayout( rightLayout );
268  mainLayout->setStretchFactor( leftLayout, 3 );
269  mainLayout->setStretchFactor( rightLayout, 5 );
270 
271  emodctrl = 0;
272  efitctrl = 0;
273  ereporter = 0;
274  emath = 0;
275  ehisto = 0;
276 
277  model_widget = false;
278  fit_widget = false;
279  signal_mc = true;
280  floated_pars = false;
281  show_msgs = true;
282 }
283 
284 // Public slot to respond to a new scan selection, signalled externally
285 void US_GlobalEquil::new_scan( int newscan )
286 {
287  signal_mc = false; // Avoid circular new-scan signals
288 
289  scan_select( (double)newscan ); // Act like scan counter was changed here
290 
291  signal_mc = true; // Re-enable external signalling
292 }
293 
294 // Load equilibrium data
296 {
297  excludedScans.clear();
298  dataList .clear();
299  rawList .clear();
300  triples .clear();
301  ds_vbar20s.clear();
302  ds_densits.clear();
303  ds_viscos .clear();
304  ds_solIDs .clear();
305 
306  modelx = 0;
307  models << "";
308  dataLoaded = false;
309  buffLoaded = false;
310  dataLatest = ck_edlast->isChecked();
313 
314  // Open a dialog to select and load data
316  dataList, triples, workingDir, QString( "equilibrium" ) );
317 
318  connect( dialog, SIGNAL( changed( bool ) ),
319  this, SLOT( update_disk_db( bool ) ) );
320 
321  if ( dialog->exec() != QDialog::Accepted ) return;
322 
323  US_DB2* dbP = NULL;
324 
325  if ( dkdb_cntrls->db() )
326  {
327  US_Passwd pw;
328  dbP = new US_DB2( pw.getPasswd() );
329 
330  workingDir = tr( "(database)" );
331  }
332 
333  else
334  {
335  workingDir = workingDir.section( workingDir.left( 1 ), 4, 4 );
336  workingDir = workingDir.left( workingDir.lastIndexOf( "/" ) );
337  }
338 
339  qApp->processEvents();
340  runfit.dbdisk = dbdisk;
341 
342  if ( dataList[ 0 ].expType != "Equilibrium" )
343  {
344  QMessageBox::critical( this, tr( "Non-Equilibrium Data" ),
345  tr( "The selected data is not of type \"Equilibrium\"!\n"
346  "Please select Equilibrium data." ) );
347  unload();
348  return;
349  }
350 
351  // Count the total scans available in the data sets and get vbars,densities
352  ntscns = 0;
353 
354  for ( int jd = 0; jd < dataList.size(); jd++ )
355  {
356  ntscns += dataList[ jd ].scanData.size();
357 
358  QString s_vbar = QString::number( TYPICAL_VBAR );
359  QString s_dens = QString::number( DENS_20W );
360  QString s_visc;
361  QString s_comp;
362  QString s_manu;
363  QString s_emsg;
364  QString solID;
365  US_SolutionVals::values( dbP, &dataList[ jd ], solID,
366  s_vbar, s_dens, s_visc, s_comp, s_manu, s_emsg );
367  ds_vbar20s << s_vbar.toDouble();
368  ds_densits << s_dens.toDouble();
369  ds_viscos << s_visc.toDouble();
370  ds_solIDs << solID;
371  }
372 DbgLv(1) << " jd vbar20 density" << 0 << ds_vbar20s[0] << ds_densits[0];
373 int nn=dataList.size()-1;
374 DbgLv(1) << " jd vbar20 density" << nn << ds_vbar20s[nn] << ds_densits[nn];
375 
376  // Get Centerpiece bottom and rotor coefficients for calc_bottom
377  US_SimulationParameters simparams;
378  simparams.initFromData( dbP, dataList[ 0 ] );
379  runfit.bottom_pos = simparams.bottom_position;
380  runfit.rcoeffs[ 0 ] = simparams.rotorcoeffs[ 0 ];
381  runfit.rcoeffs[ 1 ] = simparams.rotorcoeffs[ 1 ];
382 
383  // Build the table of available scans
384  QStringList headers;
385  QFont font( US_GuiSettings::fontFamily(),
386  US_GuiSettings::fontSize(), QFont::Bold );
387  QFontMetrics fm( font );
388  int rowHgt = fm.lineSpacing();
389  QString hdr1 = tr( "Scan" );
390  QString hdr2 = tr( "CCW Triple" );
391  QString hdr3 = tr( "Speed" );
392  QString hdr4 = tr( "scan of set" );
393  int whd1 = fm.width( hdr1 + "W" );
394  int whd2 = fm.width( hdr2 + "W" );
395  int whd3 = fm.width( hdr3 + "W" );
396  int whd4 = fm.width( hdr4 + "W" );
397  int whd0 = ( iconw * 3 ) / 2;
398 
399  headers << "" << hdr1 << hdr2 << hdr3 << hdr4;
400  tw_equiscns->setMaximumHeight( rowHgt * 60 + 12 );
401  tw_equiscns->setRowCount( ntscns );
402  tw_equiscns->setHorizontalHeaderLabels( headers );
403  tw_equiscns->verticalHeader()->hide();
404  tw_equiscns->setShowGrid( false );
405  tw_equiscns->setSelectionBehavior( QAbstractItemView::SelectRows );
406  tw_equiscns->setColumnWidth( 0, whd0 );
407  tw_equiscns->setColumnWidth( 1, whd1 );
408  tw_equiscns->setColumnWidth( 2, whd2 );
409  tw_equiscns->setColumnWidth( 3, whd3 );
410  tw_equiscns->setColumnWidth( 4, whd4 );
411  tw_equiscns->setMinimumWidth( 160 );
412  tw_equiscns->setMinimumHeight( 160 );
413 
414  scedits .resize( ntscns );
415  scanfits.clear();
416 
417  int jsscn = 0;
418 
419  for ( int jd = 0; jd < dataList.size(); jd++ )
420  { // Loop to get scans from the data sets
421  edata = &dataList[ jd ];
422  QString triple = triples[ jd ];
423  QString tdesc = edata->description;
424 
425  for ( int jr = 0; jr < edata->speedData.size(); jr++ )
426  { // Add a table entry for each speed step of each triple
427  QTableWidgetItem* item;
428  double drpm = edata->speedData[ jr ].speed;
429  int iscn = edata->speedData[ jr ].first_scan;
430  int kscn = edata->speedData[ jr ].scan_count;
431  sRadLo = edata->speedData[ jr ].dataLeft;
432  sRadHi = edata->speedData[ jr ].dataRight;
433 
434  for ( int js = iscn; js < iscn + kscn; js++ )
435  {
436  // Build a table entry with 5 column items
437  item = new QTableWidgetItem( blue_arrow, "" );
438  item->setToolTip( tdesc );
439 //if(iscn==3) item=new QTableWidgetItem( green_arrow, "" );
440 //if(iscn==5) item=new QTableWidgetItem( red_arrow, "" );
441  tw_equiscns->setItem( jsscn, 0, item );
442 
443  item = new QTableWidgetItem( QString::number( jsscn + 1 ) );
444  item->setFlags( item->flags() ^ Qt::ItemIsEditable );
445  item->setToolTip( tdesc );
446  tw_equiscns->setItem( jsscn, 1, item );
447 
448  item = new QTableWidgetItem( triple );
449  item->setFlags( item->flags() ^ Qt::ItemIsEditable );
450  item->setToolTip( tdesc );
451  tw_equiscns->setItem( jsscn, 2, item );
452 
453  item = new QTableWidgetItem( QString::number( drpm ) );
454  item->setFlags( item->flags() ^ Qt::ItemIsEditable );
455  item->setToolTip( tdesc );
456  tw_equiscns->setItem( jsscn, 3, item );
457 
458  item = new QTableWidgetItem( QString::number( js ) );
459  item->setFlags( item->flags() ^ Qt::ItemIsEditable );
460  item->setToolTip( tdesc );
461  tw_equiscns->setItem( jsscn, 4, item );
462 
463  tw_equiscns->setRowHeight( jsscn, rowHgt );
464 
465  // Build a scan edit record
466  scedits[ jsscn ].dsindex = jd;
467  scedits[ jsscn ].speedx = jr;
468  scedits[ jsscn ].scannbr = js;
469  scedits[ jsscn ].rad_lo = sRadLo;
470  scedits[ jsscn ].rad_hi = sRadHi;
471  scedits[ jsscn ].edited = false;
472 DbgLv(1) << " jsscn jd js" << jsscn << jd << js
473  << " sRadLo sRadHi" << sRadLo << sRadHi;
474 
475  jsscn++;
476  }
477  }
478  }
479 
480  od_limit = 0.9;
481 
482 DbgLv(1) << " LD: setup_runfit";
483  setup_runfit(); // Build the runfit data structure
484 DbgLv(1) << " LD: assign_scanfit";
485  assign_scanfit(); // Build a vector of scanfit data structures
486 
487 DbgLv(1) << " LD: update_limit";
488  update_limit( 0.9 ); // Possibly modify data ranges by OD limit
489 
490  // Reset the range of the scan counter to scans available
491  ct_scselect->setRange( 1.0, (double)jsscn, 1.0 );
492 
493  connect( tw_equiscns, SIGNAL( itemDoubleClicked( QTableWidgetItem* ) ),
494  this, SLOT( doubleClickedItem( QTableWidgetItem* ) ) );
495  connect( tw_equiscns, SIGNAL( itemSelectionChanged( ) ),
496  this, SLOT( itemRowChanged( ) ) );
497 
498  te_status->setText( tr( "To edit (exclude points): Ctrl-click-hold,"
499  " move, and release mouse button in the plot area;"
500  " then release Ctrl key." ) );
501 
502  edata = &dataList[ 0 ];
503 
504  if ( edata->dataType != "RA" )
505  le_mxfringe->setText( "0.0" ); // If not absorbance, change OD limit
506 
507 DbgLv(1) << "eData rvalues size" << edata->scanData[0].rvalues.size();
508  dataLoaded = true;
509  pb_details ->setEnabled( true );
510  pb_unload ->setEnabled( true );
511  pb_selModel->setEnabled( true );
512 }
513 
514 // Open a dialog to display details of the data selected
516 {
518  dialog.exec();
519  qApp->processEvents();
520 }
521 
523 { DbgLv(1) << "VIEW_REPORT()"; }
524 
525 // Unload all data
527 {
528  dataList.clear();
529  rawList .clear();
530  triples .clear();
531  scedits .clear();
532 
533  tw_equiscns->disconnect();
534  tw_equiscns->clear();
535 
536  equil_plot->detachItems();
537  equil_plot->setTitle( tr( "Experiment Equilibrium Data" ) );
538 
539  setup_runfit();
540 
541  dataLoaded = false;
542  pb_details ->setEnabled( false );
543  pb_unload ->setEnabled( false );
544  pb_selModel->setEnabled( false );
545  pb_scdiags ->setEnabled( false );
546 
547  connect( tw_equiscns, SIGNAL( itemDoubleClicked( QTableWidgetItem* ) ),
548  this, SLOT( doubleClickedItem( QTableWidgetItem* ) ) );
549  connect( tw_equiscns, SIGNAL( itemSelectionChanged( ) ),
550  this, SLOT( itemRowChanged( ) ) );
551 }
552 
553 // Generate and display scan diagnostics
555 {
556 DbgLv(1) << "SCAN_DIAGS()";
557  if ( ! dataLoaded ) // Don't bother if no data yet
558  return;
559 
560  if ( ereporter == 0 ) // Create reporter (1st time)
562  runfit, this );
563 
564  ereporter->scan_diagnostics(); // Generate and display report
565 
566  // Modify scans list icons based on new fit flags
567  for ( int jes = 0; jes < scedits.size(); jes++ )
568  {
569  if ( scanfits[ jes ].scanFit ) // Mark scan as fit/non-excluded
570  tw_equiscns->item( jes, 0 )->setIcon( green_arrow );
571 
572  else // Mark scan as non-fit/excluded
573  tw_equiscns->item( jes, 0 )->setIcon( blue_arrow );
574  }
575 
576  pb_conchist->setEnabled( true ); // Enable buttons now useable
577  pb_modlCtrl->setEnabled( true );
578  pb_initPars->setEnabled( true );
579 }
580 
582 {
584 DbgLv(1) << "CHECK_SCAN_FIT()";
585  if ( ereporter == 0 ) // Create reporter (1st time)
587  runfit, this );
588 DbgLv(1) << " CkScFit: sz - sced scnf" << scedits.size() << scanfits.size();
589 
590  // Generate and display report
591  bool crit = ereporter->check_scan_fit( modelx );
592 DbgLv(1) << " CkScFit: modelx crit" << modelx << crit;
593 
594  pb_fitcntrl->setEnabled( !crit ); // Fit Control if no critical errors
595 }
596 
598 {
599 DbgLv(1) << "CONC_HISTOGRAM()";
600  ehisto = new US_EqHistogram( od_limit, scanfits, this, 0 );
601  ehisto->show();
602 }
603 
604 // Reset the scan limits
606 {
607 DbgLv(1) << "RESET_SCAN_LIMS()";
608  sscanx = tw_equiscns->currentRow();
609  int jdx = scedits[ sscanx ].dsindex;
610  int jrx = scedits[ sscanx ].speedx;
611 
612  scedits[ sscanx ].rad_lo = dataList[ jdx ].speedData[ jrx ].dataLeft;
613  scedits[ sscanx ].rad_hi = dataList[ jdx ].speedData[ jrx ].dataRight;
614  scedits[ sscanx ].edited = false;
615 
616  edata_plot();
617  pb_resetsl->setEnabled( false );
618 }
619 
621 { DbgLv(1) << "LOAD_MODEL()"; }
622 void US_GlobalEquil::new_project_name( const QString& newpname )
623 {
624 DbgLv(1) << "NEW_PROJECT_NAME()" << newpname;
625  runfit.projname = newpname;
626 }
627 
628 // Select the model
630 {
631 DbgLv(1) << "SELECT_MODEL()";
633 
634  mdiag->exec();
635 
636 int na=aud_params.size();
637 DbgLv(1) << " modelx" << modelx << " nbr aud params" << na;
638  if ( modelx >= 0 )
639  {
640  modelname = models[ modelx ];
641  le_currmodl->setText( modelname );
642  pb_scdiags ->setEnabled( true );
643 DbgLv(1) << " model" << modelname;
644 if(na==1) DbgLv(1) << " par1: " << aud_params[0];
645 if(na==2) DbgLv(1) << " par1-2: " << aud_params[0] << aud_params[1];
646 if(na==4) DbgLv(1) << " par1-4: " << aud_params[0] << aud_params[1]
647  << aud_params[2] << aud_params[3];
648 
649  setup_runfit();
650  assign_scanfit();
651 
652 //DbgLv(1) << " fix_all model_widget" << model_widget;
653  //fix_all();
654 
655  if ( model_widget )
657 
659  }
660 }
661 
663 {
664 DbgLv(1) << "MODEL_CONTROL()";
665  sscanx = tw_equiscns->currentRow();
666 
667  if ( sscanx < 0 ) scan_select( 1.0 );
668 
669  if ( model_widget )
670  {
671  emodctrl->raise();
673  }
674 
675  else
676  {
677  model_widget = true;
678  sscann = sscanx + 1;
679 
682 
683  connect( emodctrl, SIGNAL( update_scan( int ) ),
684  this, SLOT( new_scan( int ) ) );
685 
686  emodctrl->show();
687  }
688 }
689 
691 {
692 DbgLv(1) << "FITTING_CONTROL()";
693  if ( fit_widget )
694  {
695  efitctrl->raise();
696  }
697 
698  else
699  {
700  fit_widget = true;
701  sscanx = tw_equiscns->currentRow();
702  sscann = sscanx + 1;
703 
704  if ( emath == 0 )
706 
708 
709  if ( ereporter == 0 ) // Create reporter (1st time)
711  runfit, this );
712 
715  fit_widget, sscann );
716 
717  efitctrl->show();
718  }
719 }
720 
722 { DbgLv(1) << "LOAD_FIT()"; }
724 { DbgLv(1) << "MONTE_CARLO()"; }
725 
727 {
728 DbgLv(1) << "FLOAT_PARAMS()";
729  float_all();
730 
731  if ( model_widget )
732  emodctrl->set_float( true );
733 }
734 
735 // Initialize parameters and (if need be) open a model control dialog
737 {
738 DbgLv(1) << "INIT_PARAMS()";
739  // Insure we have an object for doing needed calculations
740  if ( emath == 0 )
742 
743  // Count fitted scans and save the index to the first such
744  int fitx = -1;
745  bool update_mw = true;
746 
747  for ( int ii = 0; ii < scanfits.size(); ii++ )
748  if ( scanfits[ ii ].scanFit && fitx < 0 )
749  fitx = ii;
750 
751 DbgLv(1) << "IP: fitx" << fitx;
752 DbgLv(1) << "IP: points xvs0 xvsN" << scanfits[0].points
753  << scanfits[0].xvs[0] << scanfits[0].xvs[scanfits[0].points-1];
754  if ( fitx < 0 )
755  {
756  QMessageBox::warning( this, tr( "Scan Fits" ),
757  tr( "There are no scans to fit!\n\n"
758  "Please select one or more scans to be fitted." ) );
759  return;
760  }
761 
762  if ( runfit.mw_vals[ 0 ] <= 0.0 )
763  { // Calculate the molecular weight value the 1st time
764  runfit.mw_vals[ 0 ] = emath->linesearch();
765  runfit.mw_rngs[ 0 ] = runfit.mw_vals[ 0 ] * 0.2;
766  }
767 
768  else
769  { // Thereafter, ask if user wants to calculate a new MW
770  QMessageBox msgBox;
771  msgBox.setWindowTitle( tr( "Molecular Weight" ) );
772  msgBox.setText(
773  tr( "Do you want to use the currently defined molecular"
774  " weight for the parameter\n"
775  "initialization or calculate a newly initialized"
776  " molecular weight?" ) );
777  msgBox.setStandardButtons( QMessageBox::Yes | QMessageBox::No
778  | QMessageBox::Cancel );
779  msgBox.setButtonText( QMessageBox::Yes,
780  tr( "New Molecular Weight" ) );
781  msgBox.setButtonText( QMessageBox::No,
782  tr( "Current Molecular Weight" ) );
783 
784  switch( msgBox.exec() )
785  {
786  case QMessageBox::Yes:
787  case QMessageBox::Default:
788  default:
789  runfit.mw_vals[ 0 ] = emath->linesearch();
790  runfit.mw_rngs[ 0 ] = runfit.mw_vals[ 0 ] * 0.2;
791  update_mw = true;
792  break;
793 
794  case QMessageBox::No:
796  runfit.mw_rngs[ 0 ] = runfit.mw_vals[ 0 ] * 0.2;
797  update_mw = false;
798  break;
799  case QMessageBox::Cancel:
800  update_mw = false;
801  break;
802  }
803  }
804 DbgLv(1) << "IP: update_mw" << update_mw;
805 
806  // Initialize parameters
807 DbgLv(1) << "IP: em init_params call";
808  emath->init_params( modelx, update_mw, ds_vbar20s, aud_params );
809 
810  // Display the model control dialog
811  model_control();
812 
813  // Enable buttons that are now appropriate
814  //pb_floatPar->setEnabled( true );
815  pb_ckscfit ->setEnabled( true );
816 }
817 
818 // Respond to a change in the selected scan
819 void US_GlobalEquil::scan_select( double newscan )
820 {
821  sscann = (int)newscan;
822  sscanx = sscann - 1;
823 DbgLv(1) << "SCAN_SELECT()" << sscann << sscanx;
824 
825  tw_equiscns->setCurrentCell( sscanx, 1 ); // Select the table row
826 
827  QString triple = tw_equiscns->item( sscanx, 2 )->text();
828  double drpm = tw_equiscns->item( sscanx, 3 )->text().toDouble();
829 //DbgLv(1) << " Clicked: triple" << triple << "rpm" << drpm;
830  int jdx = -1;
831  int jrx = -1;
832 
833  bool found = findData( triple, drpm, jdx, jrx );
834 //DbgLv(1) << " Clicked: found" << found << " jdx jsx" << jdx << jsx;
835 
836  if ( found ) edata_plot(); // Change the plot to the newly selected scan
837 
838  ct_scselect->disconnect();
839  ct_scselect->setValue( newscan ); // Set the scan nbr counter
840  connect( ct_scselect, SIGNAL( valueChanged( double ) ),
841  SLOT( scan_select( double ) ) );
842 
843 DbgLv(1) << " GE:ClItem: signal_mc model_widget" << signal_mc << model_widget;
844  if ( signal_mc )
845  { // Signalling of model control is enabled
846  if ( model_widget )
847  { // If a model control is up, have it reset the scan
849  }
850  }
851 
852  pb_resetsl ->setEnabled( scedits[ sscanx ].edited );
853 
854 DbgLv(1) << " GE:ScSel: signal_mc model_widget" << signal_mc << model_widget;
855  if ( signal_mc )
856  { // Signalling of model control is enabled
857  if ( model_widget )
858  { // If a model control is up, have it reset the scan
860  }
861  }
862 }
863 
864 // Close all opened children, then close main
866 {
867 //DbgLv(1) << "CLOSE_ALL()";
868  if ( model_widget ) emodctrl->close();
869  if ( fit_widget ) efitctrl->close();
870  if ( emath != 0 ) delete emath;
871  if ( ereporter != 0 ) delete ereporter;
872 
873  close();
874 }
875 
876 // Update Disk/DB selection
877 void US_GlobalEquil::update_disk_db( bool dbaccess )
878 {
879  if ( dbaccess )
880  { // Database
881  dkdb_cntrls->set_db();
883  }
884 
885  else
886  { // Local Disk
889  }
890 
891  runfit.dbdisk = dbdisk;
892 }
893 
894 // Respond to a change in the row selected
896 {
897 DbgLv(1) << "itemRowChanged";
898  scan_select( (double)( tw_equiscns->currentRow() + 1 ) );
899 }
900 
901 // Respond to a table row being double-clicked
902 void US_GlobalEquil::doubleClickedItem( QTableWidgetItem* item )
903 {
904  int row = item->row();
905  bool fit = ! scanfits[ row ].scanFit; // reverse fit setting
906  bool excl = scanfits[ row ].autoExcl;
907  scanfits[ row ].scanFit = fit;
908 
909 DbgLv(1) << "TableItemDoubleClicked row col" << row << item->column();
910  if ( ! fit ) // Mark scan as non-fit
911  tw_equiscns->item( row, 0 )->setIcon( blue_arrow );
912 
913  else if ( ! excl ) // Mark scan as fit/non-excluded
914  tw_equiscns->item( row, 0 )->setIcon( green_arrow );
915 
916  else // Mark scan as fit/excluded
917  tw_equiscns->item( row, 0 )->setIcon( red_arrow );
918 }
919 
920 
921 // Find the data (triple and speed step) corresponding to a scan selection
922 bool US_GlobalEquil::findData( QString trip, double drpm, int& jdx, int& jrx )
923 {
924  bool found = false;
925  jdx = -1;
926  jrx = -1;
927 
928  while ( ++jdx < dataList.size() )
929  { // Search the data set list
930  jrx = -1;
931 
932  if ( trip == triples[ jdx ] )
933  { // If we are at the right triple, examine the speed data
934 
935  while ( ++jrx < dataList[ jdx ].speedData.size() )
936  { // Search the speed steps in this triple
937 
938  if ( dataList[ jdx ].speedData[ jrx ].speed == drpm )
939  { // Right speed in right triple: get data and mark found
940  edata = &dataList[ jdx ];
941  spdata = &edata->speedData[ jrx ];
942  found = true;
943  break;
944  }
945  }
946  }
947 
948  if ( found ) break; // Break out of data list search when data found
949  }
950 
951  return found;
952 }
953 
954 // Plot equilibrium data as ellipses in radius,absorbance plane
956 {
957  sscanx = tw_equiscns->currentRow();
958 
959  if ( sscanx < 0 )
960  {
961  sscanx = 0;
962  tw_equiscns->setCurrentCell( sscanx, 1 );
963  edata = &dataList[ 0 ];
964  spdata = &edata->speedData[ 0 ];
965  }
966 
967  sscann = sscanx + 1;
968  int iscan = spdata->first_scan;
969  int kscan = spdata->scan_count;
970  int jscan = tw_equiscns->item( sscanx, 4 )->text().toInt();
971 
972  if ( jscan < iscan || jscan >= ( iscan + kscan ) )
973  {
974  QMessageBox::warning( this, tr( "Scan Problem" ),
975  tr( "Scan %1 is not within speed data scan range: %2 for %3" )
976  .arg( jscan ).arg( iscan ).arg( kscan ) );
977  return;
978  }
979 
980  int nrpts = edata->pointCount();
981  double drpm = spdata->speed;
982  double radl = spdata->dataLeft;
983  double radr = spdata->dataRight;
984 DbgLv(1) << "EdataPlot: radl radr" << radl << radr;
985  QString runID = edata->runID;
986  QString editID = edata->editID;
987  QString cell = edata->cell;
988  QString chan = edata->channel;
989  QString waveln = edata->wavelength;
990 
991  // Initialize the plot and its titles
992  equil_plot->detachItems();
993  equil_plot->setTitle(
994  tr( "Run: " ) + runID + tr( " Edit: " ) + editID + "\n" +
995  tr( "Cell " ) + cell + tr( ", Channel " ) + chan +
996  tr( ", " ) + QString::number( drpm ) + tr( " rpm, Scan " ) +
997  QString::number( sscann ) );
998  equil_plot->setAxisTitle( QwtPlot::yLeft,
999  tr( "Absorbance (" ) + waveln + tr( " nm)" ) );
1000 
1001  // Set up the grid
1002  QwtPlotGrid* grid = us_grid( equil_plot );
1003  grid->enableYMin( true );
1004  grid->enableY ( true );
1005  grid->setMajPen( QPen( US_GuiSettings::plotMajGrid(), 0, Qt::DashLine ) );
1006  grid->setMinPen( QPen( US_GuiSettings::plotMinGrid(), 0, Qt::DotLine ) );
1007 
1008  // Set up the picker for mouse down, moves and up
1009  QwtPlotPicker* pick = new US_PlotPicker( equil_plot );
1010  pick->setRubberBand( QwtPicker::CrossRubberBand );
1011  connect( pick, SIGNAL( cMouseDown( const QwtDoublePoint& ) ),
1012  SLOT( pMouseDown( const QwtDoublePoint& ) ) );
1013  connect( pick, SIGNAL( cMouseUp( const QwtDoublePoint& ) ),
1014  SLOT( pMouseUp( const QwtDoublePoint& ) ) );
1015  connect( pick, SIGNAL( cMouseDrag( const QwtDoublePoint& ) ),
1016  SLOT( pMouseMoved( const QwtDoublePoint& ) ) );
1017 
1018  if ( scedits[ sscanx ].edited )
1019  {
1020  radl = scedits[ sscanx ].rad_lo;
1021  radr = scedits[ sscanx ].rad_hi;
1022  }
1023 
1024  else
1025  {
1026  mDown = false;
1027  mMoved = false;
1028  mLowerH = false;
1029  sRadLo = 0.0;
1030  sRadHi = 0.0;
1031  }
1032 DbgLv(1) << "EdataPlot: radl radr" << radl << radr
1033  << " edited" << scedits[sscanx].edited;
1034 
1035  // Accumulate data points
1036  rvec.fill( 0.0, nrpts );
1037  vvec.fill( 0.0, nrpts );
1038  double* ra = rvec.data();
1039  double* va = vvec.data();
1040 
1041  int isc = jscan - 1;
1042  int count = 0;
1043  double rlo = 9e+10;
1044  double rhi = -9e+10;
1045  double vlo = 9e+10;
1046  double vhi = -9e+10;
1047  int krpt = min( nrpts, scanfits[ sscanx ].stop_ndx + 1 );
1048 
1049  for ( int jj = 0; jj < krpt; jj++ )
1050  {
1051  double rv = edata->radius( jj );
1052 
1053  if ( rv >= radl && rv <= radr )
1054  {
1055  double vv = edata->value( isc, jj );
1056  ra[ count ] = rv;
1057  va[ count++ ] = vv;
1058  rlo = min( rlo, rv );
1059  rhi = max( rhi, rv );
1060  vlo = min( vlo, vv );
1061  vhi = max( vhi, vv );
1062  }
1063  }
1064 
1065  cRadLo = radl;
1066  cRadHi = radr;
1067 DbgLv(1) << "EdataPlot: cRadLo cRadHi" << cRadLo << cRadHi;
1068 DbgLv(1) << "EdataPlot: dr0 drn" << edata->radius(0) << edata->radius(nrpts-1);
1069 DbgLv(1) << "EdataPlot: ra0 rak" << ra[0] << ra[count-1];
1070 DbgLv(1) << "EdataPlot: va0 vak" << va[0] << va[count-1];
1071 DbgLv(1) << "EdataPlot: count" << count;
1072  vecknt = count;
1073 
1074  double rpad = ( rhi - rlo ) * 0.05; // pad range 5 percent beyond low,high
1075  double vpad = ( vhi - vlo ) * 0.05;
1076  rlo -= rpad;
1077  rhi += rpad;
1078  vlo -= vpad;
1079  vhi += vpad;
1080 
1081  // Set the scale and plot data points as ellipses
1082  equil_plot->setAxisScale( QwtPlot::xBottom, rlo, rhi );
1083  equil_plot->setAxisScale( QwtPlot::yLeft, vlo, vhi );
1084 
1085  QwtSymbol sym;
1086  sym.setStyle( QwtSymbol::Ellipse );
1087  sym.setPen ( QPen( Qt::blue ) );
1088  sym.setBrush( QBrush( Qt::yellow ) );
1089  sym.setSize ( 10 );
1090 
1091  QwtPlotCurve* curve = us_curve( equil_plot, "Equil Data" );
1092  curve->setStyle( QwtPlotCurve::NoCurve );
1093  curve->setSymbol( sym );
1094  curve->setData( ra, va, count );
1095 
1096  equil_plot->replot();
1097 }
1098 
1099 // Re-draw curve after editing to show yellow and red ellipses
1101 {
1102  // Scan data points to find where radius crosses current mouse position
1103  double* ru = rvec.data();
1104  double* vu = vvec.data();
1105  double* re = ru;
1106  double* ve = vu;
1107 
1108  int countu = 0;
1109  int counte = 0;
1110 
1111  for ( int jj = 0; jj < vecknt; jj++ )
1112  {
1113  if ( ru[ jj ] > sRadMv )
1114  { // Save count to position where radius is beyond mouse position
1115  countu = jj;
1116  break;
1117  }
1118  }
1119 
1120  if ( countu == 0 )
1121  return;
1122 
1123  // Detach previous plot curve(s)
1124  equil_plot->detachItems( QwtPlotItem::Rtti_PlotCurve );
1125 
1126  // Set up symbols and curves for unedited and edited arrays
1127  QwtSymbol symu;
1128  symu.setStyle( QwtSymbol::Ellipse );
1129  symu.setPen ( QPen( Qt::blue ) );
1130  symu.setBrush( QBrush( Qt::yellow ) ); // Unedited yellow
1131  symu.setSize ( 10 );
1132  QwtSymbol syme;
1133  syme.setStyle( QwtSymbol::Ellipse );
1134  syme.setPen ( QPen( Qt::white ) );
1135  syme.setBrush( QBrush( Qt::red ) ); // Edited red
1136  syme.setSize ( 10 );
1137 
1138  QwtPlotCurve* curvu = us_curve( equil_plot, "Equil Data" );
1139  curvu->setStyle( QwtPlotCurve::NoCurve );
1140  curvu->setSymbol( symu );
1141  QwtPlotCurve* curve = us_curve( equil_plot, "Edited Data" );
1142  curve->setStyle( QwtPlotCurve::NoCurve );
1143  curve->setSymbol( syme );
1144 
1145  // Set edited/unedited division based on lower/upper half mouse position
1146  if ( mLowerH )
1147  { // Mouse in lower half: edited at beginning; unedited at division
1148  counte = countu;
1149  countu = vecknt - counte;
1150  ru = ru + counte;
1151  vu = vu + counte;
1152  }
1153 
1154  else
1155  { // Mouse in upper half: unedited at beginning; edited at division
1156  counte = vecknt - countu;
1157  re = ru + countu;
1158  ve = vu + countu;
1159  }
1160 
1161  curvu->setData( ru, vu, countu );
1162  curve->setData( re, ve, counte );
1163 
1164  equil_plot->replot();
1165 }
1166 
1167 // Respond to mouse button down
1168 void US_GlobalEquil::pMouseDown( const QwtDoublePoint& p )
1169 {
1170  mMoved = false;
1171  mDown = true;
1172  sRadLo = p.x();
1173  double RadMid = ( cRadLo + cRadHi ) * 0.5;
1174  mLowerH = sRadLo < RadMid;
1175 }
1176 
1177 // Respond to mouse button up (after move)
1178 void US_GlobalEquil::pMouseUp( const QwtDoublePoint& p )
1179 {
1180  // If mouse never moved, ignore release; otherwise reset mouse condition
1181  if ( ! mMoved )
1182  return;
1183 
1184  mMoved = false;
1185  mDown = false;
1186 
1187  // Set new edited range based on where the mouse is positioned
1188  if ( mLowerH )
1189  { // Lower half: range-to-keep is from current position to end
1190  sRadLo = p.x();
1191  sRadHi = cRadHi;
1192  }
1193 
1194  else
1195  { // Upper half: range-to-keep is from beginning to current position
1196  sRadLo = cRadLo;
1197  sRadHi = p.x();
1198  }
1199 
1200  // Save the radius range of the edited scan
1201  scedits[ sscanx ].edited = true;
1202  scedits[ sscanx ].rad_lo = sRadLo;
1203  scedits[ sscanx ].rad_hi = sRadHi;
1204 
1205  // Turn off any zoom that might have been on
1206  eplot->btnZoom->setDown ( false );
1207  eplot->btnZoom->setChecked( false );
1208 
1209  // Re-draw the full edited plot
1210  edata_plot();
1211 
1212  pb_resetsl->setEnabled( true );
1213 }
1214 
1215 // Respond to mouse button being moved - redraw curve with edited points
1216 void US_GlobalEquil::pMouseMoved( const QwtDoublePoint& p )
1217 {
1218  if ( ! mDown )
1219  return;
1220 
1221  mMoved = true; // Flag that we are moving the mouse
1222  sRadMv = p.x(); // Save the current mouse radius position
1223 
1224  if ( sRadMv > cRadHi || sRadMv < cRadLo )
1225  return;
1226 
1227  edited_plot(); // Re-draw plot curves showing edited points
1228 }
1229 
1230 // Create the Scan Fits vector of parameters
1232 {
1233  if ( scanfits.size() > 0 )
1234  return; // Only create base scanfits array one time
1235 
1236  EqScanFit scanfit;
1237  QStringList channs;
1238  int ncomp = max( 1, runfit.nbr_comps );
1239  int mcomp = max( 4, ncomp );
1240  int nintg = mcomp + runfit.nbr_assocs;
1241 
1242  for ( int jes = 0; jes < scedits.size(); jes++ )
1243  {
1244  int jdx = scedits[ jes ].dsindex;
1245  int jrx = scedits[ jes ].speedx;
1246  int jsx = scedits[ jes ].scannbr - 1;
1247  double radlo = scedits[ jes ].rad_lo;
1248  double radhi = scedits[ jes ].rad_hi;
1249  edata = &dataList[ jdx ];
1250  US_DataIO::Scan* dscan = &edata->scanData[ jsx ];
1251  QString trip = triples[ jdx ];
1252  QString chan = trip.section( "/", 1, 1 ).simplified();
1253 
1254  if ( ! channs.contains( chan ) )
1255  channs << chan; // build list of unique channels
1256 
1257  scanfit.scanFit = false;
1258  scanfit.autoExcl = false;
1259  scanfit.limsModd = false;
1260  scanfit.points = edata->pointCount();
1261  scanfit.nbr_posr = 0;
1262  scanfit.nbr_negr = 0;
1263  scanfit.runs = 0;
1264  scanfit.start_ndx = index_radius( edata, radlo );
1265  scanfit.stop_ndx = index_radius( edata, radhi );
1266  scanfit.density = ds_densits[ jdx ];
1267  scanfit.viscosity = ds_viscos [ jdx ];
1268  scanfit.tempera = dscan->temperature;
1269  scanfit.pathlen = 1.2;
1270  scanfit.meniscus = edata->meniscus;
1271  scanfit.baseline = radlo;
1272  scanfit.baseln_ndx = scanfit.start_ndx;
1273  scanfit.baseln_rng = radlo * 0.2;
1274  scanfit.baseln_fit = true;
1275  scanfit.baseln_bnd = true;
1276  scanfit.rpm = (int)edata->speedData[ jrx ].speed;
1277  scanfit.cell = trip.section( "/", 0, 0 ).simplified().toInt();
1278  scanfit.channel = channs.indexOf( chan ) + 1;
1279  scanfit.wavelen = trip.section( "/", 2, 2 ).simplified().toInt();
1280  scanfit.runID = edata->runID;
1281  scanfit.descript = edata->description;
1282 
1283  scanfit.xvs.resize( scanfit.points );
1284  scanfit.yvs.resize( scanfit.points );
1285  scanfit.amp_vals.fill( 0.0, mcomp );
1286  scanfit.amp_ndxs.fill( 0, mcomp );
1287  scanfit.amp_rngs.fill( 0.0, mcomp );
1288  scanfit.amp_fits.fill( true, mcomp );
1289  scanfit.amp_bnds.fill( false, mcomp );
1290  scanfit.extincts.fill( 1.0, nintg );
1291  scanfit.integral.fill( 0.0, nintg );
1292 
1293  for ( int jj = 0; jj < scanfit.points; jj++ )
1294  {
1295  scanfit.xvs[ jj ] = edata->radius( jj );
1296  scanfit.yvs[ jj ] = edata->value( jsx, jj );
1297  }
1298 
1299  scanfit.stop_ndx = index_od_limit( scanfit, od_limit );
1300 
1301  scanfits << scanfit;
1302  }
1303 DbgLv(1) << "AsnSF: points xvs0 xvsN" << scanfit.points
1304  << scanfit.xvs[0] << scanfit.xvs[scanfit.points-1];
1305 }
1306 
1307 // Set up global Run Fit parameters profile
1309 {
1310  runfit.nbr_comps = 1;
1311  runfit.nbr_assocs = 0;
1312  runfit.nbr_runs = 0;
1313  runfit.runs_percent = 0.0;
1314  runfit.runs_expect = 0.0;
1315  runfit.runs_vari = 0.0;
1316  runfit.projname = le_prjname->text();
1317  modelx = max( modelx, 0 );
1318 DbgLv(1) << " sRF: modelx" << modelx;
1320 DbgLv(1) << " sRF: modlname" << runfit.modlname;
1321 
1322  switch ( modelx )
1323  {
1324  case 0:
1325  runfit.nbr_comps = 1;
1326  runfit.nbr_assocs = 0;
1327  break;
1328  case 1:
1329  runfit.nbr_comps = 2;
1330  runfit.nbr_assocs = 0;
1331  break;
1332  case 2:
1333  runfit.nbr_comps = 3;
1334  runfit.nbr_assocs = 0;
1335  break;
1336  case 3:
1337  runfit.nbr_comps = (int)aud_params[ 0 ];
1338  runfit.nbr_assocs = 1;
1339  break;
1340  case 4:
1341  case 5:
1342  case 6:
1343  case 7:
1344  case 8:
1345  case 9:
1346  case 10:
1347  runfit.nbr_comps = 1;
1348  runfit.nbr_assocs = 1;
1349  break;
1350  case 11:
1351  case 12:
1352  case 13:
1353  runfit.nbr_comps = 1;
1354  runfit.nbr_assocs = 2;
1355  break;
1356  case 14:
1357  case 16:
1358  case 17:
1359  case 19:
1360  runfit.nbr_comps = 2;
1361  runfit.nbr_assocs = 1;
1362  break;
1363  case 15:
1364  runfit.nbr_comps = 2;
1365  runfit.nbr_assocs = 2;
1366  case 18:
1367  runfit.nbr_comps = 2;
1368  runfit.nbr_assocs = 0;
1369  default:
1370  break;
1371  }
1372 DbgLv(1) << " sRF: ncomps nassocs" << runfit.nbr_comps << runfit.nbr_assocs;
1373 
1374  double dvval = TYPICAL_VBAR;
1375 
1376  if ( ds_vbar20s.size() > 0 )
1377  dvval = ds_vbar20s[ 0 ];
1378 
1379  double dvrng = dvval * 0.2;
1380  runfit.mw_vals .fill( 0.0, runfit.nbr_comps );
1381  runfit.mw_ndxs .fill( 0, runfit.nbr_comps );
1382  runfit.mw_rngs .fill( 0.0, runfit.nbr_comps );
1383  runfit.mw_fits .fill( true, runfit.nbr_comps );
1384  runfit.mw_bnds .fill( false, runfit.nbr_comps );
1385  runfit.vbar_vals.fill( dvval, runfit.nbr_comps );
1386  runfit.vbar_ndxs.fill( 0, runfit.nbr_comps );
1387  runfit.vbar_rngs.fill( dvrng, runfit.nbr_comps );
1388  runfit.vbar_fits.fill( true, runfit.nbr_comps );
1389  runfit.vbar_bnds.fill( false, runfit.nbr_comps );
1390  runfit.viri_vals.fill( 0.0, runfit.nbr_comps );
1391  runfit.viri_ndxs.fill( 0, runfit.nbr_comps );
1392  runfit.viri_rngs.fill( 0.0, runfit.nbr_comps );
1393  runfit.viri_fits.fill( false, runfit.nbr_comps );
1394  runfit.viri_bnds.fill( false, runfit.nbr_comps );
1395 
1396  for ( int ii = 0; ii < 4; ii++ )
1397  {
1398  runfit.eq_vals[ ii ] = 0.0;
1399  runfit.eq_ndxs[ ii ] = 0;
1400  runfit.eq_rngs[ ii ] = 0.0;
1401  runfit.eq_fits[ ii ] = false;
1402  runfit.eq_bnds[ ii ] = false;
1403  runfit.stoichs[ ii ] = 1.0;
1404  }
1405 }
1406 
1407 // Determine the index in the radius vector of a given radius
1409 {
1410  int l_index = edat->pointCount() - 1;
1411  int r_index = -1;
1412 
1413  while ( ++r_index < l_index )
1414  {
1415  if ( radius <= edat->radius( r_index ) )
1416  break;
1417  }
1418 
1419  return r_index;
1420 }
1421 
1422 // React to change in OD limit
1423 void US_GlobalEquil::od_limit_changed( const QString& newlim )
1424 {
1425  update_limit( newlim.toDouble() );
1426 }
1427 
1428 // Change stop index based on updated OD limit
1429 void US_GlobalEquil::update_limit( double odlim )
1430 {
1431  od_limit = odlim;
1432 
1433  if ( scanfits.size() > 0 && od_limit != 0.0 )
1434  {
1435  for ( int ii = 0; ii < scanfits.size(); ii++ )
1436  {
1437  EqScanFit* sfit = &scanfits[ ii ];
1438  int jdx = scedits[ ii ].dsindex;
1439  double radhi = scedits[ ii ].rad_hi;
1440  sfit->stop_ndx = index_radius( &dataList[ jdx ], radhi );
1441  sfit->stop_ndx = index_od_limit( scanfits[ ii], odlim );
1442  }
1443  }
1444 
1445  else
1446  {
1447  for ( int ii = 0; ii < scanfits.size(); ii++ )
1448  {
1449  int jdx = scedits[ ii ].dsindex;
1450  double radhi = scedits[ ii ].rad_hi;
1451  scanfits[ ii ].stop_ndx = index_radius( &dataList[ jdx ], radhi );
1452  }
1453  }
1454 }
1455 
1456 // Find the index in a scan of the od_limit point
1457 int US_GlobalEquil::index_od_limit( EqScanFit& scanfit, double odlim )
1458 {
1459  int stopx = scanfit.stop_ndx;
1460 
1461  for ( int jj = scanfit.start_ndx; jj < scanfit.stop_ndx + 1; jj++ )
1462  {
1463  if ( scanfit.yvs[ jj ] > odlim )
1464  {
1465  stopx = max( jj - 1, scanfit.start_ndx );
1466  break;
1467  }
1468  }
1469 
1470  return stopx;
1471 }
1472 
1473 // Set fit parameters to floated state
1475 {
1476  for ( int jj = 0; jj < runfit.nbr_comps; jj++ )
1477  runfit.mw_fits[ jj ] = true;
1478 
1479  for ( int jj = 0; jj < runfit.nbr_assocs; jj++ )
1480  runfit.eq_fits[ jj ] = true;
1481 
1482  for ( int ii = 0; ii < scanfits.size(); ii++ )
1483  {
1484  scanfits[ ii ].baseln_fit = true;
1485 
1486  for ( int jj = 0; jj < runfit.nbr_comps; jj++ )
1487  scanfits[ ii ].amp_fits[ jj ] = true;
1488  }
1489 }
1490 // Set fit parameters to fixed (locked) state
1492 {
1493  for ( int jj = 0; jj < runfit.nbr_comps; jj++ )
1494  runfit.mw_fits[ jj ] = false;
1495 
1496  for ( int jj = 0; jj < runfit.nbr_assocs; jj++ )
1497  runfit.eq_fits[ jj ] = false;
1498 
1499  for ( int ii = 0; ii < scanfits.size(); ii++ )
1500  {
1501  scanfits[ ii ].baseln_fit = false;
1502 
1503  for ( int jj = 0; jj < runfit.nbr_comps; jj++ )
1504  scanfits[ ii ].amp_fits[ jj ] = false;
1505  }
1506 }
1507