UltraScan III
us_model_editor.cpp
Go to the documentation of this file.
1 
3 #include "us_model_editor.h"
4 #include "us_gui_settings.h"
5 #include "us_settings.h"
6 #include "us_constants.h"
7 #include "us_analyte_gui.h"
8 
10  ModelSystem& current_model,
11  QWidget* p, Qt::WindowFlags f )
12  : US_WidgetsDialog( p, f ), model( current_model )
13 {
14  setWindowTitle( "Model Editor" );
15  setPalette( US_GuiSettings::frameColor() );
16 
17  setWindowModality( Qt::WindowModal );
18  setAttribute( Qt::WA_DeleteOnClose );
19 
20  QGridLayout* main = new QGridLayout( this );
21  main->setSpacing( 2 );
22 
23  component = 0; // Initial component
24  shape = PROLATE; // Inital shape (prolate)
25  c0_file = "";
26 
27  // Convenience
28  struct SimulationComponent* sc = &model.component_vector[ component ];
29  vector< struct SimulationComponent >* scl = &model.component_vector;
30 
31  sc->density = DENS_20W;
32 
33  int row = 0;
34 
36  main->addWidget( lb_header, row++, 0, 1, 4 );
37 
38  // Left Column
39 
40  // Label
41  QLabel* lb_current = us_label( tr( "Current Component:" ) );
42  lb_current-> setSizePolicy ( QSizePolicy::Preferred, QSizePolicy::Fixed );
43  main->addWidget( lb_current, row++, 0, 1, 2 );
44 
45  // Combo box
47  cmb_component1->setMaxVisibleItems( 5 );
48 
49  for ( uint i = 0; i < scl->size(); i++ )
50  cmb_component1->addItem( ( *scl )[ i ].name );
51 
52  cmb_component1->setCurrentIndex( 0 );
53 
54  connect( cmb_component1, SIGNAL( currentIndexChanged( int ) ),
55  SLOT ( change_component1 ( int ) ) );
56 
57  main->addWidget( cmb_component1, row++, 0, 1, 2 );
58 
59  // Label + line edit
60  QLabel* lb_sedCoef = us_label( tr( "Sedimentation Coeff. (sec):" ) );
61  main->addWidget( lb_sedCoef, row, 0 );
62 
63  le_sed = us_lineedit( QString::number( sc->s, 'e', 4 ) );
64  connect( le_sed, SIGNAL( textEdited( const QString& ) ),
65  SLOT ( update_sed( const QString& ) ) );
66  main->addWidget( le_sed, row++, 1 );
67 
68  // Label + line edit
69  QLabel* lb_diffusion = us_label( tr( "Diffusion Coeff. (cm<sup>2</sup>/sec):" ) );
70  main->addWidget( lb_diffusion, row, 0 );
71 
72  le_diff = us_lineedit( QString::number( sc->D, 'e', 4 ) );
73  connect( le_diff, SIGNAL( textEdited ( const QString& ) ),
74  SLOT ( update_diff( const QString& ) ) );
75  main->addWidget( le_diff, row++, 1 );
76 
77  // Label + line edit
78  QLabel* lb_ExtCoef = us_label( tr( "Extinction Coeff. (OD/mol):" ) );
79  main->addWidget( lb_ExtCoef, row, 0 );
80 
81  le_extinction = us_lineedit( QString::number( sc->extinction, 'e', 4 ) );
82  connect( le_extinction, SIGNAL( textEdited ( const QString& ) ),
83  SLOT ( update_extinction( const QString& ) ) );
84  main->addWidget( le_extinction, row++, 1 );
85 
86  // Pushbutton + line edit
87  pb_vbar = us_pushbutton( "vbar (ccm/g, 20C)" );
88  connect( pb_vbar, SIGNAL( clicked() ), SLOT( get_vbar() ) );
89  main->addWidget( pb_vbar, row, 0 );
90 
91  le_vbar = us_lineedit( QString::number( sc->vbar20, 'e', 4 ) );
92  connect( le_vbar, SIGNAL( textEdited ( const QString& ) ),
93  SLOT ( update_vbar( const QString& ) ) );
94  main->addWidget( le_vbar, row++, 1 );
95 
96  // Label + line edit
97  QLabel* lb_mw = us_label( tr( "Molecular Weight (Da)" ) );
98  main->addWidget( lb_mw, row, 0 );
99 
100  le_mw = us_lineedit( QString::number( sc->mw, 'e', 4 ) );
101  connect( le_mw, SIGNAL( textEdited( const QString& ) ),
102  SLOT ( update_mw ( const QString& ) ) );
103  main->addWidget( le_mw, row++, 1 );
104 
105  // Label + line edit
106  QLabel* lb_f_f0 = us_label( tr( "Frictional Ratio (f/f0)" ) );
107  main->addWidget( lb_f_f0, row, 0 );
108 
109  le_f_f0 = us_lineedit( QString::number( sc->f_f0, 'e', 4 ) );
110  connect( le_f_f0, SIGNAL( textEdited ( const QString& ) ),
111  SLOT ( update_f_f0( const QString& ) ) );
112  main->addWidget( le_f_f0, row++, 1 );
113 
114  // Pushbutton + spin box
115  QPushButton* pb_sim = us_pushbutton( "Simulate s and D" );
116  connect( pb_sim, SIGNAL( clicked() ), SLOT( simulate_component( ) ) );
117  main->addWidget( pb_sim, row, 0 );
118 
119  sb_count = new QSpinBox();
120  sb_count->setFont(
122  sb_count->setRange( 1, scl->size() );
123  sb_count->setValue( 1 );
124  sb_count->setPalette( US_GuiSettings::editColor() );
125 
126  connect( sb_count, SIGNAL( valueChanged ( int ) ),
127  SLOT ( change_spinbox( int ) ) );
128 
129  main->addWidget( sb_count, row++, 1 );
130 
131  // Radio buttons
132 
133  QGridLayout* prolate = us_radiobutton( "Prolate Ellipsoid", rb_prolate, true );
134  rb_prolate->setEnabled( false );
135  main->addLayout( prolate, row, 0 );
136 
137  QGridLayout* oblate = us_radiobutton( "Oblate Ellipsoid", rb_oblate );;
138  rb_oblate->setEnabled( false );
139  main->addLayout( oblate, row++, 1 );
140 
141  QGridLayout* rod = us_radiobutton( "Long Rod", rb_rod );
142  rb_rod->setEnabled( false );
143  main->addLayout( rod, row, 0 );
144 
145  QGridLayout* sphere = us_radiobutton( "Sphere", rb_sphere );
146  rb_sphere->setEnabled( false );
147  main->addLayout( sphere, row++, 1 );
148 
149  QButtonGroup* shapeButtons = new QButtonGroup( this );
150  shapeButtons->addButton( rb_prolate, PROLATE );
151  shapeButtons->addButton( rb_oblate , OBLATE );
152  shapeButtons->addButton( rb_rod , ROD );
153  shapeButtons->addButton( rb_sphere , SPHERE );
154  connect( shapeButtons, SIGNAL( buttonClicked( int ) ),
155  SLOT ( select_shape ( int ) ) );
156 
157  // Right column
158 
159  row = 1;
160 
161  QLabel* lb_linked = us_label( tr( "This component is linked to:" ) );
162  main->addWidget( lb_linked, row++, 2, 1, 2 );
163 
164  // Combo box
166  cmb_component2->setEnabled( false );
167  cmb_component2->setMaximumHeight( (int)( cmb_component1->height() * 1.7 ) );
168 
169  for ( uint i = 0; i < sc->show_component.size(); i++ )
170  cmb_component2->addItem( (*scl)[ sc->show_component[ i ] ].name );
171 
172  main->addWidget( cmb_component2, row++, 2, 2, 2 );
173  row++;
174 
175  // Label + line edit
176  QLabel* lb_conc = us_label( tr( "Partial Conc. (in OD):" ) );
177  main->addWidget( lb_conc, row, 2 );
178 
179  le_conc = us_lineedit( QString::number( sc->concentration, 'e', 4 ) );
180  connect( le_conc, SIGNAL( textEdited ( const QString& ) ),
181  SLOT ( update_conc( const QString& ) ) );
182  main->addWidget( le_conc, row++, 3 );
183 
184  // Pushbutton + line edit
185  pb_load_c0 = us_pushbutton( "Load C0 File", false );
186  connect( pb_load_c0, SIGNAL( clicked() ), SLOT( load_c0() ) );
187  main->addWidget( pb_load_c0, row, 2 );
188 
189  le_c0 = us_lineedit( "" );
190  le_c0->setEnabled( false );
191  main->addWidget( le_c0, row++, 3 );
192 
193  //label x 2
194  QLabel* lb_adjust = us_label( tr( "Please adjust for correct pathlength)" ) );
195  lb_adjust->setAlignment( Qt::AlignCenter );
196  main->addWidget( lb_adjust, row++, 2, 1, 2 );
197 
198  // Label + line edit
199  QLabel* lb_keq = us_label( tr( "Equilibrium Const. (in OD):" ) );
200  main->addWidget( lb_keq, row, 2 );
201 
202  le_keq = us_lineedit( "" );
203  le_keq->setEnabled( false );
204  connect( le_keq, SIGNAL( textEdited( const QString& ) ),
205  SLOT ( update_keq( const QString& ) ) );
206  main->addWidget( le_keq, row++, 3 );
207 
208  // Label + line edit
209  QLabel* lb_koff = us_label( tr( "K_off Rate Constant (1/sec):" ) );
210  main->addWidget( lb_koff, row, 2 );
211 
212  le_koff = us_lineedit( "" );
213  le_koff->setEnabled( false );
214  connect( le_koff, SIGNAL( textEdited ( const QString& ) ),
215  SLOT ( update_koff( const QString& ) ) );
216  main->addWidget( le_koff, row++, 3 );
217 
218  // Label + line edit
219  QLabel* lb_stoich = us_label( tr( "Stoichiometry:" ) );
220  main->addWidget( lb_stoich, row, 2 );
221 
222  le_stoich = us_lineedit( "" );
223  le_stoich->setEnabled( false );
224  main->addWidget( le_stoich, row++, 3 );
225 
226  // Label + line edit
227  QLabel* lb_sigma = us_label( tr( "Conc. Dependency of s (sigma):" ) );
228  main->addWidget( lb_sigma, row, 2 );
229 
230  le_sigma = us_lineedit( "" );
231  le_sigma->setEnabled( false );
232  connect( le_sigma, SIGNAL( textEdited ( const QString& ) ),
233  SLOT ( update_sigma( const QString& ) ) );
234  main->addWidget( le_sigma, row++, 3 );
235 
236  // Label + line edit
237  QLabel* lb_delta = us_label( tr( "Conc. Dependency of D (delta):" ) );
238  main->addWidget( lb_delta, row, 2 );
239 
240  le_delta = us_lineedit( "" );
241  le_delta->setEnabled( false );
242  connect( le_delta, SIGNAL( textEdited ( const QString& ) ),
243  SLOT ( update_delta( const QString& ) ) );
244  main->addWidget( le_delta, row++, 3 );
245 
246  // empty row
247  row++;
248 
249  // Pushbuttons
250  QBoxLayout* buttonbox = new QHBoxLayout;
251 
252  QPushButton* pb_load = us_pushbutton( tr( "Load Model File") );
253  connect( pb_load, SIGNAL( clicked() ), SLOT( load_model()) );
254  buttonbox->addWidget( pb_load );
255 
256  QPushButton* pb_save = us_pushbutton( tr( "Save Model") );
257  connect( pb_save, SIGNAL( clicked() ), SLOT( save_model()) );
258  buttonbox->addWidget( pb_save );
259 
260  QPushButton* pb_help = us_pushbutton( tr( "Help") );
261  connect( pb_help, SIGNAL( clicked() ), SLOT( help()) );
262  buttonbox->addWidget( pb_help );
263 
264  QPushButton* pb_close = us_pushbutton( tr( "Cancel") );
265  buttonbox->addWidget( pb_close );
266  connect( pb_close, SIGNAL( clicked() ), SLOT( reject()) );
267 
268  QPushButton* pb_accept = us_pushbutton( tr( "Accept Model") );
269  buttonbox->addWidget( pb_accept );
270  connect( pb_accept, SIGNAL( clicked() ), SLOT( accept_model()) );
271 
272  main->addLayout( buttonbox, row++, 0, 1, 4 );
273 }
274 
276 {
277  component = index;
278 
279  sb_count->disconnect();
280  sb_count->setValue( index + 1 );
281  connect( sb_count, SIGNAL( valueChanged( int ) ),
282  SLOT ( change_spinbox( int ) ) );
284 }
285 
287 {
288  component = value - 1;
289 
290  cmb_component1->disconnect();
291  cmb_component1->setCurrentIndex( component );
292 
293  connect( cmb_component1, SIGNAL( currentIndexChanged( int ) ),
294  SLOT ( change_component1 ( int ) ) );
295 
297 }
298 
300 {
301  rb_prolate->setEnabled( false );
302  rb_oblate ->setEnabled( false );
303  rb_rod ->setEnabled( false );
304  rb_sphere ->setEnabled( false );
305 
306  // Convenience
307  struct SimulationComponent* sc = &model.component_vector[ component ];
308  vector< struct SimulationComponent >* scl = &model.component_vector;
309 
310  le_sed ->setText( QString::number( sc->s , 'e', 4 ) );
311  le_diff ->setText( QString::number( sc->D , 'e', 4 ) );
312  le_extinction->setText( QString::number( sc->extinction, 'e', 4 ) );
313  le_vbar ->setText( QString::number( sc->vbar20 , 'e', 4 ) );
314  le_mw ->setText( QString::number( sc->mw , 'e', 4 ) );
315  le_f_f0 ->setText( QString::number( sc->f_f0 , 'e', 4 ) );
316 
317  if ( sc->shape == "sphere" ) rb_sphere ->setDown( true );
318  else if ( sc->shape == "prolate" ) rb_prolate ->setDown( true );
319  else if ( sc->shape == "oblate" ) rb_oblate ->setDown( true );
320  else if ( sc->shape == "rod" ) rb_rod ->setDown( true );
321 
322  // Find the associated components for component1 and enter them into the
323  // linked component list:
324 
325  cmb_component2->clear();
326 
327  for ( uint i = 0; i < sc->show_component.size(); i++ )
328  cmb_component2->addItem( (*scl) [ sc->show_component[ i ] ].name );
329 
330  if ( sc->show_conc )
331  {
332  le_conc ->setEnabled( true );
333  le_mw ->setEnabled( true );
334  le_vbar ->setEnabled( true );
335  pb_load_c0->setEnabled( true );
336  pb_vbar ->setEnabled( true );
337 
338  if ( sc->concentration == -1.0 )
339  {
340  le_conc->setText( "from file" );
341  le_c0 ->setText( c0_file );
342  }
343  else
344  {
345  le_conc->setText( QString::number( sc->concentration, 'e', 4 ) );
346  sc->c0.radius.clear();
347  sc->c0.concentration.clear();
348  }
349  }
350  else
351  {
352  // Can't edit an associated species' mw or vbar
353  le_mw ->setEnabled( false );
354 
355  le_vbar ->setEnabled( false );
356  pb_vbar ->setEnabled( false );
357 
358  le_conc ->setEnabled( false );
359  le_conc ->setText ( "" );
360 
361  pb_load_c0->setEnabled( false );
362  le_c0 ->setText ( "" );
363  }
364 
365  // Convenience
366  vector<struct Association>* assoc = &model.assoc_vector;
367 
368  if ( sc->show_keq )
369  {
370  for ( uint i = 0; i < assoc->size(); i++ )
371  { // only check the dissociating species
372  if ( (*assoc)[ i ].component2 == component
373  || (*assoc)[ i ].component3 == component )
374  {
375  le_keq ->setText( QString::number( (*assoc)[ i ].keq, 'e', 4 ) );
376  le_keq ->setEnabled( true );
377 
378  le_koff->setText( QString::number( (*assoc)[ i ].k_off, 'e', 4 ) );
379  le_koff->setEnabled( true );
380  }
381  }
382  }
383  else
384  {
385  le_keq->setEnabled ( false );
386  le_keq->setText ( "" );
387 
388  le_koff->setEnabled( false );
389  le_koff->setText ( "" );
390  }
391 
392  if ( sc->show_stoich > 0 )
393  {
394  // This species is the dissociating species in a self-associating system
395 
396  // le_stoich->setEnabled( true );
397  le_stoich->setText( QString::number( sc->show_stoich ) );
398  sc->mw =
399  (*scl) [ sc->show_component[ 0 ] ].mw * sc->show_stoich;
400 
401 
402  le_mw->setText( QString::number( sc->mw, 'e', 4 ) );
403 
404 
405  sc->vbar20 =
406  (*scl) [ sc->show_component[ 0 ] ].vbar20;
407 
408  le_vbar->setText( QString::number( sc->vbar20, 'e', 4 ) );
409 
410  update_sD();
411  }
412  else if ( sc->show_stoich == -1 )
413  {
414  // This species is the dissociating species in a 2-component
415  // hetero-associating system
416 
417  le_stoich->setText( "hetero-associating" );
418 
419  sc->mw = (*scl) [ sc->show_component[ 0 ] ].mw +
420  (*scl) [ sc->show_component[ 1 ] ].mw;
421 
422  le_mw->setText( QString::number( sc->mw, 'e', 4 ) );
423 
424  double fraction1 = (*scl)[ sc->show_component[ 0 ] ].mw / sc->mw;
425  double fraction2 = (*scl)[ sc->show_component[ 1 ] ].mw / sc->mw;
426 
427  sc->vbar20 = (*scl) [ sc->show_component[ 0 ] ].vbar20 * fraction1 +
428  (*scl) [ sc->show_component[ 1 ] ].vbar20 * fraction2;
429 
430  le_vbar->setText( QString::number( sc->vbar20, 'e', 4 ) );
431 
432  update_sD();
433  }
434  else
435  {
436  le_stoich->setText ( "" );
437  le_stoich->setEnabled( false );
438  }
439 }
440 
442 {
443  // Convenience
444  struct SimulationComponent* sc = &model.component_vector[ component ];
445 
446  double base = 0.75 / AVOGADRO * sc->mw * sc->vbar20 * M_PI * M_PI;
447 
448  sc->s = sc->mw * ( 1.0 - sc->vbar20 * DENS_20W )
449  / ( AVOGADRO * sc->f_f0 * 6.0 * VISC_20W * pow( base, 1.0 / 3.0 ) );
450 
451  base = 2.0 * sc->s * sc->f_f0 * sc->vbar20 * VISC_20W
452  / ( 1.0 - sc->vbar20 * DENS_20W );
453 
454  sc->D = R * K20
455  / ( AVOGADRO * sc->f_f0 * 9.0 * VISC_20W * M_PI * pow( base, 0.5 ) );
456 
457  le_sed ->setText( QString::number( sc->s, 'e', 4 ) );
458  le_diff->setText( QString::number( sc->D, 'e', 4 ) );
459 }
460 
462 {
463  US_AnalyteGui* vbar_dlg = new US_AnalyteGui( -1, true );
464  connect( vbar_dlg, SIGNAL( valueChanged( double ) ),
465  SLOT ( update_vbar ( double ) ) );
466  vbar_dlg->exec();
467 }
468 
469 void US_ModelEditor::update_vbar( double vbar )
470 {
471  if ( vbar > 0.0 ) le_vbar->setText( QString::number( vbar, 'f', 4 ) );
472 }
473 
475 {
476  US_Predict1* hydro = new US_Predict1( simcomp );
477  connect( hydro, SIGNAL( changed() ), SLOT( update_shape() ) );
478  hydro->exec();
479 
480  rb_prolate->setEnabled( true );
481  rb_oblate ->setEnabled( true );
482  rb_rod ->setEnabled( true );
483  rb_sphere ->setEnabled( true );
484 }
485 
486 void US_ModelEditor::select_shape( int new_shape )
487 {
488  shape = new_shape;
489  update_shape();
490 }
491 
493 {
494  struct SimulationComponent* sc = &model.component_vector[ component ];
495 
496  switch ( shape )
497  {
498  case PROLATE:
499  sc->s = simcomp.prolate.s;
500  sc->D = simcomp.prolate.D;
501  sc->f_f0 = simcomp.prolate.f_f0;
502  sc->shape = "prolate";
503  break;
504 
505  case OBLATE:
506  sc->s = simcomp.oblate.s;
507  sc->D = simcomp.oblate.D;
508  sc->f_f0 = simcomp.oblate.f_f0;
509  sc->shape = "oblate";
510  break;
511 
512  case ROD:
513  sc->s = simcomp.rod.s;
514  sc->D = simcomp.rod.D;
515  sc->f_f0 = simcomp.rod.f_f0;
516  sc->shape = "rod";
517  break;
518  case SPHERE:
519 
520  sc->s = simcomp.sphere.s;
521  sc->D = simcomp.sphere.D;
522  sc->f_f0 = simcomp.sphere.f_f0;
523  sc->shape = "sphere";
524  break;
525  }
526 
527  sc->mw = simcomp.mw;
528  sc->vbar20 = simcomp.vbar;
529  sc->density = simcomp.density;
530 
532 
533  rb_prolate->setEnabled( true );
534  rb_oblate ->setEnabled( true );
535  rb_rod ->setEnabled( true );
536  rb_sphere ->setEnabled( true );
537 }
538 
540 {
541  QMessageBox::information( this,
542  tr( "UltraScan Information" ),
543  tr( "Please note:\n\n"
544  "The initial concentration file should have\n"
545  "the following format:\n\n"
546  "radius_value1 concentration_value1\n"
547  "radius_value2 concentration_value2\n"
548  "radius_value3 concentration_value3\n"
549  "etc...\n\n"
550  "radius values smaller than the meniscus or\n"
551  "larger than the bottom of the cell will be\n"
552  "excluded from the concentration vector." ) );
553 
554  QString fn = QFileDialog::getOpenFileName(
555  this, US_Settings::resultDir(), "*" );
556 
557  if ( ! fn.isEmpty() )
558  {
559  le_c0->setText( fn );
560 
561  QFile f( fn );;
562 
563  if ( f.open( QIODevice::ReadOnly | QIODevice::Text ) )
564  {
565  c0_file = fn;
566  QTextStream ts( &f );
567 
568  struct SimulationComponent* sc = &model.component_vector[ component ];
569 
570  sc->c0.radius.clear();
571  sc->c0.concentration.clear();
572 
573  // Sets concentration for this component to -1 to signal that we are
574  // using a concentration vector
575 
576  le_conc->setText( "from file" );
577 
578  double val1;
579  double val2;
580 
581  while ( ! ts.atEnd() )
582  {
583  ts >> val1;
584  ts >> val2;
585 
586  if ( val1 > 0.0 ) // ignore radius pairs that aren't positive
587  {
588  sc->c0.radius .push_back( val1 );
589  sc->c0.concentration .push_back( val2 );
590  }
591  }
592 
593  f.close();
594  }
595  else
596  {
597  QMessageBox::warning( this,
598  tr( "UltraScan Warning" ),
599  tr( "Please note:\n\n"
600  "UltraScan could not open the file specified\n" ) + fn );
601  }
602  }
603 }
604 
606 {
607  QString fn = QFileDialog::getOpenFileName(
608  this,
609  tr( "Load Model File" ),
611  "*.model.?? *.model-?.?? *model-??.??" );
612 
613  if ( ! fn.isEmpty() )
614  {
615  int code = US_FemGlobal::read_modelSystem( model, fn );
616 
617  if ( code == 0 )
618  {
619  // Successfully loaded a new model
620  QMessageBox::information( this,
621  tr( "Simulation Module"),
622  tr( "Successfully loaded Model:" ) );
623 
624  lb_header->setText( US_Constants::modelStrings()[ model.model ] );
625 
626  cmb_component1->disconnect();
627  cmb_component1->clear();
628 
629  vector< struct SimulationComponent >* scl = &model.component_vector;
630 
631  for ( uint i = 0; i< (*scl).size(); i++)
632  {
633  cmb_component1->addItem( ( *scl )[ i ].name );
634  }
635  cmb_component1->setCurrentIndex( 0 );
636  connect( cmb_component1, SIGNAL( currentIndexChanged( int ) ),
637  SLOT ( change_component1 ( int ) ) );
638 
639 
640  component = 0;
641  sb_count->setRange( 1, (*scl).size() );
642  sb_count->setValue( 1 ); // Resets screen
643 
644  //select_component((int) 0);
645  }
646 
647  else if ( code == -40 )
648  {
649  QMessageBox::warning( this,
650  tr( "UltraScan Warning" ),
651  tr( "Please note:\n\n"
652  "UltraScan could not open\n"
653  "the selected Model File!" ) );
654  }
655 
656  else if ( code == 1 )
657  {
658  QMessageBox::warning( this,
659  tr( "UltraScan Warning" ),
660  tr( "Sorry, the old-style models are no longer supported.\n\n"
661  "Please load a different model or create a new Model" ) );
662  }
663 
664  else if ( code < 0 && code > -40 )
665  {
666  QMessageBox::warning( this,
667  tr( "UltraScan Warning" ),
668  tr( "Please note:\n\n"
669  "There was an error reading\n"
670  "the selected Model File!\n\n"
671  "This file appears to be corrupted" ) );
672  }
673  }
674 }
675 
677 {
678  if ( ! verify_model() ) return;
679 
680  QString fn = QFileDialog::getSaveFileName( this,
681  tr( "Save model as" ),
683  "*.model.?? *.model-?.?? *model-??.??" );
684 
685  if ( ! fn.isEmpty() )
686  {
687  int k = fn.lastIndexOf( "." );
688 
689  // If an extension was given, strip it.
690  if ( k != -1 ) fn.resize( k );
691 
692  fn += ".model-" + QString::number( model.model ) + ".11";
693 
694  QFile f( fn );
695 
696  if ( f.exists() )
697  {
698  int answer = QMessageBox::question( this,
699  tr( "Warning" ),
700  tr( "Attention:\n"
701  "This file exists already!\n\n"
702  "Do you want to overwrite it?" ),
703  QMessageBox::Yes, QMessageBox::No );
704 
705  if ( answer == QMessageBox::No ) return;
706  }
707 
708  int result = US_FemGlobal::write_modelSystem( model, fn );
709  if ( result != 0 ) error( tr( "Could not write file." ) );
710  }
711 }
712 
714 {
715  if ( verify_model() ) accept();
716 }
717 
718 // The next five methods are virtually identical. It would be nice
719 // to combine them. It would require a custom class and signal.
720 void US_ModelEditor::update_sed( const QString& text )
721 {
722  if ( text == "" ) return;
723 
724  struct SimulationComponent* sc = &model.component_vector[ component ];
725  sc->s = text.toDouble();
726 }
727 
728 void US_ModelEditor::update_diff( const QString& text )
729 {
730  if ( text == "" ) return;
731 
732  struct SimulationComponent* sc = &model.component_vector[ component ];
733  sc->D = text.toDouble();
734 }
735 
736 void US_ModelEditor::update_extinction( const QString& text )
737 {
738  if ( text == "" ) return;
739 
740  struct SimulationComponent* sc = &model.component_vector[ component ];
741  sc->extinction = text.toDouble();
742 }
743 
744 void US_ModelEditor::update_sigma( const QString& text )
745 {
746  if ( text == "" ) return;
747 
748  struct SimulationComponent* sc = &model.component_vector[ component ];
749  sc->sigma = text.toDouble();
750 }
751 
752 void US_ModelEditor::update_delta( const QString& text )
753 {
754  if ( text == "" ) return;
755 
756  struct SimulationComponent* sc = &model.component_vector[ component ];
757  sc->delta = text.toDouble();
758 }
759 
760 // The next two methods are virtually identical. It would be nice
761 // to combine them.
762 void US_ModelEditor::update_vbar( const QString& text )
763 {
764  struct SimulationComponent* sc = &model.component_vector[ component ];
765  double vbar = text.toDouble();
766 
767  if ( vbar <= 0.0 )
768  {
769  error( tr( "The vbar value must be greater than 0.0" ) );
770  le_vbar->setText( QString::number( sc->vbar20, 'e', 4 ) );
771  return;
772  }
773 
774  sc->vbar20 = vbar;
775 }
776 
777 void US_ModelEditor::update_mw( const QString& text )
778 {
779  struct SimulationComponent* sc = &model.component_vector[ component ];
780  double mw = text.toDouble();
781 
782  if ( mw <= 0.0 )
783  {
784  error( tr( "The Molecular weight must be greater than 0.0" ) );
785  le_mw->setText( QString::number( sc->mw, 'e', 4 ) );
786  return;
787  }
788 
789  sc->mw = mw;
790 }
791 
792 void US_ModelEditor::update_f_f0( const QString& text )
793 {
794  if ( text == "" ) return;
795 
796  struct SimulationComponent* sc = &model.component_vector[ component ];
797  sc->f_f0 = text.toDouble();
798 }
799 
800 void US_ModelEditor::update_conc( const QString& text )
801 {
802  struct SimulationComponent* sc = &model.component_vector[ component ];
803 
804  if ( text == "" ) return;
805 
806  if ( text == "from file")
807  sc->concentration = -1.0;
808  else
809  {
810  bool ok;
811  double concentration;
812  concentration = text.toDouble( &ok );
813  if ( ok )
814  {
815  sc->concentration = concentration;
816  sc->c0.radius.clear();
817  sc->c0.concentration.clear();
818 
819  le_c0->setText( "" );
820  }
821  else
822  error( tr( "The Partial Concentration field is invalid." ) );
823  }
824 }
825 
826 // The next two methods are virtually identical. It would be nice
827 // to combine them
828 void US_ModelEditor::update_keq( const QString& text )
829 {
830  if ( text == "" ) return;
831 
832  vector< struct Association >* av = &model.assoc_vector;
833 
834  // Check to see if the current component is a dissociation component
835 
836  for ( uint i = 0; i < (*av).size(); i++ )
837  {
838  struct Association* as = &model.assoc_vector[ i ];
839 
840  // Check to see if there is an dissociation linked to this component
841  if ( as->component2 == component )
842  {
843  // Check to make sure this component is not an irreversible component
844  if ( as->stoichiometry1 != as->stoichiometry2 // Self-association
845  || ( as->stoichiometry1 == 0
846  && as->stoichiometry2 == 0)) // Hetero-association
847  {
848  as->keq = text.toDouble();
849  }
850  }
851  }
852 }
853 
854 void US_ModelEditor::update_koff( const QString& text )
855 {
856  if ( text == "" ) return;
857 
858  vector< struct Association >* av = &model.assoc_vector;
859 
860  // Check to see if the current component is a dissociation component
861  for ( uint i = 0; i < (*av).size(); i++ )
862  {
863  struct Association* as = &model.assoc_vector[ i ];
864 
865  // Check to see if there is an dissociation linked to this component
866  if ( as->component2 == component )
867  {
868  // Check to make sure this component is not an irreversible component
869  if ( as->stoichiometry1 != as->stoichiometry2 // Self-association
870  || ( as->stoichiometry1 == 0
871  && as->stoichiometry2 == 0)) // Hetero-association
872  {
873  as->k_off = text.toDouble();
874  }
875  }
876  }
877 }
878 
879 void US_ModelEditor::error( const QString& message )
880 {
881  QMessageBox::warning( this, tr( "Model Error" ), message );
882 }
883 
885 {
886  bool flag = true;
887 
888  QString str;
889 
890  vector< struct Association >* av = &model.assoc_vector;
891  vector< struct SimulationComponent >* cv = &model.component_vector;
892 
893  for ( uint i=0; i < (*av).size(); i++ )
894  {
895  struct Association* as = &model.assoc_vector[ i ];
896 
897  // See if we need to check if the MWs match
898  if ( as->stoichiometry2 > 0 && as->stoichiometry3 != 1 )
899  {
900  if ( fabs( (*cv) [ as->component2 ].mw -
901  ( (*cv) [ as->component1 ].mw *
902  as->stoichiometry2 / as->stoichiometry1
903  ) ) > 1.0 ) // MWs don't match within 1 dalton
904  {
905  str = tr( "The molecular weights of the reacting species\n"
906  "in reaction " ) + QString::number( i + 1 ) +
907  tr( "do not agree:\n\n" )
908 
909  + tr( "Molecular weight of species 1: " ) +
910  QString::number( (*cv) [ as->component1 ].mw, 'e', 4 ) + "\n"
911 
912  + tr( "Molecular weight of species 2:" ) +
913  QString::number( (*cv) [ as->component2 ].mw, 'e', 4 ) + "\n"
914 
915  + tr( "Stoichiometry of reaction " ) +
916  QString::number( i + 1 ) + tr( ": MW(1) *" ) +
917  QString::number( as->stoichiometry2 ) + tr( " = MW(2)\n\n" )
918 
919  + tr( "Please adjust either MW(1) or MW(2) before proceeding..." );
920 
921  QMessageBox::warning( this, tr( "Model Definition Error" ), str );
922  flag = false;
923  }
924  }
925 
926  // See if we need to check if the sum of MW(1) + MW(2) = MW(3)
927  if ( as->stoichiometry3 == 1 )
928  {
929  if ( fabs( model.component_vector[ as->component3 ].mw -
930  model.component_vector[ as->component2 ].mw -
931  model.component_vector[ as->component1 ].mw ) > 1.0 )
932  // MWs don't match within 10 dalton
933  {
934  str = tr( "The molecular weights of the reacting species\n"
935  "in reaction ") + QString::number( i + 1 ) +
936  tr( "do not agree:\n\n" )
937 
938  + tr( "Molecular weight of species 1: " ) +
939  QString::number( (*cv) [ as->component1 ].mw, 'e', 4 ) + "\n"
940 
941  + tr( "Molecular weight of species 2: " ) +
942  QString::number( (*cv) [ as->component2 ].mw, 'e', 4 ) + "\n"
943 
944  + tr( "Molecular weight of species 3: " ) +
945  QString::number( (*cv) [ as->component3 ].mw, 'e', 4 ) + "\n"
946 
947  + tr( "Stoichiometry of reaction " ) +
948  QString::number( i + 1 ) + tr( ": MW(1) + MW(2) = MW(3)\n\n" )
949 
950  + tr( "Please adjust the molecular weight of the appropriate\n"
951  "component before proceeding..." );
952 
953  QMessageBox::warning( this, tr( "Model Definition Error" ), str );
954  flag = false;
955  }
956  }
957  }
958 
959  return (flag);
960 }