UltraScan III
us_equiltime.cpp
Go to the documentation of this file.
1 #include "us_equiltime.h"
3 #include "us_gui_settings.h"
4 #include "us_settings.h"
5 #include "us_constants.h"
6 #include "us_math2.h"
7 #include "us_license_t.h"
8 #include "us_license.h"
9 #include "us_model_gui.h"
10 //#include "us_model_editor.h"
11 
13 // the class US_Equilspeed.
14 
15 int main( int argc, char* argv[] )
16 {
17  QApplication application( argc, argv );
18 
19  #include "main1.inc"
20 
21  // License is OK. Start up.
22 
23  US_EquilTime w;
24  w.show(); // Member of QWidget
25  return application.exec(); // Member of QApplication
26 }
27 
29 {
31 
32  connect( astfem_rsa, SIGNAL( new_scan ( QVector< double >*, double* ) ),
33  SLOT( check_equil( QVector< double >*, double* ) ) );
34 
35  connect( astfem_rsa, SIGNAL( new_time( double ) ),
36  SLOT( set_time( double ) ) );
37 
38  setWindowTitle( tr( "Equilibrium Time Prediction" ) );
39  setPalette( US_GuiSettings::frameColor() );
40 
42 
43  QBoxLayout* main = new QHBoxLayout( this );
44  main->setSpacing ( 2 );
45  main->setContentsMargins ( 2, 2, 2, 2 );
46 
47  // Left Column
48  QGridLayout* left = new QGridLayout;
49  int row = 0;
50 
51  QLabel* lb_sample = us_banner( tr( "Model Settings" ) );
52  left->addWidget( lb_sample, row++, 0, 1, 2 );
53 
54  QGridLayout* buttons1 = new QGridLayout;
55  int b_row = 0;
56 
57  pb_changeModel = us_pushbutton( tr( "Set / Change / Review Model") );
58  connect ( pb_changeModel, SIGNAL( clicked() ) , SLOT( change_model() ) );
59  buttons1->addWidget( pb_changeModel, b_row++, 0, 1, 2 );
60 
61  left->addLayout( buttons1, row, 0, 3, 2 );
62  row += 3;
63 
64 
65  QPalette p;
66  p.setColor( QPalette::WindowText, Qt::white );
67  p.setColor( QPalette::Shadow , Qt::white );
68 
69  QFont font( US_GuiSettings::fontFamily(),
71  QFont::Bold );
72 
73  // Radius Info
74  QLabel* lb_radius = us_banner( tr( "Radius Settings" ) );
75  left->addWidget( lb_radius, row++, 0, 1, 2 );
76 
77  QGroupBox* channelGroupBox = new QGroupBox( tr( "Channel Type" ) );
78  channelGroupBox->setContentsMargins ( 2, 2, 2, 2 );
79  channelGroupBox->setPalette( p );
80  channelGroupBox->setFont ( font );
81 
82  QRadioButton* rb_inner;
83  QRadioButton* rb_outer;
84  QRadioButton* rb_center;
85  QRadioButton* rb_custom;
86 
87  QGridLayout* rb5 = us_radiobutton( tr( "Inner Channel" ), rb_inner, true );
88  QGridLayout* rb6 = us_radiobutton( tr( "Outer Channel" ), rb_outer );
89  QGridLayout* rb7 = us_radiobutton( tr( "Center Channel" ), rb_center );
90  QGridLayout* rb8 = us_radiobutton( tr( "Custom" ), rb_custom );
91 
92  // Group the buttons
93  QButtonGroup* channelGroup = new QButtonGroup;
94  channelGroup->addButton( rb_inner , INNER );
95  channelGroup->addButton( rb_outer , OUTER );
96  channelGroup->addButton( rb_center, CENTER );
97  channelGroup->addButton( rb_custom, CUSTOM );
98  connect( channelGroup, SIGNAL( buttonClicked( int ) ),
99  SLOT ( new_channel ( int ) ) );
100 
102 
103  QGridLayout* channel = new QGridLayout;
104  channel->setContentsMargins ( 2, 2, 2, 2 );
105  channel->setSpacing( 0 );
106  channel->addLayout( rb5, 0, 0 );
107  channel->addLayout( rb6, 0, 1 );
108  channel->addLayout( rb7, 1, 0 );
109  channel->addLayout( rb8, 1, 1 );
110 
111  channelGroupBox->setLayout( channel );
112 
113  left->addWidget( channelGroupBox, row, 0, 2, 2 );
114  row += 2;
115 
116  // Top Radius
117  QLabel* lb_top = us_label( tr( "Top Radius:" ) );
118  left->addWidget( lb_top, row, 0 );
119 
120  cnt_top = us_counter( 3, 5.8, 7.3, 5.9 );
121  cnt_top->setStep ( 0.01 );
122  cnt_top->setEnabled ( false );
123  left->addWidget( cnt_top, row++, 1 );
124 
125  // Bottom Radius
126  QLabel* lb_bottom = us_label( tr( "Bottom Radius:" ) );
127  left->addWidget( lb_bottom, row, 0 );
128 
129  cnt_bottom = us_counter( 3, 5.8, 7.3, 6.2 );
130  cnt_bottom->setStep ( 0.01 );
131  cnt_bottom->setEnabled ( false );
132  left->addWidget( cnt_bottom, row++, 1 );
133 
134  // Rotorspeed Info
135  QLabel* lb_rotor = us_banner( tr( "Rotorspeed Settings" ) );
136  left->addWidget( lb_rotor, row++, 0, 1, 2 );
137 
138  // Speed type buttons
139  QGroupBox* rotor = new QGroupBox( tr( "Speed Type" ) );
140  rotor->setContentsMargins ( 2, 10, 2, 2 );
141  rotor->setPalette( p );
142  rotor->setFont ( font );
143 
144  QRadioButton* rb_sigma;
145  QRadioButton* rb_rpm;
146 
147  QGridLayout* rb9 = us_radiobutton( "Use Sigma", rb_sigma, true );
148  QGridLayout* rb10 = us_radiobutton( "Use RPM" , rb_rpm );
149 
150  speed_type = SIGMA;
151  sigma_start = 1;
152  sigma_stop = 4;
153  rpm_start = 18000;
154  rpm_stop = 36000;
155 
156  speed_count = 5;
157 
158  QButtonGroup* speedGroup = new QButtonGroup;
159  speedGroup->addButton( rb_sigma, SIGMA );
160  speedGroup->addButton( rb_rpm , RPM );
161  connect( speedGroup, SIGNAL( buttonClicked( int ) ),
162  SLOT ( update_speeds( int ) ) );
163 
164  QGridLayout* speedType = new QGridLayout;
165  speedType->setContentsMargins( 2, 2, 2, 2 );
166  speedType->setSpacing ( 0 );
167 
168  speedType->addLayout( rb9, 0, 0 );
169  speedType->addLayout( rb10, 0, 1 );
170 
171  rotor->setLayout( speedType );
172 
173  left->addWidget( rotor, row++, 0, 1, 2 );
174 
175  // Low speed
176  lb_lowspeed = us_label( tr( "Low Speed (sigma):" ) );
177  left->addWidget( lb_lowspeed, row, 0 );
178 
179  cnt_lowspeed = us_counter( 3, 0.01, 10.0, sigma_start );
180  cnt_lowspeed->setStep( 0.01 );
181  left->addWidget( cnt_lowspeed, row++, 1 );
182  connect( cnt_lowspeed, SIGNAL( valueChanged( double ) ),
183  SLOT ( new_lowspeed( double ) ) );
184 
185  // High speed
186  lb_highspeed = us_label( tr( "High Speed (sigma):" ) );
187  left->addWidget( lb_highspeed, row, 0 );
188 
189  cnt_highspeed = us_counter( 3, 0.01, 10.0, sigma_stop );
190  cnt_highspeed->setStep( 0.01 );
191  left->addWidget( cnt_highspeed, row++, 1 );
192  connect( cnt_highspeed, SIGNAL( valueChanged ( double ) ),
193  SLOT ( new_highspeed( double ) ) );
194  // Speed steps
195  QLabel* lb_speedsteps = us_label( tr( "Speed Steps:" ) );
196  left->addWidget( lb_speedsteps, row, 0 );
197 
198  cnt_speedsteps = us_counter( 3, 1.0, 100.0, speed_count );
199  cnt_speedsteps->setStep( 1.0 );
200  left->addWidget( cnt_speedsteps, row++, 1 );
201  connect( cnt_speedsteps, SIGNAL( valueChanged ( double ) ),
202  SLOT ( new_speedstep( double ) ) );
203  // Speed list
204  QLabel* lb_speedlist = us_label( tr( "Current Speed List:" ) );
205  left->addWidget( lb_speedlist, row, 0 );
206 
208  te_speedlist->setReadOnly( true );
209 
210  left->addWidget( te_speedlist, row, 1, 3, 1 );
211 
212  left->setRowStretch ( row + 1, 99 );
213  row += 3;
214 
215  // Misc Info
216  QLabel* lb_sim2 = us_banner( tr( "Simulation Settings" ) );
217  left->addWidget( lb_sim2, row++, 0, 1, 2 );
218 
219  // Tolerance
220  QLabel* lb_tolerance = us_label( tr( "Tolerance:" ) );
221  left->addWidget( lb_tolerance, row, 0 );
222 
223  cnt_tolerance = us_counter( 3, 1.0e-5, 0.01, 0.0005 );
224  cnt_tolerance->setStep( 1.0e-5 );
225  left->addWidget( cnt_tolerance, row++, 1 );
226 
227  // Time increment
228  QLabel* lb_time = us_label( tr( "Time Increment (min):" ) );
229  left->addWidget( lb_time, row, 0 );
230 
231  cnt_timeIncrement = us_counter( 3, 1.0, 1000.0, 15.0 );
232  cnt_timeIncrement->setStep( 1.0 );
233  left->addWidget( cnt_timeIncrement, row++, 1 );
234 
235  QGridLayout* buttons2 = new QGridLayout;
236  b_row = 0;
237 
238  pb_estimate = us_pushbutton( tr( "Estimate Times" ) );
239  pb_estimate->setEnabled( false );
240  connect( pb_estimate, SIGNAL( clicked() ), SLOT( simulate() ) );
241  buttons2->addWidget( pb_estimate, b_row++, 0, 1, 2 );
242 
243  QPushButton* pb_help = us_pushbutton( tr( "Help" ) );
244  connect( pb_help, SIGNAL( clicked() ), SLOT( help() ) );
245  buttons2->addWidget( pb_help, b_row, 0 );
246 
247  QPushButton* pb_close = us_pushbutton( tr( "Close" ) );
248  connect( pb_close, SIGNAL( clicked() ), SLOT( close() ) );
249  buttons2->addWidget( pb_close, b_row++, 1 );
250 
251  left->addLayout( buttons2, row, 0, 2, 2 );
252 
253  main->addLayout( left );
254 
255  // Right Column
256  // Simulation plot
257  QBoxLayout* right = new QVBoxLayout;
258 
259  QBoxLayout* plot = new US_Plot( equilibrium_plot,
260  tr( "Approach to Equilibrium Simulation" ),
261  tr( "Radius" ), tr( "Concentration" ) );
263 
264  equilibrium_plot->setMinimumSize( 600, 400 );
265  equilibrium_plot->setAxisScale( QwtPlot::yLeft , 0.0, 1.5 );
266  equilibrium_plot->setAxisScale( QwtPlot::xBottom, 5.9, 6.2 );
267 
268  right->addLayout( plot );
269 
270  te_info = new US_Editor( 0, true );
271 
272  QFontMetrics fm( te_info->font() );
273  te_info->setFixedHeight( fm.height() * 15 );
274 
275  right->addWidget( te_info );
276  right->setStretchFactor( plot , 10 );
277  right->setStretchFactor( te_info, 2 );
278 
279  main->addLayout( right );
280 
281  model.components.clear();
283 }
284 
285 void US_EquilTime::new_lowspeed( double speed )
286 {
287  if ( speed > cnt_highspeed->value() )
288  {
289  cnt_lowspeed->setValue( cnt_highspeed->value() );
290 
291  QMessageBox::warning( this,
292  tr( "Warning" ),
293  tr( "The low speed value cannot exceed the high speed value" ) );
294  return;
295  }
296 
297  if ( speed_type == SIGMA )
298  {
299  sigma_start = speed;
300  if ( fabs( sigma_stop - sigma_start ) < 1.0e-4 )
301  {
302  speed_count = 1;
303  cnt_speedsteps->setValue( speed_count );
304  }
305  }
306  else
307  {
308  rpm_start = speed;
309  if ( fabs( rpm_stop - rpm_start ) < 100.0 )
310  {
311  speed_count = 1;
312  cnt_speedsteps->setValue( speed_count );
313  }
314  }
315 
317 }
318 
319 void US_EquilTime::new_highspeed( double speed )
320 {
321  if ( speed < cnt_lowspeed->value() )
322  {
323  cnt_highspeed->setValue( cnt_lowspeed->value() );
324 
325  QMessageBox::warning( this,
326  tr( "Warning" ),
327  tr( "The high speed value cannot be less than the low speed value" ) );
328  return;
329  }
330 
331  if ( speed_type == SIGMA )
332  {
333  sigma_stop = speed;
334  if ( fabs( sigma_stop - sigma_start ) < 1.0e-4 )
335  {
336  speed_count = 1;
337  cnt_speedsteps->setValue( speed_count );
338  }
339  }
340  else
341  {
342  rpm_stop = speed;
343  if ( fabs( rpm_stop - rpm_start ) < 100.0 )
344  {
345  speed_count = 1;
346  cnt_speedsteps->setValue( speed_count );
347  }
348  }
349 
351 }
352 
353 void US_EquilTime::new_speedstep( double count )
354 {
355  speed_count = (int) count ;
357 }
358 
359 void US_EquilTime::new_channel( int channel )
360 {
361  cnt_top ->setEnabled( false );
362  cnt_bottom->setEnabled( false );
363 
364  const double inner_top = 5.788;
365  const double inner_bottom = 6.111;
366  const double center_top = 6.290;
367  const double center_bottom = 6.613;
368  const double outer_top = 6.781;
369  const double outer_bottom = 7.104;
370 
371 
372  switch ( channel )
373  {
374  case INNER:
375  cnt_top ->setValue( inner_top );
376  cnt_bottom->setValue( inner_bottom );
377  equilibrium_plot->setAxisScale( QwtPlot::xBottom,
378  inner_top, inner_bottom );
379  break;
380 
381  case OUTER:
382  cnt_top ->setValue( outer_top );
383  cnt_bottom->setValue( outer_bottom );
384  equilibrium_plot->setAxisScale( QwtPlot::xBottom,
385  outer_top, outer_bottom );
386  break;
387 
388  case CENTER:
389  cnt_top ->setValue( center_top );
390  cnt_bottom->setValue( center_bottom );
391  equilibrium_plot->setAxisScale( QwtPlot::xBottom,
392  outer_top, center_bottom );
393  break;
394 
395  case CUSTOM:
396  cnt_top ->setEnabled( true );
397  cnt_bottom->setEnabled( true );
398  equilibrium_plot->setAxisScale( QwtPlot::xBottom, 5.8, 7.3 );
399  break;
400  }
401 
402  equilibrium_plot->replot();
403 }
404 
405 double US_EquilTime::rpmFromSigma( double sigma )
406 {
407  double T = K0 + 20.0; // 20C for now
408  double mw = model.components[ 0 ].mw;
409  double vbar = model.components[ 0 ].vbar20;
410  double rho = DENS_20W; //model.density;
411 
412  double rpm = 30.0 / M_PI *
413  sqrt( sigma * R * T * 2 / ( mw * ( 1 - vbar * rho ) ) );
414 
415  rpm = floor( rpm / 100.0 + 0.5 ) * 100.0; // Round to closest 100
416 
417  return rpm;
418 }
419 
420 double US_EquilTime::sigmaFromRpm( double rpm )
421 {
422  /* Sigma is a measure of the curvature of the exponential.
423  * If it is too small, the curvature is shallow and there is not
424  * enough information. If it is too steep, most of the concentration
425  * points are going to be near zero, drowned out by bad s/n ratios.
426  * The happy medium is between 1 < sigma < 4, as far as equilibrium
427  * exponents are concerned. It's a convenient way to parameterize
428  * curvature, which is a function of rotor speed and molecular weight.
429  */
430 
431  double T = K0 + 20.0; // 20C for now
432  double mw = model.components[ 0 ].mw;
433  double vbar = model.components[ 0 ].vbar20;
434  double rho = DENS_20W; //model.density;
435 
436  double sigma = mw * ( 1 - vbar * rho ) * sq( M_PI / 30.0 * rpm ) /
437  ( 2 * R * T );
438 
439  return sigma;
440 }
441 
443 {
444  speed_steps.clear();
445  te_speedlist->clear();
446 
447  if ( type == SIGMA )
448  {
449  // Determine max sigma
450  double max_sigma;
451 
452  if ( model.components.size() > 0 )
453  {
454  max_sigma = sigmaFromRpm( 60000.0 );
455 
456  if ( type != speed_type )
457  {
458  sigma_start = sigmaFromRpm( cnt_lowspeed ->value() );
459  sigma_stop = sigmaFromRpm( cnt_highspeed->value() );
460  }
461  }
462  else
463  max_sigma = 10;
464 
465  if ( sigma_start > max_sigma ) sigma_start = max_sigma;
466  if ( sigma_stop > max_sigma ) sigma_stop = max_sigma;
467 
468  lb_lowspeed ->setText( tr( "Low Speed (sigma):" ) );
469  lb_highspeed->setText( tr( "High Speed (sigma):" ) );
470 
471  // Reset counters
472  cnt_lowspeed ->disconnect();
473  cnt_highspeed->disconnect();
474  cnt_lowspeed ->setRange( 0.1, max_sigma, 0.01 );
475  cnt_lowspeed ->setValue( sigma_start );
476  cnt_highspeed->setRange( 0.1, max_sigma, 0.01 );
477  cnt_highspeed->setValue( sigma_stop );
478 
479  connect( cnt_lowspeed, SIGNAL( valueChanged( double ) ),
480  SLOT ( new_lowspeed( double ) ) );
481 
482  connect( cnt_highspeed, SIGNAL( valueChanged ( double ) ),
483  SLOT ( new_highspeed( double ) ) );
484 
485  if ( fabs( sigma_stop - sigma_start ) < 0.1 ) speed_count = 1;
486  cnt_speedsteps->setValue( speed_count );
487 
488  if ( speed_count > 1 )
489  {
490  double increment = ( sigma_stop - sigma_start ) / ( speed_count - 1 );
491 
492  for ( int i = 0; i < speed_count; i++ )
493  {
494  speed_steps << sigma_start + i * increment;
495  te_speedlist->append( QString::number( i + 1 ) + ": sigma = " +
496  QString::number( speed_steps[ i ], 'f', 3 ) );
497  }
498  }
499  else
500  {
502  te_speedlist->append( "1: sigma = " +
503  QString::number( sigma_start, 'f', 3 ) );
504  }
505  }
506  else
507  {
508  if ( model.components.size() > 0 && type != speed_type )
509  {
510  rpm_start = rpmFromSigma( cnt_lowspeed ->value() );
511  rpm_stop = rpmFromSigma( cnt_highspeed->value() );
512  }
513 
514  lb_lowspeed ->setText( tr( "Low Speed (rpm):" ) );
515  lb_highspeed->setText( tr( "High Speed (rpm):" ) );
516 
517  // Reset counters
518  cnt_lowspeed ->disconnect();
519  cnt_highspeed->disconnect();
520 
521  cnt_lowspeed ->setRange( 100, 60000, 100 );
522  cnt_lowspeed ->setValue( rpm_start );
523  cnt_highspeed ->setRange( 100, 60000, 100 );
524  cnt_highspeed ->setValue( rpm_stop );
525 
526  connect( cnt_lowspeed, SIGNAL( valueChanged( double ) ),
527  SLOT ( new_lowspeed( double ) ) );
528 
529  connect( cnt_highspeed, SIGNAL( valueChanged ( double ) ),
530  SLOT ( new_highspeed( double ) ) );
531 
532  if ( fabs( rpm_stop - rpm_start ) < 100.0 ) speed_count = 1;
533  cnt_speedsteps->setValue( speed_count );
534 
535  if ( speed_count > 1 )
536  {
537  double increment = ( rpm_stop - rpm_start ) / ( speed_count - 1 );
538 
539  for ( int i = 0; i < speed_count; i++ )
540  {
541  double rpm = rpm_start + i * increment;
542  rpm = floor( rpm / 100.0 + 0.5 ) * 100.0; // Round to closest 100
543  speed_steps << rpm;
544  te_speedlist->append( QString::number( i + 1 ) + ": rpm = " +
545  QString::number( rpm ) );
546  }
547  }
548  else
549  {
551  te_speedlist->append( "1: rpm = " +
552  QString::number( rpm_start ) );
553  }
554  }
555 
556  speed_type = type;
557 }
558 
560 {
561  US_ModelGui* dialog = new US_ModelGui( model );
562  connect( dialog, SIGNAL( valueChanged( US_Model ) ),
563  SLOT ( set_model ( US_Model ) ) );
564  dialog->exec();
565 }
566 
568 {
569  model = m;
570 
571  pb_estimate ->setEnabled( true );
573 }
574 
576 {
577  simparams.speed_step.clear();
578 
580 
581  // These are the only changes from the constructor
582  sp.duration_hours = 100;
583  sp.rotorspeed = 100; // Updated before use
584  sp.scans = 2;
585  sp.acceleration_flag = false;
586 
587  simparams.speed_step << sp;
588 }
589 
591 {
592  astfem_data.scanData.clear();
593  astfem_data.xvalues .clear();
594 
595  // Assign radius data
596  double r = simparams.meniscus;
597 
598  while ( r <= simparams.bottom )
599  {
600  astfem_data.xvalues << r;
602  }
603 
605 
606  // Contant temperature for now. A temperature counter could be added
607  // the this program.
608 
609  US_DataIO::Scan scan;
610  scan.temperature = 20.0;
611  scan.wavelength = 999;
612  scan.rvalues.fill( 0.0, radius_points );
613 
614  astfem_data.scanData << scan;
615 }
616 
618 {
619  simparams.meniscus = cnt_top ->value();
620  simparams.bottom = cnt_bottom->value();
621 
622  // Handle case of custom channel type
623  equilibrium_plot->setAxisScale( QwtPlot::xBottom,
625 
626  equilibrium_plot->clear();
627  equilibrium_plot->replot();
628  current_time = 0.0;
629  concentration.clear();
630  current_curve = NULL;
631 
632  astfem_rsa->set_movie_flag( true );
633 
635 
636  te_info->e->append( tr( "Sigma RPM Time Increment Total Time\n" ) );
637 
638  for ( int i = 0; i < speed_steps.count(); i++ )
639  {
640  simparams.firstScanIsConcentration = ( i == 0 ) ? false : true;
641  astfem_rsa->setStopFlag( false );
642 
643  // Set up simparams for this step
644  if ( speed_type == SIGMA )
645  {
646  double sigma = speed_steps[ i ];
647  simparams.speed_step[ 0 ].rotorspeed = (int) rpmFromSigma( sigma );
648  }
649  else
650  simparams.speed_step[ 0 ].rotorspeed = (int) speed_steps[ i ];
651 
652  // Setup this curve
653  current_curve =
654  new QwtPlotCurve( "Step Number " + QString::number( i ) );
655 
656  current_curve->setPen( QPen( Qt::green ) );
657  current_curve->attach( equilibrium_plot );
658 
659  // Set up for next step
660  astfem_data.scanData[ 0 ].seconds = 0.0;
661 
662  next_scan_time = cnt_timeIncrement->value() * 60.0;
663  step_time = 0.0;
664 
665  // Do the simulation
667 
669 
670  // Copy last scan data to initial concentration
671  for ( int i = 0; i < radius_points; i++ )
672  astfem_data.scanData[ 0 ].rvalues[ i ] = concentration[ i ];
673 
674  concentration.clear(); // Force allocation of new plot data
675 
676  // Draw curve
677  current_curve->setPen( QPen( Qt::red ) );
678  equilibrium_plot->replot();
679 
680  // Output results
681  int rpm = simparams.speed_step[ 0 ].rotorspeed;
682  double sigma = sigmaFromRpm( rpm );
683 
684  QString results;
685  results.sprintf( "%6.4f %5d %6.2f hours %6.2f hours",
686  sigma, rpm, step_time / 3600.0, current_time / 3600.0 );
687 
688  te_info->e->append( results );
689  }
690 
691  te_info->e->append(
692  tr( "(Note: All speeds have been adjusted to be rounded to "
693  "the nearest 100 RPM.)\n" ) +
694  "______________________________________________________"
695  "_____________________\n" );
696 }
697 
698 void US_EquilTime::check_equil( QVector< double >* x, double* c )
699 {
700  // If we are not at the time increment, return
701  if ( step_time < next_scan_time ) return;
702 
703  // If first scan, just copy concentrations
704  if ( concentration.isEmpty() )
705  {
706  radius_points = x->size();
707  sim_radius .resize( radius_points );
708  concentration.resize( radius_points );
709 
710  for ( int i = 0; i < radius_points; i++ )
711  {
712  sim_radius [ i ] = (*x)[ i ];
713  concentration[ i ] = c [ i ];
714  }
715  return;
716  }
717 
718  // Determine the scan concentration differences
719  double diffs = 0.0;
720 
721  for ( int i = 0; i < radius_points; i++ )
722  diffs += sq( concentration[ i ] - c[ i ] );
723 
724  // If within tolerance stop the simulation, otherwise copy the concentrations
725  if ( sqrt( diffs / radius_points ) < cnt_tolerance->value() )
726  {
727  astfem_rsa->setStopFlag( true );
728 
729  current_curve->setData( sim_radius.data(), concentration.data(),
730  radius_points );
731  equilibrium_plot->replot();
732  }
733  else
734  for ( int i = 0; i < radius_points; i++ ) concentration[ i ] = c[ i ];
735 
736  next_scan_time += cnt_timeIncrement->value() * 60.0;
737 }
738