UltraScan III
us_astfem_sim.cpp
Go to the documentation of this file.
1 
3 #include <QApplication>
4 
5 #include "us_license.h"
6 #include "us_license_t.h"
7 #include "us_settings.h"
8 #include "us_gui_settings.h"
9 #include "us_astfem_sim.h"
11 #include "us_math2.h"
12 #include "us_defines.h"
13 #include "us_clipdata.h"
14 #include "us_gui_util.h"
15 #include "us_model_gui.h"
16 #include "us_buffer_gui.h"
17 #include "us_util.h"
18 #include "us_lamm_astfvm.h"
19 
23 int main( int argc, char* argv[] )
24 {
25  QApplication application( argc, argv );
26 
27  #include "main1.inc"
28 
29  // License is OK. Start up.
30  US_Astfem_Sim w;
31  w.show();
32  return application.exec();
33 }
34 
35 US_Astfem_Sim::US_Astfem_Sim( QWidget* p, Qt::WindowFlags f )
36  : US_Widgets( true, p, f )
37 {
39 
40  setWindowTitle( "UltraScan3 Simulation Module" );
41  setPalette( US_GuiSettings::frameColor() );
43 
44  stopFlag = false;
45  movieFlag = false;
46  save_movie = false;
47  time_correctionFlag = false;
48  imagedir = US_Settings::tmpDir() + "/movies";
49 
50  astfem_rsa = NULL;
51  astfvm = NULL;
52 
53  clean_etc_dir();
54 
55  QGridLayout* main = new QGridLayout( this );
56  main->setSpacing( 2 );
57  main->setContentsMargins ( 2, 2, 2, 2 );
58  main->setColumnStretch( 1, 1 );
59 
60  QBoxLayout* buttonbox = new QVBoxLayout;
61 
62  QPushButton* pb_new = us_pushbutton( tr( "Model Control") );
63  connect( pb_new, SIGNAL( clicked() ), SLOT( new_model() ) );
64  buttonbox->addWidget( pb_new );
65 
66  pb_buffer = us_pushbutton( tr( "Define Buffer"), false );
67  connect( pb_buffer, SIGNAL( clicked() ), SLOT( new_buffer() ) );
68  buttonbox->addWidget( pb_buffer );
69 
70  pb_simParms = us_pushbutton( tr( "Simulation Parameters"), false );
71  connect ( pb_simParms, SIGNAL( clicked() ), SLOT( sim_parameters() ) );
72  buttonbox->addWidget( pb_simParms );
73 
74  QGridLayout* movie = us_checkbox( "Show Movie", ck_movie, movieFlag );
75  buttonbox->addLayout( movie );
76 
77  QGridLayout* lo_svmovie = us_checkbox( "Save Movie", ck_savemovie, false );
78  connect( ck_savemovie, SIGNAL( toggled ( bool ) ),
79  SLOT( update_save_movie( bool ) ) );
80  buttonbox->addLayout( lo_svmovie );
81 
82  QGridLayout* timeCorr = us_checkbox( "Use Time Correction", ck_timeCorr,
84 
85  connect( ck_timeCorr, SIGNAL( clicked() ), SLOT( update_time_corr() ) );
86  buttonbox->addLayout( timeCorr );
87 
88  pb_start = us_pushbutton( tr( "Start Simulation" ), false );
89  connect( pb_start, SIGNAL( clicked() ), SLOT( start_simulation() ) );
90  buttonbox->addWidget( pb_start );
91 
92  pb_stop = us_pushbutton( tr( "Stop Simulation" ), false );
93  connect( pb_stop, SIGNAL( clicked() ), SLOT( stop_simulation() ) );
94  buttonbox->addWidget( pb_stop );
95 
96  pb_saveSim = us_pushbutton( tr( "Save Simulation" ), false );
97  connect( pb_saveSim, SIGNAL( clicked() ), SLOT( save_scans() ) );
98  buttonbox->addWidget( pb_saveSim );
99 
100  QPushButton* pb_help = us_pushbutton( tr( "Help" ) );
101  connect( pb_help, SIGNAL( clicked() ), SLOT( help()) );
102  buttonbox->addWidget( pb_help );
103 
104  QPushButton* pb_close = us_pushbutton( tr( "Close" ) );
105  buttonbox->addWidget( pb_close );
106  connect( pb_close, SIGNAL( clicked() ), SLOT( close()) );
107 
108  QPalette pa( pb_close->palette() );
110  te_status->setPalette( pa );
111  te_status->setTextBackgroundColor( pa.color( QPalette::Window ) );
112  te_status->setTextColor( pa.color( QPalette::WindowText ) );
113  QFontMetrics fm( te_status->font() );
114  te_status->setMaximumHeight( fm.lineSpacing() * 17 / 2 );
115 
116  change_status();
117 
118  buttonbox->addWidget( te_status );
119  buttonbox->addStretch();
120 
121  main->addLayout( buttonbox, 0, 0 );
122 
123  // Right Column
124  QBoxLayout* plot = new QVBoxLayout;
125 
126  // Simulation Plot
127  plot1 = new US_Plot( moviePlot, tr( "Simulation Window" ),
128  tr( "Radius (cm)" ), tr( "Concentration" ) );
129  us_grid ( moviePlot );
130  moviePlot->setMinimumSize( 600, 275);
131  moviePlot->setAxisScale( QwtPlot::yLeft, 0.0, 2.0 );
132  moviePlot->setAxisScale( QwtPlot::xBottom, 5.8, 7.2 );
133 
134  plot->addLayout( plot1 );
135 
136  QBoxLayout* timeSpeed = new QHBoxLayout;
137 
138  QLabel* lb_time = us_label( tr( "Time( in seconds):" ) );
139  lb_time->setAlignment ( Qt::AlignCenter );
140  timeSpeed->addWidget( lb_time );
141 
142  lcd_time = us_lcd( 5, 0 );
143  timeSpeed->addWidget( lcd_time );
144 
145  QLabel* lb_speed = us_label( tr( "Current Speed:" ) );
146  lb_speed->setAlignment ( Qt::AlignCenter );
147  timeSpeed->addWidget( lb_speed );
148 
149  lcd_speed = us_lcd( 5, 0 );
150  timeSpeed->addWidget( lcd_speed );
151 
152  plot->addLayout( timeSpeed );
153 
154  // Saved Scans
155  plot2 = new US_Plot( scanPlot, tr( "Saved Scans" ),
156  tr( "Radius (cm)" ), tr( "Concentration" ) );
157  QwtPlotGrid* grid2 = us_grid ( scanPlot );
158  grid2->enableX( true );
159  grid2->enableY( true );
160  scanPlot->setMinimumSize( 600, 275);
161  scanPlot->setAxisScale( QwtPlot::yLeft, 0.0, 2.0 );
162  scanPlot->setAxisScale( QwtPlot::xBottom, 5.8, 7.2 );
163  plot->addLayout( plot2 );
164 
165  QBoxLayout* completion = new QHBoxLayout;
166 
167  lb_component = us_label( tr( "Component:" ) );
168  lb_component->setAlignment ( Qt::AlignCenter );
169  completion->addWidget( lb_component );
170 
171  lcd_component = us_lcd( 7, 0 );
172  completion->addWidget( lcd_component );
173 
174  lb_progress = us_label( tr( "% Completed:" ) );
175  lb_progress->setAlignment ( Qt::AlignCenter );
176  completion->addWidget( lb_progress );
177 
178  progress = us_progressBar( 0, 100, 0 );
179  completion->addWidget( progress );
180 
181  plot->addLayout( completion );
182 
183  main->addLayout( plot, 0, 1 );
184 }
185 
187 {
189  QString rotor_calibr = "0";
190  double rpm = 45000.0;
191 
192  // set up bottom start and rotor coefficients from hardware file
193  simparams.setHardware( NULL, rotor_calibr, 0, 0 );
194 
195  // calculate bottom from rpm, channel bottom pos., rotor coefficients
196  double bottom = US_AstfemMath::calc_bottom( rpm,
198  bottom = (double)( qRound( bottom * 1000.0 ) ) * 0.001;
199 qDebug() << "ASim:InSP: rotor_calibr" << rotor_calibr;
200 qDebug() << "ASim:InSP: rpm" << rpm;
201 qDebug() << "ASim:InSP: bottom_position" << simparams.bottom_position;
202 qDebug() << "ASim:InSP: rotorcoeffs" << simparams.rotorcoeffs[0] << simparams.rotorcoeffs[1];
203 qDebug() << "ASim:InSP: bottom" << bottom << simparams.bottom;
204 
205  simparams.mesh_radius.clear();
206  simparams.speed_step .clear();
207 
208  sp.duration_hours = 5;
209  sp.duration_minutes = 30.0;
210  sp.delay_hours = 0;
211  sp.delay_minutes = 20.0;
212  sp.rotorspeed = (int)rpm;
213  sp.scans = 30;
214  sp.acceleration = 400;
215  sp.acceleration_flag = true;
216 
217  simparams.speed_step << sp;
218 
219  simparams.simpoints = 200;
223  simparams.meniscus = 5.8;
224  simparams.bottom = bottom;
225  simparams.rnoise = 0.0;
226  simparams.tinoise = 0.0;
227  simparams.rinoise = 0.0;
228  simparams.band_volume = 0.015;
229  simparams.rotorCalID = rotor_calibr;
230  simparams.band_forming = false;
231 }
232 
234 {
235  US_ModelGui* dialog = new US_ModelGui( system );
236  connect( dialog, SIGNAL( valueChanged( US_Model ) ),
237  SLOT ( change_model( US_Model ) ) );
238  dialog->exec();
239 }
240 
242 {
243  system = m;
244  pb_buffer ->setEnabled( true );
245  pb_simParms->setEnabled( true );
246 
247  // set default of FVM if model is non-ideal
248  if ( system.components[ 0 ].sigma != 0.0 ||
249  system.components[ 0 ].delta != 0.0 ||
250  system.coSedSolute >= 0 ||
251  buffer.compressibility > 0.0 )
253 
254  else // normal (ideal) default
256 
257  change_status();
258 }
259 
261 {
262  US_BufferGui* dialog = new US_BufferGui( true, buffer );
263 
264  connect( dialog, SIGNAL( valueChanged( US_Buffer ) ),
265  SLOT ( change_buffer( US_Buffer ) ) );
266 
267  dialog->exec();
268  qApp->processEvents();
269 }
270 
272 {
273  buffer = b;
274 
275  if ( buffer.compressibility > 0.0 )
277 
278  change_status();
279 }
280 
282 {
283  QStringList mtyps;
284  mtyps << "ASTFEM" << "CLAVERIE" << "MOVING_HAT" << "USER" << "ASTFVM";
285  QString simtype = mtyps[ (int)simparams.meshType ];
286 
287  int dhrs = simparams.speed_step[ 0 ].duration_hours;
288  double dmns = simparams.speed_step[ 0 ].duration_minutes;
289  int scns = simparams.speed_step[ 0 ].scans;
290 
291  for ( int ii = 1; ii < simparams.speed_step.size(); ii++ )
292  {
293  dhrs += simparams.speed_step[ ii ].duration_hours;
294  dmns += simparams.speed_step[ ii ].duration_minutes;
295  scns += simparams.speed_step[ ii ].scans;
296  }
297 
298  if ( dmns > 59 )
299  {
300  int khrs = dmns / 60;
301  dmns -= ( khrs * 60.0 );
302  dhrs += khrs;
303  }
304 
305  te_status->setText( tr(
306  "Model:\n %1\n"
307  "Buffer (density/viscosity/compress.):\n %2 / %3 / %4\n"
308  "SimParams (type/duration/scans):\n %5 / %6 h %7 m / %8" )
309  .arg( system.description ).arg( buffer.density ).arg( buffer.viscosity )
310  .arg( buffer.compressibility ).arg( simtype )
311  .arg( simparams.speed_step[ 0 ].duration_hours )
312  .arg( simparams.speed_step[ 0 ].duration_minutes )
313  .arg( simparams.speed_step[ 0 ].scans ) );
314 }
315 
317 {
319 
320  US_SimulationParametersGui* dialog =
322 
323  connect( dialog, SIGNAL( complete() ), SLOT( set_parameters() ) );
324 
325  dialog->exec();
326 }
327 
329 {
331 
332  pb_start ->setEnabled( true );
333 
334  change_status();
335 }
336 
338 {
339  stopFlag = true;
340 
341  if ( astfem_rsa )
343 
344  if ( astfvm )
346 }
347 
349 {
350 
351  moviePlot->clear();
352  moviePlot->replot();
353  curve_count = 0;
354  image_count = 0;
355  double rpm = simparams.speed_step[ 0 ].rotorspeed;
356 
357  scanPlot->clear();
358  scanPlot->setAxisAutoScale( QwtPlot::xBottom );
359  scanPlot->replot();
360 
361  pb_stop ->setEnabled( true );
362  pb_start ->setEnabled( false );
363  pb_saveSim->setEnabled( false );
364 
365  // The astfem/astfvm simulation routines expects a dataset structure that
366  // is initialized with a time and radius grid, and all concentration points
367  // need to be set to zero. Each speed is a separate mfem_data set.
368  sim_data.xvalues .clear();
369  sim_data.scanData.clear();
370 
371  sim_data.type[0] = 'R';
372  sim_data.type[1] = 'A';
373 
374  QString guid = US_Util::new_guid();
375  US_Util::uuid_parse( guid, (uchar*)sim_data.rawGUID );
376 
377  sim_data.cell = 1;
378  sim_data.channel = 'S';
379  sim_data.description = "Simulation";
380 
381  int points = qRound( ( simparams.bottom - simparams.meniscus ) /
383 
384  sim_data.xvalues.resize( points );
385 
386  for ( int i = 0; i < points; i++ )
389 
390  int total_scans = 0;
391 
392  for ( int i = 0; i < simparams.speed_step.size(); i++ )
393  total_scans += simparams.speed_step[ i ].scans;
394 
395  sim_data.scanData.resize( total_scans );
396  int terpsize = ( points + 7 ) / 8;
397 
398  for ( int i = 0; i < total_scans; i++ )
399  {
400  US_DataIO::Scan* scan = &sim_data.scanData[ i ];
401 
403  scan->rpm = rpm;
404  scan->omega2t = 0.0;
405  scan->wavelength = system.wavelength;
406  scan->plateau = 0.0;
408 
409  scan->rvalues .fill( 0.0, points );
410  scan->interpolated.fill( 0, terpsize );
411  }
412 
413  // Set the time for the scans
414  double w2t_sum = 0.0;
415  double delay = simparams.speed_step[ 0 ].delay_hours * 3600.0
416  + simparams.speed_step[ 0 ].delay_minutes * 60.0;
417  double current_time = 0.0;
418  double duration;
419  double increment = 0.0;
420  int scan_number = 0;
421 
422  for ( int ii = 0; ii < simparams.speed_step.size(); ii++ )
423  {
424  // To get the right time, we need to take the last scan time from the
425  // previous speed step and add the delays to it
426 
427  // First time point of this speed step in secs
429  double w2t = sq( sp->rotorspeed * M_PI / 30.0 );
430 
431  delay = sp->delay_hours * 3600. + sp->delay_minutes * 60.;
432  duration = sp->duration_hours * 3600. + sp->duration_minutes * 60.;
433  increment = ( duration - delay ) / (double)( sp->scans - 1 );
434  double w2t_inc = increment * w2t;
435  current_time += delay;
436  w2t_sum = ( ii == 0 ) ? ( current_time * w2t ) : w2t_sum;
437 DbgLv(2) << "SIM curtime dur incr" << current_time << duration << increment
438  << "w2t w2tsum" << w2t << w2t_sum;
439 
440  for ( int jj = 0; jj < sp->scans; jj++ )
441  {
442  US_DataIO::Scan* scan = &sim_data.scanData[ scan_number ];
443  scan->seconds = (double)qRound( current_time );
444  scan->omega2t = w2t_sum;
445  w2t_sum += w2t_inc;
446  current_time += increment;
447 
448  scan_number++;
449 DbgLv(2) << "SIM scan time omega2t" << scan_number << scan->seconds
450  << scan->omega2t;
451  }
452 
453  int j1 = scan_number - sp->scans;
454  int j2 = scan_number - 1;
455  sp->w2t_first = sim_data.scanData[ j1 ].omega2t;
456  sp->w2t_last = sim_data.scanData[ j2 ].omega2t;
457  sp->time_first = sim_data.scanData[ j1 ].seconds;
458  sp->time_last = sim_data.scanData[ j2 ].seconds;
459  }
460 
461  lb_progress->setText( tr( "% Completed:" ) );
462  progress->setMaximum( system.components.size() );
463  progress->reset();
464  lcd_component->display( 0 );
465 
466  stopFlag = false;
467 
468  simparams.mesh_radius.clear();
470 
471  // Run the simulation
472 
474  { // the normal case: ASTFEM (finite element)
475 
476  if ( system.associations.size() > 0 )
477  lb_component->setText( tr( "RA Step:" ) );
478  else
479  lb_component->setText( tr( "Component:" ) );
480 
482 
483  connect( astfem_rsa, SIGNAL( new_scan( QVector< double >*, double* ) ),
484  SLOT( update_movie_plot( QVector< double >*, double* ) ) );
485  connect( astfem_rsa, SIGNAL( current_component( int ) ),
486  SLOT ( update_progress ( int ) ) );
487  connect( astfem_rsa, SIGNAL( new_time ( double ) ),
488  SLOT ( update_time( double ) ) );
489  connect( astfem_rsa, SIGNAL( current_speed( int ) ),
490  SLOT ( update_speed ( int ) ) );
491  connect( astfem_rsa, SIGNAL( calc_progress( int ) ),
492  SLOT ( show_progress( int ) ) );
493  connect( astfem_rsa, SIGNAL( calc_done( void ) ),
494  SLOT ( calc_over( void ) ) );
495 
496  astfem_rsa->set_movie_flag( ck_movie->isChecked() );
497 
498  astfem_rsa->setTimeInterpolation( false );
502 
505 
506  if ( dbg_level > 0 )
507  {
508  double cmin=99999.9;
509  double cmax=-999999.9;
510  int ilo=0;
511  int ihi=0;
512  int jlo=0;
513  int jhi=0;
514  int nscn=sim_data.scanCount();
515  int ncvl=sim_data.pointCount();
516 
517  for ( int ii=0; ii<nscn; ii++ )
518  {
519  double t0=sim_data.scanData[ii].seconds;
520  double t1=(ii==0)?sim_data.scanData[1].seconds
521  :sim_data.scanData[ii-1].seconds;
522  if ( ii==0 || (ii+1)==nscn || (ii*2)==nscn )
523  DbgLv(2) << " Scan" << ii << " Time" << t0 << t1;
524  for ( int jj=0; jj<ncvl; jj++ )
525  {
526  double cval = sim_data.value(ii,jj);
527  if ( cval < cmin ) { ilo=ii; jlo=jj; cmin=cval; }
528  if ( cval > cmax ) { ihi=ii; jhi=jj; cmax=cval; }
529  if ( ii==0 || (ii+1)==nscn || (ii*2)==nscn )
530  {
531  if ( jj<10 || (jj+11)>ncvl ||
532  ((jj*2)>(ncvl-10)&&(jj*2)<(ncvl+11)) )
533  DbgLv(2) << " C index value" << jj << cval;
534  }
535  }
536  }
537  DbgLv(1) << "SIM data min conc ilo jlo" << cmin << ilo << jlo;
538  DbgLv(1) << "SIM data max conc ihi jhi" << cmax << ihi << jhi;
539  DbgLv(1) << "SIM:fem: m b s D rpm" << simparams.meniscus << simparams.bottom
540  << system.components[0].s << system.components[0].D
541  << simparams.speed_step[0].rotorspeed;
542  }
543  }
544 
545  else
546  { // special case: ASTFVM (finite volume)
547 
548  // create ASTFVM solver
549  astfvm = new US_LammAstfvm( system, simparams, this );
550 
551  // set up to report progress
552  connect( astfvm, SIGNAL( calc_start( int ) ),
553  SLOT ( start_calc( int ) ) );
554 
555  connect( astfvm, SIGNAL( calc_progress( int ) ),
556  SLOT ( show_progress( int ) ) );
557 
558  connect( astfvm, SIGNAL( calc_done( void ) ),
559  SLOT ( calc_over( void ) ) );
560 
561  connect( astfvm, SIGNAL( new_scan( QVector< double >*, double* ) ),
562  SLOT( update_movie_plot( QVector< double >*, double* ) ) );
563  connect( astfvm, SIGNAL( new_time ( double ) ),
564  SLOT ( update_time( double ) ) );
565 
566  // initialize LCD with component "1"
567  lcd_component->setMode( QLCDNumber::Dec );
568  lcd_component->display( 1 );
569  lcd_speed ->display( (int)rpm );
570 
572  astfvm->setMovieFlag( ck_movie->isChecked() );
573 
574  // solve using ASTFVM
575  int rc = astfvm->calculate( sim_data );
576 
577  if ( rc != 0 )
578  { // report on multiple non-ideal conditions
579  QMessageBox::information( this,
580  tr( "Non-Ideal Simulation Error" ),
581  tr( "Unable to create simulation.\n"
582  "Multiple non-ideal conditions exist.\n"
583  "Edit the ambiguous model." ) );
584  return;
585  }
586 
587  // on completion, set LCD display to components count
588  lcd_component->display( system.components.size() );
589  }
590 
591  if ( dbg_level > 0 )
592  {
593  DbgLv(1) << "SIM data simp.temperature" << simparams.temperature;
594  double cmin=99999.9;
595  double cmax=-999999.9;
596  int ilo=0;
597  int ihi=0;
598  int jlo=0;
599  int jhi=0;
600  int nscn=sim_data.scanCount();
601  int npts=sim_data.pointCount();
602  for ( int ii=0; ii<nscn; ii++ )
603  {
604  double t0=sim_data.scanData[ii].seconds;
605  double t1=(ii==0)?sim_data.scanData[1].seconds
606  :sim_data.scanData[ii-1].seconds;
607  if ( ii==0 || (ii+1)==nscn || (ii*2)==nscn )
608  DbgLv(2) << " Scan" << ii << " Time" << t0 << t1
609  << "temp" << sim_data.scanData[ii].temperature;
610  for ( int jj=0; jj<npts; jj++ )
611  {
612  double cval = sim_data.value(ii,jj);
613  if ( cval < cmin ) { ilo=ii; jlo=jj; cmin=cval; }
614  if ( cval > cmax ) { ihi=ii; jhi=jj; cmax=cval; }
615  if ( ii==0 || (ii+1)==nscn || (ii*2)==nscn )
616  {
617  if ( jj<10 || (jj+11)>npts ||
618  ((jj*2)>(npts-10)&&(jj*2)<(npts+11)) )
619  DbgLv(2) << " C index value" << jj << cval;
620  }
621  }
622  }
623  DbgLv(1) << "SIM data min conc ilo jlo" << cmin << ilo << jlo;
624  DbgLv(1) << "SIM data max conc ihi jhi" << cmax << ihi << jhi;
625  DbgLv(1) << "SIM:fem: m b s D rpm" << simparams.meniscus
626  << simparams.bottom << system.components[0].s
627  << system.components[0].D << simparams.speed_step[0].rotorspeed;
628  }
629 
630  finish();
631 }
632 
634 {
635  total_conc = 0.0;
636 
637  for ( int ii = 0; ii < system.components.size(); ii++ )
638  {
639  if ( ii != system.coSedSolute )
640  total_conc += system.components[ ii ].signal_concentration;
641  }
642 
643 //DbgLv(1) << "FIN: comp size" << system.components.size();
644 //DbgLv(1) << "FIN: total_conc" << total_conc;
645  ri_noise();
646  random_noise();
647  ti_noise();
648 
649  // If we didn't interrupt, we need to set to 100 % complete at end of run
650  if ( ! stopFlag )
651  {
652  update_progress( progress->maximum() );
653 //DbgLv(1) << "FIN: progress maxsize" << progress->maximum();
654  }
655 
656  stopFlag = false;
657 
658  plot();
659 
660  pb_stop ->setEnabled( false );
661  pb_start ->setEnabled( true );
662  pb_saveSim->setEnabled( true );
663 
664  if ( astfem_rsa )
665  {
666  delete astfem_rsa;
667  astfem_rsa = NULL;
668  }
669 
670  if ( astfvm )
671  {
672  delete astfvm;
673  astfvm = NULL;
674  }
675 }
676 
678 {
679  if ( simparams.rinoise == 0.0 ) return;
680 
681  // Add radially invariant noise
682  for ( int j = 0; j < sim_data.scanData.size(); j++ )
683  {
684  double rinoise =
686 
687  for ( int k = 0; k < sim_data.pointCount(); k++ )
688  sim_data.scanData[ j ].rvalues[ k ] += rinoise;
689  }
690 }
691 
693 {
694  if ( simparams.rnoise == 0.0 ) return;
695  // Add random noise
696 
697  for ( int j = 0; j < sim_data.scanData.size(); j++ )
698  {
699  for ( int k = 0; k < sim_data.pointCount(); k++ )
700  {
701  sim_data.scanData[ j ].rvalues[ k ]
703  }
704  }
705 }
706 
708 {
709  if ( simparams.tinoise == 0.0 ) return;
710 
711  // Add time invariant noise
712  int points = sim_data.pointCount();
713  QVector< double > tinoise;
714  tinoise.resize( points );
715 
716  double val = US_Math2::box_muller( 0, total_conc * simparams.tinoise / 100 );
717 
718  for ( int k = 0; k < points; k++ )
719  {
720  val += US_Math2::box_muller( 0, total_conc * simparams.tinoise / 100 );
721  tinoise[ k ] = val;
722  }
723 
724  for ( int j = 0; j < sim_data.scanData.size(); j++ )
725  {
726  for ( int k = 0; k < points; k++ )
727  sim_data.scanData[ j ].rvalues[ k ] += tinoise[ k ];
728  }
729 }
730 
732 {
733  scanPlot->detachItems();
734 
735  // Set plot scale
736  if ( simparams.band_forming )
737  scanPlot->setAxisScale( QwtPlot::yLeft, 0, total_conc );
738 
739  else if ( system.coSedSolute >= 0 )
740  {
741  scanPlot->setAxisAutoScale( QwtPlot::xBottom );
742  scanPlot->setAxisAutoScale( QwtPlot::yLeft );
743  }
744 
745  else
746  {
747  scanPlot->setAxisScale( QwtPlot::yLeft, 0, total_conc * 2.0 );
748  }
749 
750  QwtPlotGrid* grid2 = us_grid( scanPlot );
751  grid2->enableX( true );
752  grid2->enableY( true );
753 
754  // Plot the simulation
755  if ( ! stopFlag )
756  {
757  int scan_count = sim_data.scanCount();
758  int points = sim_data.pointCount();
759  int* curve = new int[ scan_count ];
760 
761  double* x;
762  double** y;
763 
764  x = new double [ points ];
765  y = new double* [ scan_count ];
766 
767  for ( int j = 0; j < points; j++ )
768  x[ j ] = sim_data.xvalues[ j ];
769 
770  for ( int j = 0; j < scan_count; j++ )
771  y[ j ] = new double [ points ];
772 
773  for ( int j = 0; j < scan_count; j++ )
774  {
775  for ( int k = 0; k < points; k++ )
776  y[ j ][ k ] = sim_data.value( j, k );
777  }
778 
779  for ( int j = 0; j < scan_count; j++ )
780  {
781  QString title = "Concentration" + QString::number( j );
782  QwtPlotCurve* plotCurve = new QwtPlotCurve( title );
783 
784  plotCurve->setData( x, y[ j ], points );
785  plotCurve->setPen( QPen( Qt::yellow ) );
786  plotCurve->attach( scanPlot );
787  }
788 
789  delete [] x;
790 
791  for ( int j = 0; j < scan_count; j++ ) delete [] y[ j ];
792  delete [] y;
793 
794  delete [] curve;
795  scanPlot->replot();
796  }
797 }
798 
800 {
801  QString fn = QFileDialog::getExistingDirectory( this,
802  tr( "Select a directory for the simulated data:" ),
804 
805  // The user gave a directory name, save in openAUC format
806 
807  if ( ! fn.isEmpty() )
808  {
809  fn = fn.replace( "\\", "/" );
810  save_xla( fn );
811  }
812 }
813 
814 void US_Astfem_Sim::save_xla( const QString& dirname )
815 {
816  double brad = simparams.bottom;
817  double mrad = simparams.meniscus;
818  double grid_res = simparams.radial_resolution;
819 
820  // Add 30 points in front of meniscus
821  int points = (int)( ( brad - mrad ) / grid_res ) + 31;
822 
823  double maxc = 0.0;
824  int total_scans = sim_data.scanCount();
825  int old_points = sim_data.pointCount();
826 
827  for ( int ii = 0; ii < total_scans; ii++ )
828  { // Accumulate the maximum computed OD value
829  for ( int kk = 0; kk < old_points; kk++ )
830  maxc = qMax( maxc, sim_data.value( ii, kk ) );
831  }
832 
833  // Compute a data threshold that is scan 1's plateau reading times 3
834  QVector< double > xtmpVec( total_scans );
835  QVector< double > ytmpVec( total_scans );
836  double *xtmp = xtmpVec.data();
837  double *ytmp = ytmpVec.data();
838  double intercept;
839  double slope;
840  double sigma;
841  double correl;
842 
843  for ( int ii = 0; ii < total_scans; ii++ )
844  { // Build time,omega2t points
845  xtmp[ ii ] = sim_data.scanData[ ii ].seconds;
846  ytmp[ ii ] = sim_data.scanData[ ii ].omega2t;
847  }
848 
849  // Fit to time,omega2t and use fit to compute the time correction
850  US_Math2::linefit( &xtmp, &ytmp, &slope, &intercept, &sigma,
851  &correl, total_scans );
852 
853  double timecorr = -intercept / slope;
854  double s20wcorr = -2.0;
855  double omega = sim_data.scanData[ 0 ].rpm * M_PI / 30.0;
856  double oterm = ( sim_data.scanData[ 0 ].seconds - timecorr )
857  * omega * omega * s20wcorr;
858  double s1plat = 0.0;
859 
860  for ( int jj = 0; jj < system.components.count(); jj++ )
861  {
863  double conc = sc->signal_concentration;
864  double sval = sc->s;
865  s1plat += ( conc * exp( oterm * sval ) );
866  }
867 
868  double dthresh = maxc;
869 DbgLv(1) << "Sim:SV: maxc" << maxc << "s1plat" << s1plat
870  << "dthresh" << dthresh << "total_conc" << total_conc;
871  double maxrad = brad;
872  s1plat = qMin( s1plat, ( dthresh * 0.5 ) );
873 DbgLv(1) << "Sim:SV: reset s1plat" << s1plat;
874 
875  US_ClipData* cd = new US_ClipData( dthresh, maxrad, mrad, total_conc );
876 
877  if ( ! cd->exec() )
878  {
879  maxrad = brad;
880  dthresh = maxc;
881  }
882 
883  // If the overall maximum reading exceeds the threshold,
884  // limit OD values in all scans to the threshold
885  if ( maxc > dthresh )
886  {
887  int nchange = 0; // Total threshold changes
888  int nmodscn = 0; // Modified scans
889 
890 
891  for ( int ii = 0; ii < total_scans; ii++ )
892  { // Examine each scan
893  int kchange = 0; // Changes in a scan
894 
895  for ( int jj = 0; jj < old_points; jj++ )
896  { // Examine each reading point in a scan
897 
898  if ( sim_data.value( ii, jj ) > dthresh )
899  { // Set a high value to the threshold and bump counts
900  sim_data.setValue( ii, jj, dthresh );
901  nchange++; // Bump the count of total threshold changes
902  kchange++; // Bump the count of threshold changes in this scan
903  }
904  }
905 
906  // Bump the count of scans in which a threshold limit was applied
907  if ( kchange > 0 )
908  nmodscn++;
909  }
910 DbgLv(1) << "Sim:SV: OD-Limit nchange nmodscn" << nchange << nmodscn
911  << "maxc dthresh" << maxc << dthresh;
912 
913  // Report that some readings were threshold-limited
914  QMessageBox::information( this,
915  tr( "OD Values Threshold Limited" ),
916  tr( "%1 readings in %2 scans were reset\n"
917  "to a threshold value of %3 .\n"
918  "The pre-threshold-limit maximum OD\n"
919  "value was %4 ." )
920  .arg( nchange ).arg( nmodscn ).arg( dthresh ).arg( maxc ) );
921  }
922 
923 
924  points = (int)( ( maxrad - mrad ) / grid_res ) + 31;
925 
926  progress->setMaximum( total_scans );
927  progress->reset();
928 
929  QVector< double > tconc_v( points );
930  double* temp_conc = tconc_v.data();
931  double rad = mrad - 30.0 * grid_res;
932  sim_data.xvalues.resize( points );
933  lb_progress->setText( "Writing..." );
934 
935  for ( int jj = 0; jj < points; jj++ )
936  {
937  sim_data.xvalues[ jj ] = rad;
938  rad += grid_res;
939  }
940 
941  for ( int ii = 0; ii < total_scans; ii++ )
942  {
943  US_DataIO::Scan* scan = &sim_data.scanData[ ii ];
944 
945  for ( int jj = 30; jj < points; jj++ )
946  { // Position the computed concentration values after the first 30
947  temp_conc[ jj ] = scan->rvalues[ jj - 30 ];
948  }
949 
950  for ( int jj = 0; jj < 30; jj++ )
951  { // Zero the first 30 points
952  temp_conc[ jj ] = 0.0;
953  }
954 
955  temp_conc[ 30 ] = s1plat * 2.0; // Put a spike at the meniscus
956 
957  scan->rvalues.resize( points );
958 
959  for ( int jj = 0; jj < points; jj++ )
960  { // Store the values: first 30 then computed values
961  scan->rvalues[ jj ] = temp_conc[ jj ];
962  }
963 
964  progress->setValue( ( ii + 1 ) );
965 //DbgLv(2) << "WD:sc secs" << scan->seconds;
966 //if ( ii == 0 || (ii+1) == total_scans ) {
967 //DbgLv(2) << "WD:S0:c00" << scan->rvalues[0];
968 //DbgLv(2) << "WD:S0:c01" << scan->rvalues[1];
969 //DbgLv(2) << "WD:S0:c30" << scan->rvalues[30];
970 //DbgLv(2) << "WD:S0:cn1" << scan->rvalues[points-2];
971 //DbgLv(2) << "WD:S0:cnn" << scan->rvalues[points-1]; }
972  }
973 
974  QString run_id = dirname.section( "/", -1, -1 );
975  QString stype = QString( QChar( sim_data.type[ 0 ] ) )
976  + QString( QChar( sim_data.type[ 1 ] ) );
977  QString schann = QString( QChar( sim_data.channel ) );
978  int cell = sim_data.cell;
979  int wvlen = qRound( sim_data.scanData[ 0 ].wavelength );
980  wvlen = ( wvlen < 99 ) ? 123 : wvlen;
981  QString ofname = QString( "%1/%2.%3.%4.%5.%6.auc" )
982  .arg( dirname ).arg( run_id ).arg( stype ).arg( cell )
983  .arg( schann ).arg( wvlen );
984 
986 
987  progress->setValue( total_scans );
988  lb_progress->setText( tr( "Completed" ) );
989 
990  pb_saveSim->setEnabled( false );
991 }
992 
993 // slot to update progress and lcd based on current component
994 void US_Astfem_Sim::update_progress( int component )
995 {
996  if ( component == -1 )
997  { // -1 component flags reset to maximum
998  progress->setValue( progress->maximum() );
999  lcd_component->setMode( QLCDNumber::Hex );
1000  lcd_component->display( "rA " );
1001  }
1002 
1003  else if ( component < 0 )
1004  { // other negative component flags set maximum
1005  progress->setMaximum( -component );
1006  lcd_component->setMode( QLCDNumber::Dec );
1007  lcd_component->display( 0 );
1008  }
1009 
1010  else
1011  { // normal call sets progress value
1012  progress->setValue( component );
1013  lcd_component->setMode( QLCDNumber::Dec );
1014  lcd_component->display( component );
1015  }
1016 }
1017 
1018 // slot to update lcd based on current component
1019 void US_Astfem_Sim::update_component( int component )
1020 {
1021  lcd_component->setMode( QLCDNumber::Dec );
1022  lcd_component->display( component );
1023 }
1024 
1025 // slot to update progress by time step
1026 void US_Astfem_Sim::show_progress( int time_step )
1027 {
1028  progress->setValue( time_step );
1029 }
1030 
1031 // slot to initialize progress and set maximum steps
1032 void US_Astfem_Sim::start_calc( int steps )
1033 {
1034  progress_text = lb_progress->text();
1035  progress_maximum = progress->maximum();
1036  progress_value = progress->value();
1037 
1038  progress ->setMaximum( steps );
1039  progress ->reset();
1040  lb_progress->setText( tr( "Calculating..." ) );
1041 }
1042 
1043 // slot to set progress to maximum
1045 {
1046  progress ->setMaximum( progress_maximum );
1047  progress ->setValue ( progress_value );
1048  lb_progress->setText( progress_text );
1049 }
1050 
1051 // slot to update movie plot
1052 void US_Astfem_Sim::update_movie_plot( QVector< double >* x, double* c )
1053 {
1054  moviePlot->clear();
1055  double total_c = 0.0;
1056  double yscale = 0.0;
1057 
1059  system.coSedSolute < 0 )
1060  { // Get total concentration of all components
1061  for ( int ii = 0; ii < system.components.size(); ii++ )
1062  total_c += system.components[ ii ].signal_concentration;
1063 
1064  yscale = total_c * 3.0;
1065  }
1066  else
1067  { // Get total concentration of non-cosed components
1068  for ( int ii = 0; ii < system.components.size(); ii++ )
1069  {
1070  if ( ii != system.coSedSolute )
1071  total_c += system.components[ ii ].signal_concentration;
1072  }
1073 
1074  yscale = total_c * 7.0;
1075  }
1076 
1077  moviePlot->setAxisScale( QwtPlot::yLeft, 0, yscale );
1078  //moviePlot->setAxisAutoScale( QwtPlot::yLeft );
1079  moviePlot->setAxisAutoScale( QwtPlot::xBottom );
1080 
1081  double* r = new double [ x->size() ];
1082 
1083  for ( int i = 0; i < x->size(); i++ ) r[ i ] = (*x)[ i ];
1084 
1085  QwtPlotCurve* curve =
1086  new QwtPlotCurve( "Scan Number " + QString::number( curve_count++ ) );
1087 
1088  curve->setPen( QPen( Qt::yellow, 3 ) );
1089  curve->setData( r, c, x->size() );
1090  curve->attach( moviePlot );
1091 
1092  moviePlot->replot();
1093 
1094  if ( save_movie )
1095  {
1096  QPixmap pmap;
1097  image_count++;
1098  imageName = imagedir + QString().sprintf( "frame%05d.png", image_count );
1100  }
1101 
1102  qApp->processEvents();
1103 //int k=x->size()-1;
1104 //int h=x->size()/2;
1105 //DbgLv(1) << "UpdMovie: r0 rh rn c0 ch cn" << r[0] << r[h] << r[k]
1106 // << c[0] << c[h] << c[k];
1107 
1108  delete [] r;
1109 }
1110 
1111 // slot to update movie plot
1113 {
1114 DbgLv(1) << "SIM: update_save_movie" << ckd;
1115  save_movie = ckd;
1116 
1117  if ( save_movie )
1118  {
1119  imagedir = QFileDialog::getExistingDirectory( this,
1120  tr( "Select or create a movie frames directory" ),
1121  US_Settings::tmpDir() );
1122 DbgLv(1) << "SIM: upd_sv_movie: imagedir" << imagedir;
1123 
1124  if ( imagedir.isEmpty() )
1125  {
1126  imagedir = US_Settings::tmpDir() + "/movies";
1127  QDir().mkpath( imagedir );
1128  }
1129 
1130  if ( ! imagedir.endsWith( "/" ) )
1131  imagedir = imagedir + "/";
1132 DbgLv(1) << "SIM: upd_sv_movie: imagedir" << imagedir;
1133  }
1134 }
1135 
1137 {
1138  qDebug() << "description" <<system.description;
1139  qDebug() << "modelGUID" << system.modelGUID;
1140  qDebug() << "component vector size" << system.components.size();
1141  for ( int i = 0; i < system.components.size(); i++ )
1142  {
1143  qDebug() << "component " << i + 1;
1145  }
1146  qDebug() << "association vector size" << system.associations.size();
1147  for ( int i = 0; i < system.associations.size(); i++ )
1148  {
1149  qDebug() << "Association vector " << i + 1;
1151  }
1152 }
1153 
1155 {
1156  qDebug() << "molar_concentration" << sc.molar_concentration;
1157  qDebug() << "signal_concentration" << sc.signal_concentration;
1158  qDebug() << "vbar20" << sc.vbar20;
1159  qDebug() << "mw" << sc.mw;
1160  qDebug() << "s" << sc.s;
1161  qDebug() << "D" << sc.D;
1162  qDebug() << "f" << sc.f;
1163  qDebug() << "f_f0" << sc.f_f0;
1164  qDebug() << "extinction" << sc.extinction;
1165  qDebug() << "axial_ratio" << sc.axial_ratio;
1166  qDebug() << "sigma" << sc.sigma;
1167  qDebug() << "delta" << sc.delta;
1168  qDebug() << "oligomer" << sc.oligomer;
1169  qDebug() << "shape" << sc.shape;
1170  qDebug() << "name" << sc.name;
1171  qDebug() << "analyte_type" << sc.analyte_type;
1172  qDebug() << "mfem_initial:";
1173  dump_mfem_initial( sc.c0 );
1174 }
1175 
1177 {
1178  qDebug() << "radius list size " << mfem.radius.size();
1179 // qDebug() << "radius list" << mfem.radius;
1180  qDebug() << "concentration list size " << mfem.concentration.size();
1181 // qDebug() << "concentration list" << mfem.concentration;
1182 }
1183 
1185 {
1186  qDebug() << "k_d" << as.k_d;
1187  qDebug() << "k_off" << as.k_off;
1188  qDebug() << "rcomps list size " << as.rcomps.size();
1189  qDebug() << "rcomps list " << as.rcomps;
1190  qDebug() << "stoichs list size " << as.stoichs.size();
1191  qDebug() << "stoichs list " << as.stoichs;
1192 }
1193 
1195 {
1196  qDebug() << "simparams";
1197  qDebug() << "mesh_radius list size " << simparams.mesh_radius.size();
1198 // qDebug() << "mesh_radius list " << simparams.mesh_radius;;
1199  qDebug() << "speed profile list size " << simparams.speed_step.size();
1200  for ( int i = 0; i < simparams.speed_step.size(); i++ )
1201  dump_ss( simparams.speed_step[ i ] );
1202  qDebug() << "simpoints " << simparams.simpoints;
1203  qDebug() << "meshType " << simparams.meshType;
1204  qDebug() << "gridType " << simparams.gridType;
1205  qDebug() << "radial_resolution " << simparams.radial_resolution;
1206  qDebug() << "meniscus " << simparams.meniscus;
1207  qDebug() << "bottom " << simparams.bottom;
1208  qDebug() << "rnoise " << simparams.rnoise;
1209  qDebug() << "tinoise " << simparams.tinoise;
1210  qDebug() << "rinoise " << simparams.rinoise;
1211  qDebug() << "rotorCalID " << simparams.rotorCalID;
1212  qDebug() << "band_forming " << simparams.band_forming;
1213  qDebug() << "band_volume " << simparams.band_volume;
1214  qDebug() << "firstScanIsConcentration "
1216 }
1217 
1219 {
1220  qDebug() << "speed profile";
1221  qDebug() << "duration_hours " << sp.duration_hours;
1222  qDebug() << "duration_minutes " << sp.duration_minutes;
1223  qDebug() << "delay_hours " << sp.delay_hours;
1224  qDebug() << "delay_minutes " << sp.delay_minutes;
1225  qDebug() << "scans " << sp.scans;
1226  qDebug() << "acceleration " << sp.acceleration;
1227  qDebug() << "rotorspeed " << sp.rotorspeed;
1228  qDebug() << "acceleration_flag " << sp.acceleration_flag;
1229 }
1230 
1232 {
1233  /*
1234  qDebug() << "astfem_data---- list size " << astfem_data.size();
1235  for ( int j = 0; j < astfem_data.size(); j++ )
1236  {
1237  qDebug() << "id " << astfem_data[ j ].id;
1238  qDebug() << "cell " << astfem_data[ j ].cell;
1239  qDebug() << "channel " << astfem_data[ j ].channel;
1240  qDebug() << "wavelength " << astfem_data[ j ].wavelength;
1241  qDebug() << "rpm " << astfem_data[ j ].rpm;
1242  qDebug() << "s20w_correction " << astfem_data[ j ].s20w_correction;
1243  qDebug() << "D20w_correction " << astfem_data[ j ].D20w_correction;
1244  qDebug() << "viscosity " << astfem_data[ j ].viscosity;
1245  qDebug() << "density " << astfem_data[ j ].density;
1246  qDebug() << "vbar " << astfem_data[ j ].vbar;
1247  qDebug() << "avg_temperature " << astfem_data[ j ].avg_temperature;
1248  qDebug() << "vbar20 " << astfem_data[ j ].vbar20;
1249  qDebug() << "meniscus " << astfem_data[ j ].meniscus;
1250  qDebug() << "bottom " << astfem_data[ j ].bottom;
1251  qDebug() << "radius list size " << astfem_data[ j ].radius.size();
1252  //qDebug() << "radius list " << astfem_data[ j ].radius;;
1253  qDebug() << "scan list size " << astfem_data[ j ].scan.size();
1254  for ( int i = 0; i < astfem_data[ j ].scan.size(); i++ )
1255  dump_mfem_scan( astfem_data[ j ].scan [ i ] );
1256  }
1257  */
1258 }
1259 
1261 {
1262  /*
1263  qDebug() << "mfem_scan----";
1264  qDebug() << "time " << ms.time;
1265  qDebug() << "omega_s_t " << ms.omega_s_t;
1266  qDebug() << "rpm " << ms.rpm;
1267  qDebug() << "temperature " << ms.temperature;
1268  qDebug() << "time " << ms.time;
1269  qDebug() << "conc list size " << ms.conc.size();
1270  //qDebug() << "conc " << ms.conc;
1271  */
1272 }
1273