UltraScan III
2dsa_master.cpp
Go to the documentation of this file.
1 #include "us_mpi_analysis.h"
2 #include "us_math2.h"
3 #include "us_util.h"
4 #include "us_tar.h"
5 #include "us_astfem_rsa.h"
6 #include "us_simparms.h"
7 #include "us_constants.h"
8 
10 {
11  init_solutes();
12  fill_queue();
13 
14  work_rss.resize( gcores_count );
15 
16  current_dataset = 0;
17  datasets_to_process = 1; // Process one dataset at a time for now
18  dset_calc_solutes.clear();
19 
20  int max_iters_all = max_iterations;
21 
22  if ( mc_iterations > 1 )
23  max_iterations = max_iters_all > 1 ? max_iters_all : 5;
24 
25  while ( true )
26  {
27  int worker;
28  meniscus_value = meniscus_values.size() == 1 ?
29  data_sets[ current_dataset ]->run_data.meniscus :
31 //if ( max_depth > 1 )
32 // DbgLv(1) << " master loop-TOP: jq-empty?" << job_queue.isEmpty() << " areReady?" << worker_status.contains(READY)
33 // << " areWorking?" << worker_status.contains(WORKING);
34 
35  // Give the jobs to the workers
36  while ( ! job_queue.isEmpty() && worker_status.contains( READY ) )
37  {
38  worker = ready_worker();
39 
40  Sa_Job job = job_queue.takeFirst();
41  submit( job, worker );
42  worker_depth [ worker ] = job.mpi_job.depth;
43  worker_status[ worker ] = WORKING;
44  }
45 
46  // All done with the pass if no jobs are ready or running
47  if ( job_queue.isEmpty() && ! worker_status.contains( WORKING ) )
48  {
49  US_DataIO::EditedData* edata = &data_sets[ current_dataset ]->run_data;
50  QString tripleID = edata->cell + edata->channel + edata->wavelength;
51  QString progress =
52  "Iteration: " + QString::number( iterations );
53 
54  if ( datasets_to_process > 1 )
55  progress += "; Datasets: "
56  + QString::number( datasets_to_process );
57  else
58  progress += "; Dataset: "
59  + QString::number( current_dataset + 1 )
60  + " (" + tripleID + ") of "
61  + QString::number( count_datasets );
62 
63  if ( mc_iterations > 1 )
64  progress += "; MonteCarlo: "
65  + QString::number( mc_iteration + 1 );
66 
67  else if ( meniscus_values.size() > 1 )
68  progress += "; Meniscus: "
69  + QString::number( meniscus_value, 'f', 3 )
70  + tr( " (%1 of %2)" ).arg( meniscus_run + 1 )
71  .arg( meniscus_values.size() );
72  else
73  progress += "; RMSD: "
74  + QString::number( sqrt( simulation_values.variance ) );
75 
76  send_udp( progress );
77 
78  // Iterative refinement
79  if ( max_iterations > 1 )
80  {
81  if ( data_sets.size() > 1 && iterations == 1 )
82  {
83  if ( datasets_to_process == 1 )
84  {
85  qDebug() << " == Refinement Iterations for Dataset"
86  << current_dataset + 1 << "==";
87  }
88  else
89  {
90  qDebug() << " == Refinement Iterations for Datasets 1 to"
91  << datasets_to_process << "==";
92  }
93  }
94  qDebug() << "Iteration:" << iterations << " Variance:"
95  << simulation_values.variance << "RMSD:"
96  << sqrt( simulation_values.variance );
97 
98  iterate();
99  }
100 
101  if ( ! job_queue.isEmpty() ) continue;
102 
103  iterations = 1;
104 DbgLv(1) << " master loop-BOT: dssize" << data_sets.size() << "ds_to_p"
105  << datasets_to_process << "curr_ds" << current_dataset;
107 int ks=edat->scanCount() - 10;
108 int kr=edat->pointCount() - 10;
109 int ss=ks/2;
110 int rr=kr/2;
111 DbgLv(1) << " master loop-BOT: ds" << current_dataset+1 << "data l m h"
112  << edat->value(10,10) << edat->value(ss,rr) << edat->value(ks,kr);
113 
114  // Manage multiple data sets in global fit
115  if ( is_global_fit && datasets_to_process == 1 )
116  {
117  global_fit();
118  }
119 DbgLv(1) << " master loop-BOT: GF job_queue empty" << job_queue.isEmpty();
120 
121  if ( ! job_queue.isEmpty() ) continue;
122 
123  if ( is_global_fit )
124  write_global();
125 
126  else
127  write_output();
128 
129  // Fit meniscus
130  if ( ( meniscus_run + 1 ) < meniscus_values.size() )
131  {
132  set_meniscus();
133  }
134 
135  if ( ! job_queue.isEmpty() ) continue;
136 
137  // Monte Carlo
138  if ( mc_iterations > 1 )
139  { // Recompute final fit to get simulation and residual
140  mc_iteration++;
143 
144  calc_residuals( 0, data_sets.size(), wksim_vals );
145 
146  qDebug() << "Base-Sim RMSD" << sqrt( simulation_values.variance )
147  << " Exp-Sim RMSD" << sqrt( wksim_vals.variance )
148  << " of MC_Iteration" << mc_iteration;
149  max_iterations = max_iters_all;
151 
152  if ( mc_iteration < mc_iterations )
153  {
155 
156  set_monteCarlo();
157  }
158  }
159 
160  if ( ! job_queue.isEmpty() ) continue;
161 
162  if ( is_composite_job )
163  { // Composite job: update outputs in TAR and bump dataset count
164  QString tripleID = QString( data_sets[ current_dataset ]->model
165  .description ).section( ".", -3, -3 );
166  current_dataset++;
168 
169  update_outputs();
170 
171  if ( simulation_values.noisflag == 0 )
172  {
173  DbgLv(0) << my_rank << ": Dataset" << current_dataset
174  << "(" << tripleID << ")"
175  << " : model was output.";
176  }
177  else
178  {
179  DbgLv(0) << my_rank << ": Dataset" << current_dataset
180  << "(" << tripleID << ")"
181  << " : model/noise(s) were output.";
182  }
183 
184 DbgLv(1) << " master loop-BOT: cds kds" << current_dataset << count_datasets;
185  if ( current_dataset < count_datasets )
186  {
187  meniscus_run = 0;
188  iterations = 1;
189  mc_iteration = 0;
190 
191  if ( meniscus_points > 1 )
192  { // Reset the range of fit-meniscus points for this data set
193  US_DataIO::EditedData* edata
194  = &data_sets[ current_dataset ]->run_data;
195  double men_str = edata->meniscus - meniscus_range / 2.0;
196  double men_inc = meniscus_range / ( meniscus_points - 1.0 );
197  double dat_str = edata->radius( 0 );
198  double men_end = men_str + meniscus_range - men_inc;
199  if ( men_end >= dat_str )
200  { // Adjust first meniscus so range remains below data range
201  men_end = dat_str - men_inc / 2.0;
202  men_str = men_end - meniscus_range + men_inc;
203  }
204  for ( int ii = 0; ii < meniscus_points; ii++ )
205  meniscus_values[ ii ] = men_str + men_inc * ii;
206 DbgLv(1) << " master loop-BOT: menpt" << meniscus_points << "mv0 mvn"
207  << meniscus_values[0] << meniscus_values[meniscus_points-1]
208  << "gcores_count" << gcores_count;
209  }
210 
211 // for ( int ii = 1; ii < gcores_count; ii++ )
212 // worker_status[ ii ] = READY;
213 
214  fill_queue();
215 
216  for ( int ii = 1; ii < gcores_count; ii++ )
217  worker_status[ ii ] = READY;
218 DbgLv(1) << " master loop-BOT: wkst1 wkstn" << worker_status[1]
219  << worker_status[gcores_count-1];
220 
221  for ( int ii = 0; ii < calculated_solutes.size(); ii++ )
222  calculated_solutes[ ii ].clear();
223 
224  continue;
225  }
226  }
227 
228  shutdown_all(); // All done
229  break; // Break out of main loop.
230  }
231 
232  // Wait for worker to send a message
233  int sizes[ 4 ];
234  MPI_Status status;
235 
236  MPI_Recv( sizes,
237  4,
238  MPI_INT,
239  MPI_ANY_SOURCE,
240  MPI_ANY_TAG,
242  &status);
243 
244  worker = status.MPI_SOURCE;
245 
246 if ( max_depth > 0 )
247  DbgLv(1) << " master loop-BOTTOM: status TAG" << status.MPI_TAG
248  << MPI_Job::READY << MPI_Job::RESULTS << " source" << status.MPI_SOURCE;
249  switch( status.MPI_TAG )
250  {
251  case MPI_Job::READY: // Ready for work
252  worker_status[ worker ] = READY;
253  break;
254 
255  case MPI_Job::RESULTS: // Return solute data
256  process_results( worker, sizes );
257  work_rss[ worker ] = sizes[ 3 ];
258  break;
259 
260  default: // Should never happen
261  QString msg = "Master 2DSA: Received invalid status " +
262  QString::number( status.MPI_TAG );
263  abort( msg );
264  break;
265  }
266 
267  max_rss();
268  }
269 }
270 
271 // Generate the initial set of solutes
273 {
274  calculated_solutes.clear();
275  orig_solutes.clear();
276  simulation_values.noisflag = parameters[ "tinoise_option" ].toInt() > 0 ?
277  1 : 0;
278  simulation_values.noisflag += parameters[ "rinoise_option" ].toInt() > 0 ?
279  2 : 0;
280  simulation_values.dbg_level = dbg_level;
281  simulation_values.dbg_timing = dbg_timing;
282 DbgLv(0) << "DEBUG_LEVEL" << simulation_values.dbg_level;
283 
284  // Test to see if there is a custom grid model
285  QString model_filename = data_sets[ 0 ]->model_file;
286  double cnstff0 = 0.0;
287 
288  if ( model_filename.isEmpty() )
289  { // If no model file given, calculate initial solutes in a fixed grid
290 
291  double s_min = parameters[ "s_min" ].toDouble() * 1.0e-13;
292  double s_max = parameters[ "s_max" ].toDouble() * 1.0e-13;
293  double ff0_min = parameters[ "ff0_min" ].toDouble();
294  double ff0_max = parameters[ "ff0_max" ].toDouble();
295 
296  int grid_reps = qMax( parameters[ "uniform_grid" ].toInt(), 1 );
297  double s_pts = 60.0;
298  double ff0_pts = 60.0;
299 
300  if ( parameters.contains( "s_grid_points" ) )
301  s_pts = parameters[ "s_grid_points" ].toDouble();
302 
303  else if ( parameters.contains( "s_resolution" ) )
304  s_pts = parameters[ "s_resolution" ].toDouble() * grid_reps;
305 
306  if ( parameters.contains( "ff0_grid_points" ) )
307  ff0_pts = parameters[ "ff0_grid_points" ].toDouble();
308 
309  else if ( parameters.contains( "ff0_resolution" ) )
310  ff0_pts = parameters[ "ff0_resolution" ].toDouble() * grid_reps;
311 
312  int nsstep = (int)( s_pts );
313  int nkstep = (int)( ff0_pts );
314 
315  US_Solute::init_solutes( s_min, s_max, nsstep,
316  ff0_min, ff0_max, nkstep,
317  grid_reps, cnstff0, orig_solutes );
318  }
319 
320  else
321  { // If a model file was given, use it to set the initial solutes
322  US_Model model;
323  QString fn = "../" + model_filename;
324  model.load( fn );
325  int nsubgrid = model.subGrids;
326  int ncomps = model.components.size();
327 DbgLv(1) << "InSol: fn" << fn;
328 DbgLv(1) << "Insol: nsubgrid ncomps" << nsubgrid << ncomps;
329 
330  if ( nsubgrid < 1 )
331  abort( "Custom grid model file has no subgrids", -1 );
332 
333  if ( ( ncomps / nsubgrid ) > 150 )
334  { // Subgrids too large: adjust subgrid count and size
335  nsubgrid = ( ncomps / 100 + 1 ) | 1;
336  model.subGrids = nsubgrid;
337 DbgLv(1) << "Insol: nsubgrid sbsize" << nsubgrid << ( ncomps / nsubgrid );
338  }
339 
340  QVector< US_Solute > solvec;
341 
342  for ( int ii = 0; ii < nsubgrid; ii++ )
343  {
344  solvec.clear();
345 
346  for ( int jj = ii; jj < ncomps; jj += nsubgrid )
347  {
348  double ffval = model.components[ jj ].f_f0;
349  double vbval = model.components[ jj ].vbar20;
350 
351  // Save each solute contained in the custom grid model
352  US_Solute soli( model.components[ jj ].s,
353  ffval,
354  0.0,
355  vbval,
356  model.components[ jj ].D );
357  solvec << soli;
358  }
359 
360  orig_solutes << solvec;
361  }
362  }
363 }
364 
365 // Fill the job queue, using the list of initial solutes
367 {
368  worker_status.resize( gcores_count );
369  worker_depth .resize( gcores_count );
370 
371  worker_status.fill( INIT );
372  worker_depth .fill( 0 );
373  max_depth = 0;
374  worknext = 1;
376 
377  // Put all jobs in the queue
378  job_queue.clear();
379 
380  for ( int i = 0; i < orig_solutes.size(); i++ )
381  {
383  orig_solutes[ i ].size() );
384  Sa_Job job;
385  job.solutes = orig_solutes[ i ];
386  job_queue << job;
387  }
388 }
389 
392 {
393  // To do a global fit across multiple data sets:
394  // 1. Each individual data set must be run
395  // 2. Sum the total concentration of all returned solutes
396  // 3. Divide all experiment concentrations by the total concentration
397  // 4. Send the concentration-equalized data to the workers
398  // 5. Do an additional run against the combined datasets for the baseline
399  // Any additional Monte Carlo iterations will use the adjusted data for
400  // all data sets.
401 
402  double concentration = 0.0;
403 
404  // The first dataset is done automatically.
405  for ( int solute = 0; solute < simulation_values.solutes.size(); solute++ )
406  {
407  concentration += simulation_values.solutes[ solute ].c;
408  }
409 
410  qDebug() << " == Dataset" << current_dataset + 1
411  << "Total Concentration" << concentration << "==";
412 
413  // Point to current dataset
414  US_DataIO::EditedData* edata = &data_sets[ current_dataset ]->run_data;
415 
416  concentrations[ current_dataset ] = concentration;
417  edata->ODlimit /= concentration;
418  int scan_count = edata->scanCount();
419  int radius_points = edata->pointCount();
420  int index = 0;
421  QVector< double > scaled_data( scan_count * radius_points + 1 );
422 double isum=0.0;
423 double dsum=0.0;
424 
425  // Scale the data
426  for ( int ss = 0; ss < scan_count; ss++ )
427  {
428  for ( int rr = 0; rr < radius_points; rr++ )
429  {
430 double ival=edata->value(ss,rr);
431 isum+=ival;
432  double scaled_value = edata->value( ss, rr ) / concentration;
433  scaled_data[ index++ ] = scaled_value;
434  edata->setValue( ss, rr, scaled_value );
435 dsum+=scaled_value;
436  }
437  }
438 
439  scaled_data[ index ] = edata->ODlimit;
440 DbgLv(1) << "ScaledData sum" << dsum << "iSum" << isum << "concen" << concentration;
441 
442  // Send the scaled version of current data to the workers
443  MPI_Job job;
445  job.length = scaled_data.size();
446  job.solution = 1;
447  job.meniscus_value = data_sets[ current_dataset ]->run_data.meniscus;
449  job.dataset_count = 1;
450 
451  // Tell each worker that new data is coming.
452  // Cannot use a broadcast, since the worker is expecting a Send.
453  for ( int worker = 1; worker <= my_workers; worker++ )
454  {
455  MPI_Send( &job,
456  sizeof( MPI_Job ),
457  MPI_BYTE,
458  worker,
460  my_communicator );
461  }
462 
463  // Get everybody synced up
464  MPI_Barrier( my_communicator );
465 
466  MPI_Bcast( scaled_data.data(),
467  scaled_data.size(),
468  MPI_DOUBLE,
470  my_communicator );
471 
472  // Go to the next dataset
473  job_queue.clear();
475  current_dataset++;
476 
478  { // If all datasets have been scaled, do all datasets from now on
480  current_dataset = 0;
481  }
482 
483  else
484  {
485  for ( int ii = 1; ii < gcores_count; ii++ )
486  worker_status[ ii ] = READY;
487  }
488 
489  fill_queue();
490 
491  for ( int ii = 0; ii < calculated_solutes.size(); ii++ )
492  calculated_solutes[ ii ].clear();
493 }
494 
495 // Reset for a fit-meniscus iteration
497 {
498  meniscus_run++;
499 
500  // We incremented meniscus_run above. Just rerun from the beginning.
501  for ( int i = 0; i < orig_solutes.size(); i++ )
502  {
503  Sa_Job job;
504  job.solutes = orig_solutes[ i ];
505 
506  job_queue << job;
507  }
508 
509  worker_depth.fill( 0 );
510  max_depth = 0;
511  for ( int ii = 0; ii < calculated_solutes.size(); ii++ )
512  calculated_solutes[ ii ].clear();
513 }
514 
515 // Reset for a Monte Carlo iteration
517 {
518 DbgLv(1) << "sMC: max_depth" << max_depth << "calcsols size" << calculated_solutes[max_depth].size()
519  << "simvsols size" << simulation_values.solutes.size();
520 
521  // Set up new data modified by a gaussian distribution
522  if ( mc_iteration == 1 )
523  {
524  set_gaussians();
525 
526  sim_data1 = simulation_values.sim_data;
527 
528  double fitrmsd = sqrt( simulation_values.variance );
529  qDebug() << " MC_Iteration 1 Simulation RMSD"
530  << QString::number( fitrmsd, 'f', 7 );
531  }
532 
533  US_DataIO::EditedData* edata = &data_sets[ current_dataset ]->run_data;
534  int ds_points = total_points;
535  int ds_start = 0;
536  int ds_end = count_datasets;
537 
538  if ( is_composite_job )
539  {
540  ds_points = edata->scanCount() * edata->pointCount();
541  ds_start = current_dataset;
542  ds_end = ds_start + datasets_to_process;
543  }
544 DbgLv(1) << "sMC: totpts" << total_points << "mc_iter" << mc_iteration;
545  mc_data.resize( ds_points );
546  int index = 0;
547  int scnx = 0;
548  double varrmsd = 0.0;
549  double varisum = 0.0;
550  double varimin = 1.0;
551  double varimax = -1.0;
552  double datasum = 0.0;
553 
554  // Get a randomized variation of the concentrations
555  // Use a gaussian distribution with the residual as the standard deviation
556  for ( int ee = ds_start; ee < ds_end; ee++ )
557  {
558  edata = &data_sets[ ee ]->run_data;
559  int scan_count = edata->scanCount();
560  int radius_points = edata->pointCount();
561 int indxh=((scan_count/2)*radius_points)+(radius_points/2);
562 
563  for ( int ss = 0; ss < scan_count; ss++, scnx++ )
564  {
565  for ( int rr = 0; rr < radius_points; rr++ )
566  {
567  double variation = US_Math2::box_muller( 0.0, sigmas[ index ] );
568  double mcdata = sim_data1.value( scnx, rr ) + variation;
569  varrmsd += sq( variation );
570  varisum += variation;
571  varimin = qMin( varimin, variation );
572  varimax = qMax( varimax, variation );
573  datasum += mcdata;
574 
575 if ( index<5 || index>(total_points-6) || (index>(indxh-4)&&index<(indxh+3)) )
576 DbgLv(1) << "sMC: index" << index << "sdat" << sim_data1.value(ss,rr)
577  << "sigma" << sigmas[index] << "vari" << variation << "mdat" << mcdata;
578 
579  mc_data[ index++ ] = mcdata;
580  }
581  }
582  }
583 DbgLv(1) << "sMC: mcdata sum" << datasum;
584 
585  varrmsd = sqrt( varrmsd / (double)( ds_points ) );
586  qDebug() << " Box_Muller Variation RMSD"
587  << QString::number( varrmsd, 'f', 7 )
588  << " for MC_Iteration" << mc_iteration + 1;
589 
590 DbgLv(1) << "sMC: variation sum min max" << varisum << varimin << varimax
591  << "mcdata sum" << datasum;
592 
593  // Broadcast Monte Carlo data to all workers
594  MPI_Job newdata;
595  newdata.command = MPI_Job::NEWDATA;
596  newdata.length = ds_points;
597  newdata.solution = mc_iteration + 1;
598  newdata.meniscus_value = data_sets[ 0 ]->run_data.meniscus;
599  newdata.dataset_offset = ds_start;
600  newdata.dataset_count = ds_end - ds_start;
601 
602  // Tell each worker that new data coming
603  // Can't use a broadcast because the worker is expecting a Send
604 DbgLv(1) << "sMC: MPI send my_workers" << my_workers;
605  for ( int worker = 1; worker <= my_workers; worker++ )
606  {
607  MPI_Send( &newdata,
608  sizeof( MPI_Job ),
609  MPI_BYTE,
610  worker,
612  my_communicator );
613  }
614 
615  // Get everybody synced up
616 DbgLv(1) << "sMC: MPI Barrier";
617  MPI_Barrier( my_communicator );
618 
619 DbgLv(1) << "sMC: MPI Bcast";
620  MPI_Bcast( mc_data.data(),
621  ds_points,
622  MPI_DOUBLE,
624  my_communicator );
625 
626  fill_queue();
627 
628  for ( int ii = 0; ii < calculated_solutes.size(); ii++ )
629  calculated_solutes[ ii ].clear();
630 }
631 
633 // Generate the simulated data and calculate the residuals
634 // Use the residuals as the standard deviation for varying the
635 // data in Monte Carlo iterations
637 {
638  US_DataIO::EditedData* edata = &data_sets[ current_dataset ]->run_data;
639  int ds_start = 0;
640  int ds_end = count_datasets;
641  int ds_count = count_datasets;
642 
643  if ( is_composite_job )
644  {
645  ds_start = current_dataset;
646  ds_end = ds_start + datasets_to_process;
647  ds_count = datasets_to_process;
648  }
649 DbgLv(1) << "sGA: calcsols size mxdpth" << calculated_solutes.size() << max_depth;
650 
652 
653 int mm=simulation_values.solutes.size()-1;
654 DbgLv(1) << "sGA: sol0.s solM.s" << simulation_values.solutes[0].s
655  << simulation_values.solutes[mm].s << " M=" << mm;;
656 DbgLv(1) << "sGA: solM.k" << simulation_values.solutes[mm].k;
657 DbgLv(1) << "sGA: solM.c" << simulation_values.solutes[mm].c;
658 edata = &data_sets[ds_start]->run_data;
659 DbgLv(1) << "sGA: edata scans points" << edata->scanCount() << edata->pointCount();
660 
661  calc_residuals( ds_start, ds_count, simulation_values );
662 
663  sigmas.clear();
664  res_data = &simulation_values.residuals;
665  int scnx = 0;
666 DbgLv(1) << "sGA: resids scans points" << res_data->scanCount() << res_data->pointCount();
667 
668  for ( int ee = ds_start; ee < ds_end; ee++ )
669  {
670  edata = &data_sets[ ee ]->run_data;
671  int scan_count = edata->scanCount();
672  int radius_points = edata->pointCount();
673 DbgLv(1) << "sGA: ee" << ee << "scans points" << scan_count << radius_points;
674 
675  // Place the residuals magnitudes into a single vector for convenience
676  for ( int ss = 0; ss < scan_count; ss++, scnx++ )
677  {
678  for ( int rr = 0; rr < radius_points; rr++ )
679  {
680  sigmas << qAbs( res_data->value( scnx, rr ) );
681  }
682  }
683  }
684 int ssz=sigmas.size();
685 DbgLv(1) << "sGA: sigmas size" << ssz << "sigmas[mm]" << sigmas[ssz/2];
686 }
687 
688 // Write model (and maybe noise) output at the end of an iteration
690 {
692 
693  double save_meniscus = meniscus_value;
695  int mxdssz = -1;
696 
697  if ( mdl_type == US_Model::TWODSA || mdl_type == US_Model::GA )
698  {
700  mxdssz = sim.solutes.size();
701  }
702 
703  else if ( mdl_type == US_Model::DMGA )
704  { // Handle DMGA need for dummy solutes
705  QVector< US_Solute > solvec;
706  max_depth = 0;
707  calculated_solutes.clear();
708  calculated_solutes << solvec;
709  sim.solutes = solvec;
710 DbgLv(1) << "MAST: wrout: mdl_type DMGA";
711  }
712 
713  else if ( mdl_type == US_Model::PCSA )
714  { // PCSA: Order model records and pick best model
715  max_depth = 0;
716  qSort( mrecs );
717 //*DEBUG*
718 DbgLv(1) << "MAST: wrout: mdl_type PCSA mrecs size" << mrecs.size();
719 if(dbg_level>0)
720 {
721  for(int jj=0;jj<mrecs.size();jj++)
722  {
723  DbgLv(1) << "M:wo: jj" << jj << "typ tx" << mrecs[jj].ctype << mrecs[jj].taskx << "isz csz"
724  << mrecs[jj].isolutes.size() << mrecs[jj].csolutes.size() << "rmsd" << mrecs[jj].rmsd
725  << "sy ey" << mrecs[jj].str_y << mrecs[jj].end_y
726  << "p1 p2" << mrecs[jj].par1 << mrecs[jj].par2;
727  }
728 }
729 //*DEBUG*
730  sim.zsolutes = mrecs[ 0 ].csolutes;
731  mxdssz = sim.zsolutes.size();
732  sim.ti_noise = mrecs[ 0 ].ti_noise;
733  sim.ri_noise = mrecs[ 0 ].ri_noise;
734  }
735 DbgLv(1) << "WrO: mxdssz" << mxdssz;
736 
737  if ( mxdssz == 0 )
738  { // Handle the case of a zero-solute final model
739  int simssz = simulation_values.zsolutes.size();
740  int dm1ssz = max_depth > 0 ?
741  calculated_solutes[ max_depth - 1 ].size() : 0;
742  DbgLv( 0 ) << "*WARNING* Final solutes size" << mxdssz
743  << "max_depth" << max_depth << "Sim and Depth-1 solutes size"
744  << simssz << dm1ssz;
745  if ( simssz > 0 )
746  { // Use the last result solutes if there are some
747  sim.zsolutes = simulation_values.zsolutes;
748  DbgLv( 0 ) << " SimValues solutes used";
749  }
750  else if ( dm1ssz > 0 )
751  { // Else use the max-depth-minus-1 solutes if there are some
752  //sim.zsolutes = calculated_solutes[ max_depth - 1 ];
753  DbgLv( 0 ) << " CalcValue[mxdepth-1] solutes used";
754  }
755  else
756  { // Else report the bad situation of no solutes available
757  DbgLv( 0 ) << " *ERROR* No solutes will be used";
758  }
759  }
760 
761 DbgLv(1) << "WrO: meniscus_run" << meniscus_run << "mvsz" << meniscus_values.size();
763  ? meniscus_values[ meniscus_run ] : save_meniscus;
764 
765  if ( mdl_type == US_Model::PCSA )
766  {
767 DbgLv(1) << "WrO: qSort solutes sssz" << sim.zsolutes.size();
768  qSort( sim.zsolutes );
769  }
770 
771  else if ( mdl_type != US_Model::DMGA )
772  {
773 DbgLv(1) << "WrO: qSort solutes sssz" << sim.solutes.size();
774  qSort( sim.solutes );
775  }
776 
777 DbgLv(1) << "WrO: wr_model mdl_type" << mdl_type;
778  write_model( sim, mdl_type );
779  meniscus_value = save_meniscus;
780 
781 DbgLv(1) << "WrO: wr_noise";
782  if ( parameters[ "tinoise_option" ].toInt() > 0 )
784 
785  if ( parameters[ "rinoise_option" ].toInt() > 0 )
787 
788 DbgLv(1) << "WrO: wr_mrecs";
789  if ( mdl_type == US_Model::PCSA )
790  { // Output mrecs for PCSA, if we have a final mrecs vector
791  int tikreg = parameters[ "tikreg_option" ].toInt();
792  int mc_iters = parameters[ "mc_iterations" ].toInt();
793 
794  if ( tikreg == 0 && mc_iters < 2 )
795  write_mrecs();
796  }
797 }
798 
799 // Write global model outputs at the end of an iteration
801 {
805 
806  int nsolutes = ( mdl_type != US_Model::DMGA ) ? sim.solutes.size() : -1;
807 
808  if ( nsolutes == 0 )
809  { // Handle the case of a zero-solute final model
810  DbgLv( 0 ) << " *ERROR* No solutes available for global model";
811  return;
812  }
813 
814 DbgLv(1) << "WrGlob: mciter mxdepth" << mc_iteration+1 << max_depth
815  << "calcsols size" << calculated_solutes[max_depth].size()
816  << "simvsols size" << nsolutes;
817 
818  if ( mdl_type == US_Model::TWODSA )
819  {
820  // 2DSA: Recompute the global fit and save A and b matrices for later use
822  US_SolveSim solvesim( data_sets, my_rank, false );
823  solvesim.calc_residuals( 0, data_sets.size(), wksim_vals, false,
824  &gl_nnls_a, &gl_nnls_b );
825 DbgLv(1) << "WrGlob: glob recompute nsols" << wksim_vals.solutes.size()
826  << "globrec A,b sizes" << gl_nnls_a.size() << gl_nnls_b.size();
827 
828  for ( int ee = 0; ee < data_sets.size(); ee++ )
829  {
830  US_DataIO::EditedData* edata = &data_sets[ ee ]->run_data;
831  current_dataset = ee;
832  meniscus_value = edata->meniscus;
833  double concentration = concentrations[ ee ];
834 DbgLv(1) << "WrGlob: currds" << ee << "concen" << concentration;
835 
836  for ( int cc = 0; cc < nsolutes; cc++ )
837  {
838  sim.solutes[ cc ].c = gsim->solutes[ cc ].c * concentration;
839  }
840 
841 DbgLv(1) << "WrGlob: call write_model(1)";
842  // Output the model from global solute points
843  write_model( sim, mdl_type, true );
844 
845  // Grab dataset portion of A and b, then re-fit
846  wksim_vals = sim;
847  wksim_vals.solutes.clear();
848  int kscans = edata->scanCount();
849  int kpoints = edata->pointCount();
850  int narows = kscans * kpoints;
851  int navals = narows * nsolutes;
852  int ksolutes = 0;
853  QVector< double > nnls_a( navals, 0.0 );
854  QVector< double > nnls_b( narows, 0.0 );
855  QVector< double > nnls_x( nsolutes, 0.0 );
856 DbgLv(1) << "WrGlob: ks kp nar nav" << kscans << kpoints << narows << navals;
857 
858  dset_matrices( ee, nsolutes, nnls_a, nnls_b );
859 
860 DbgLv(1) << "WrGlob: mats built; calling NNLS";
861  US_Math2::nnls( nnls_a.data(), narows, narows, nsolutes,
862  nnls_b.data(), nnls_x.data() );
863 
864 DbgLv(1) << "WrGlob: building solutes from nnls_x";
865  for ( int cc = 0; cc < nsolutes; cc++ )
866  {
867  double soluval = nnls_x[ cc ];
868  if ( soluval > 0.0 )
869  {
870  US_Solute solu = sim.solutes[ cc ];
871  solu.c = soluval;
872  wksim_vals.solutes << solu;
873  ksolutes++;
874  }
875  }
876 DbgLv(1) << "WrGlob: currds" << ee << "nsol ksol" << nsolutes << ksolutes;
877 
878  // Output the model refitted to individual dataset
879  write_model( wksim_vals, mdl_type );
880  }
881  }
882 
883  else if ( mdl_type == US_Model::GA )
884  { // GA: Compute and output each dataset model
885  int ksolutes = 0;
886 
887  for ( int ee = 0; ee < data_sets.size(); ee++ )
888  {
890  US_DataIO::EditedData* edata = &data_sets[ ee ]->run_data;
891  current_dataset = ee;
892  meniscus_value = edata->meniscus;
893  US_SolveSim solvesim( data_sets, my_rank, false );
894  solvesim.calc_residuals( ee, 1, wksim_vals, false );
895  ksolutes = wksim_vals.solutes.size();
896 
897  // Output the model fitted to individual dataset
898  write_model( wksim_vals, mdl_type );
899 DbgLv(1) << "WrGlob: currds" << ee << "nsol ksol" << nsolutes << ksolutes;
900  }
901  }
902 
903  else if ( mdl_type == US_Model::DMGA )
904  { // DMGA: Compute and output each dataset model
905  }
906 
907  current_dataset = 0;
908 }
909 
910 // Reset for a refinement iteration
912 {
913  // Just return if the number of iterations exceeds the max
914  // or if the last two iterations converged and are essentially identical
915  if ( ++iterations > max_iterations ) return;
916 
917  double diff = qAbs( simulation_values.variance - previous_values.variance );
918  bool ssame = false;
919 
920  if ( iterations > 2 )
921  {
922  if ( diff < min_variance_improvement ) return;
923 
924  int nsols = previous_values.solutes.size();
925 
926  if ( nsols == simulation_values.solutes.size() )
927  {
928  ssame = true;
929 
930  for ( int jj = 0; jj < nsols; jj++ )
931  {
932  if ( previous_values.solutes[ jj ] != simulation_values.solutes[ jj ] )
933  { // Mismatch: may need to iterate
934  ssame = false;
935  break;
936  }
937  }
938  }
939  }
940 
941  if ( ssame ) return; // Solutes same as previous: no more iterations
942 
943  // Save the most recent variance for the next time
944  previous_values.variance = simulation_values.variance;
945  previous_values.solutes = simulation_values.solutes;
946 
947  // Set up for another round at depth 0
948  Sa_Job job;
953 
954  QVector< US_Solute > prev_solutes = simulation_values.solutes;
955 
956  for ( int i = 0; i < orig_solutes.size(); i++ )
957  {
958  job.solutes = orig_solutes[ i ];
959 
960  // Add back all non-zero Solutes to each job
961  // Ensure there are no duplicates
962  for ( int s = 0; s < prev_solutes.size(); s++ )
963  {
964  if ( ! job.solutes.contains( prev_solutes[ s ] ) )
965  {
966  job.solutes << prev_solutes[ s ];
967  }
968  }
969 
970  job_queue << job;
971 
972  // Bump max solutes per subgrid to new observed max
974  job.solutes.size() );
975  }
976 
977  // Clear depth and calculated-solutes lists
978  worker_depth.fill( 0 );
979  max_depth = 0;
980  for ( int ii = 0; ii < calculated_solutes.size(); ii++ )
981  calculated_solutes[ ii ].clear();
982 
983  return;
984 }
985 
987 // Shutdown the workers
988 // Actually this doesn't necessarily shut them down, it breaks
989 // out of the processing loop.
991 {
992  MPI_Job job;
994 DbgLv(1) << "2dsa master shutdown : master maxrss" << maxrss;
995 
996  for ( int i = 1; i <= my_workers; i++ )
997  {
998  MPI_Send( &job,
999  sizeof( job ),
1000  MPI_BYTE,
1001  i, // Send to each worker
1003  my_communicator );
1004 
1005  maxrss += work_rss[ i ];
1006 DbgLv(1) << "2dsa master shutdown : worker" << i << " upd. maxrss" << maxrss
1007  << " wkrss" << work_rss[ i ];
1008  }
1009 }
1010 
1012 void US_MPI_Analysis::submit( Sa_Job& job, int worker )
1013 {
1015  job.mpi_job.length = job.solutes.size();
1020 int dd=job.mpi_job.depth;
1021 if (dd==0) { DbgLv(1) << "Mast: submit: worker" << worker << " sols"
1022  << job.mpi_job.length << "mciter cds" << mc_iteration << current_dataset << " depth" << dd; }
1023 else { DbgLv(1) << "Mast: submit: worker" << worker << " sols"
1024  << job.mpi_job.length << "mciter cds" << mc_iteration << current_dataset << " depth" << dd; }
1025 DbgLv(1) << "Mast: submit: len sol offs cnt"
1026  << job.mpi_job.length
1027  << job.mpi_job.solution
1028  << job.mpi_job.dataset_offset
1029  << job.mpi_job.dataset_count;
1030 
1031  // Tell worker that solutes are coming
1032  MPI_Send( &job.mpi_job,
1033  sizeof( MPI_Job ),
1034  MPI_BYTE,
1035  worker, // Send to system that needs work
1037  my_communicator );
1038 DbgLv(1) << "Mast: submit: send #1";
1039 
1040  // Send solutes
1041  MPI_Send( job.solutes.data(),
1042  job.mpi_job.length * solute_doubles,
1043  MPI_DOUBLE, // Pass solute vector as hw independent values
1044  worker, // to worker
1046  my_communicator );
1047 DbgLv(1) << "Mast: submit: send #2";
1048 }
1049 
1050 // Add a job to the queue, maintaining depth order
1052 {
1053  int jdepth = job.mpi_job.depth;
1054 
1055  for ( int qq = 0; qq < job_queue.size(); qq++ )
1056  {
1057  if ( jdepth < job_queue[ qq ].mpi_job.depth )
1058  { // Insert this job before any with a greater depth
1059  job_queue.insert( qq, job );
1060  return;
1061  }
1062  }
1063 
1064  // In most circumstances, we just append the job to the end of the queue
1065  job_queue << job;
1066  return;
1067 }
1068 
1069 // Process the results from a just-completed worker task
1071  const int* size )
1072 {
1073  simulation_values.solutes.resize( size[ 0 ] );
1074  simulation_values.variances.resize( datasets_to_process );
1075  simulation_values.ti_noise.resize( size[ 1 ] );
1076  simulation_values.ri_noise.resize( size[ 2 ] );
1077 
1078  max_experiment_size = qMax( max_experiment_size, size[ 0 ] );
1079 
1080  MPI_Status status;
1081 
1082  // Get all simulation_values
1083  MPI_Recv( simulation_values.solutes.data(),
1084  size[ 0 ] * solute_doubles,
1085  MPI_DOUBLE,
1086  worker,
1087  MPI_Job::TAG0,
1089  &status );
1090 
1091  MPI_Recv( &simulation_values.variance,
1092  1,
1093  MPI_DOUBLE,
1094  worker,
1095  MPI_Job::TAG0,
1096  my_communicator,
1097  &status );
1098 
1099  MPI_Recv( simulation_values.variances.data(),
1101  MPI_DOUBLE,
1102  worker,
1103  MPI_Job::TAG0,
1105  &status );
1106 
1107  MPI_Recv( simulation_values.ti_noise.data(),
1108  size[ 1 ],
1109  MPI_DOUBLE,
1110  worker,
1111  MPI_Job::TAG0,
1113  &status );
1114 
1115  MPI_Recv( simulation_values.ri_noise.data(),
1116  size[ 2 ],
1117  MPI_DOUBLE,
1118  worker,
1119  MPI_Job::TAG0,
1121  &status );
1122 
1123  worker_status[ worker ] = INIT;
1124  int depth = worker_depth[ worker ];
1125 
1126 if (depth == 0) { DbgLv(1) << "Mast: process_results: worker" << worker
1127  << " solsize" << size[0] << "depth" << depth; }
1128 else { DbgLv(1) << "Mast: process_results: worker" << worker
1129  << " solsize" << size[0] << "depth" << depth; }
1130  Result result;
1131  result.depth = depth;
1132  result.worker = worker;
1133  result.solutes = simulation_values.solutes;
1134 
1135  int lwdepth = low_working_depth();
1136 
1137  // If there are no cached results and the job result's depth
1138  // is not beyond the low working depth, just process the result solutes
1139  if ( cached_results.size() == 0 && depth <= lwdepth )
1140  {
1141  process_solutes( depth, worker, result.solutes );
1142  }
1143 
1144  // If there are cached results or the job result's depth is below
1145  // the low working depth, then first cache the current result
1146  // in its proper depth-ordered place in the cached list
1147  else
1148  {
1149  cache_result( result );
1150  }
1151 
1152  // Process any previous results that were cached.
1153  // As long as there are cached depth-ordered results and the low on
1154  // the list is less than or equal to the low-working depth;
1155  // each first-on-the-list gets taken off and processed.
1156 
1157  while ( cached_results.size() > 0 &&
1158  cached_results[ 0 ].depth <= lwdepth )
1159  {
1160  result = cached_results.takeFirst();
1161  depth = result.depth;
1162  worker = result.worker;
1163 
1164  process_solutes( depth, worker, result.solutes );
1165  }
1166 }
1167 
1168 // Process the calculated solute vector from a job result
1169 void US_MPI_Analysis::process_solutes( int& depth, int& worker,
1170  QVector< US_Solute >& result_solutes )
1171 {
1172 DbgLv(1) << "Mast: process_solutes: worker" << worker
1173  << " solsize" << result_solutes.size() << "depth" << depth;
1174  int next_depth = depth + 1;
1175  // This loop should only execute, at most, once per result.
1176  while ( calculated_solutes.size() < next_depth )
1177  calculated_solutes << QVector< US_Solute >();
1178 
1179  // How big will our vector be?
1180  int csol_size = calculated_solutes[ depth ].size();
1181  int rsol_size = result_solutes.size();
1182  int new_size = csol_size + rsol_size;
1183 
1184  // Submit with previous solutes if new size would be too big
1185  if ( new_size > max_experiment_size )
1186  {
1187  // Put current solutes on queue at depth + 1
1188  Sa_Job job;
1189  job.solutes = calculated_solutes[ depth ];
1190  job.mpi_job.depth = next_depth;
1193  qSort( job.solutes );
1194  add_to_queue( job );
1195 
1196 DbgLv(1) << "Mast: queue NEW DEPTH sols" << job.solutes.size() << " d="
1197  << job.mpi_job.depth << " newsz mxesz" << new_size << max_experiment_size;
1198  max_depth = qMax( next_depth, max_depth );
1199  calculated_solutes[ depth ].clear();
1200  }
1201 
1202  new_size = rsol_size * 2;
1203 
1204  if ( next_depth > 1 && new_size > max_experiment_size )
1205  { // Adjust max_experiment_size if it is only large enough for one output
1206  max_experiment_size = ( new_size * 11 + 9 ) / 10; // 10% above
1207 DbgLv(1) << "Mast: NEW max_exp_size" << max_experiment_size
1208  << "from new_size rsol_size" << new_size << rsol_size;
1209  }
1210 
1211  // Add in the current results
1212  calculated_solutes[ depth ] += result_solutes;
1213  csol_size = calculated_solutes[ depth ].size();
1214 
1215  // At this point we need to clean up, For each depth
1216  // below the current one, if there is nothing in the queue or working
1217  // for that depth or below and there are calculated solutes left, those
1218  // tasks need to be submitted.
1219 
1220  int dcheck = depth;
1221  if ( depth == 0 && max_depth > 0 ) dcheck = 1;
1222 
1223  for ( int d = 0; d < dcheck; d++ )
1224  {
1225  bool queued = false;
1226  for ( int q = 0; q < job_queue.size(); q++ )
1227  {
1228  if ( job_queue[ q ].mpi_job.depth <= d )
1229  {
1230  queued = true;
1231  break;
1232  }
1233  }
1234 
1235  bool working = false;
1236  for ( int w = 1; w <= my_workers; w++ )
1237  {
1238  if ( worker_depth[ w ] <= d && worker_status[ w ] == WORKING )
1239  {
1240  working = true;
1241  break;
1242  }
1243  }
1244 
1245  int remainder = calculated_solutes[ d ].size();
1246 
1247  if ( ! working && ! queued && remainder > 0 )
1248  { // Submit a job with remaining calculated solutes from an earlier depth
1249  int next_d = d + 1;
1250  Sa_Job job;
1251  job.solutes = calculated_solutes[ d ];
1252  job.mpi_job.depth = next_d;
1255  max_depth = qMax( next_d, max_depth );
1256  qSort( job.solutes );
1257  add_to_queue( job );
1258 DbgLv(1) << "Mast: queue REMAINDER" << remainder << " d=" << d+1;
1259 
1260  calculated_solutes[ d ].clear();
1261  }
1262  }
1263 
1264  // Is anyone working?
1265  bool working = false;
1266  for ( int w = 1; w <= my_workers; w++ )
1267  {
1268  if ( worker_status[ w ] == WORKING )
1269  {
1270  working = true;
1271  break;
1272  }
1273  }
1274 
1275  // Submit one last time with all solutes if necessary.
1276  // This is the case if
1277  // (1) this result is from the maximum submitted-jobs depth;
1278  // (2) no jobs are queued or working; and
1279  // (3) the current set of calculated solutes comes from more
1280  // than one task result.
1281  if ( depth == max_depth &&
1282  job_queue.isEmpty() &&
1283  ! working &&
1284  csol_size > rsol_size )
1285  {
1286  Sa_Job job;
1287  job.solutes = calculated_solutes[ depth ];
1288  job.mpi_job.depth = next_depth;
1289  meniscus_value = ( meniscus_values.size() == 1 )
1290  ? data_sets[ current_dataset ]->run_data.meniscus
1292  qSort( job.solutes );
1293 DbgLv(1) << "Mast: queue LAST ns=" << job.solutes.size() << " d=" << depth+1
1294  << max_depth << " nsvs=" << simulation_values.solutes.size();
1295 
1296  calculated_solutes[ depth ].clear();
1297  csol_size = 0;
1298  max_depth = next_depth;
1299  worker = ready_worker();
1300 
1301  if ( worker > 0 )
1302  { // Submit what should be the last job of this iteration
1303  ljob_solutes = job.solutes;
1304  submit( job, worker );
1305  worker_depth [ worker ] = job.mpi_job.depth;
1306  worker_status[ worker ] = WORKING;
1307  // Insure calculated solutes is empty for final depth
1308  if ( calculated_solutes.size() > max_depth )
1309  calculated_solutes[ max_depth ].clear();
1310  else
1311  calculated_solutes << QVector< US_Solute >();
1312  }
1313  else
1314  { // Shouldn't happen, but put job in queue if no worker is yet ready
1315 DbgLv(1) << "Mast: WARNING: LAST depth and no worker ready!";
1316  job_queue << job;
1317  }
1318  }
1319 
1320  // Force an abort if we are in a run-away situation
1321  if ( max_depth > 20 )
1322  {
1323  abort( "Max Depth is exceeding 20" );
1324  calculated_solutes[ depth + 20 ].clear();
1325  }
1326 
1327 }
1328 
1329 // Write model output at the end of an iteration
1332  bool glob_sols )
1333 {
1334  US_DataIO::EditedData* edata = &data_sets[ current_dataset ]->run_data;
1335 
1336  // Fill in and write out the model file
1337  US_Model model;
1338  int subtype = ( type == US_Model::PCSA ) ? mrecs[ 0 ].ctype : 0;
1339 
1340 DbgLv(1) << "wrMo: type" << type << "(DMGA=" << US_Model::DMGA << ") (PCSA="
1341  << US_Model::PCSA << ") subtype=" << subtype;
1342  if ( type == US_Model::DMGA )
1343  { // For discrete GA, get the already constructed model
1344  model = data_sets[ 0 ]->model;
1345 DbgLv(1) << "wrMo: model comps" << model.components.size();
1346  }
1347 
1348  int mc_iter = ( mgroup_count < 2 || is_composite_job )
1349  ? ( mc_iteration + 1 ) : mc_iteration;
1350  model.monteCarlo = mc_iterations > 1;
1351  model.wavelength = edata->wavelength.toDouble();
1352  model.modelGUID = ( ! model.monteCarlo || mc_iter == 1 )
1354 DbgLv(1) << "wrMo: mc mciter mGUID" << model.monteCarlo << mc_iter
1355  << model.modelGUID;
1356  model.editGUID = edata->editGUID;
1357  model.requestGUID = requestGUID;
1358  model.dataDescrip = edata->description;
1359  //model.optics = ??? How to get this? Is is needed?
1360  model.analysis = type;
1361  QString runID = edata->runID;
1362 
1363  if ( meniscus_points > 1 )
1364  model.global = US_Model::MENISCUS;
1365 
1366  else if ( is_global_fit )
1367  {
1368  model.global = US_Model::GLOBAL;
1369  if ( glob_sols )
1370  runID = "Global-" + runID;
1371  model.modelGUID = US_Util::new_guid();
1372  modelGUID = model.modelGUID;
1373  }
1374 
1375  else
1376  model.global = US_Model::NONE;
1377 DbgLv(1) << "wrMo: is_glob glob_sols" << is_global_fit << glob_sols;
1378 
1379  model.meniscus = meniscus_value;
1380  model.variance = sim.variance;
1381 
1382  // demo1_veloc. 1A999. e201101171200_a201101171400_2DSA us3-0000003 .model
1383  // demo1_veloc. 1A999. e201101171200_a201101171400_2DSA us3-0000003 .ri_noise
1384  // demo1.veloc. 1A999. e201101171200_a201101171400_2DSA_us3-0000003_i01-m62345.ri_noise
1385  // demo1_veloc. 1A999. e201101171200_a201101171400_2DSA_us3-0000003_mc001 .model
1386  // runID.tripleID.analysisID.recordType
1387  // analysisID = editID_analysisDate_analysisType_requestID_iterID (underscores)
1388  // editID:
1389  // requestID: from lims or 'local'
1390  // analysisType : 2DSA GA others
1391  // iterID: 'i01-m62345' for meniscus, mc001 for monte carlo, i01 default
1392  //
1393  // recordType: ri_noise, ti_noise, model
1394 
1395  QString tripleID = edata->cell + edata->channel + edata->wavelength;
1396  QString dates = "e" + edata->editID + "_a" + analysisDate;
1397 DbgLv(1) << "wrMo: tripleID" << tripleID << "dates" << dates;
1398  QString iterID;
1399 
1400  if ( mc_iterations > 1 )
1401  iterID.sprintf( "mc%04d", mc_iter );
1402  else if ( meniscus_points > 1 )
1403  iterID.sprintf( "i%02d-m%05d",
1404  meniscus_run + 1,
1405  (int)(meniscus_value * 10000 ) );
1406  else
1407  iterID = "i01";
1408 
1409  QString mdlid = tripleID + "." + iterID;
1410  QString id = model.typeText( subtype );
1411  if ( analysis_type.contains( "CG" ) )
1412  id = id.replace( "2DSA", "2DSA-CG" );
1413  QString analyID = dates + "_" + id + "_" + requestID + "_" + iterID;
1414  int stype = data_sets[ current_dataset ]->solute_type;
1415  double vbar20 = data_sets[ current_dataset ]->vbar20;
1416 
1417  model.description = runID + "." + tripleID + "." + analyID + ".model";
1418 DbgLv(1) << "wrMo: model descr" << model.description;
1419 
1420  // Save as class variable for later reference
1421  modelGUID = model.modelGUID;
1422 
1423  if ( type == US_Model::PCSA )
1424  { // For PCSA, construct the model from zsolutes
1425  for ( int ii = 0; ii < sim.zsolutes.size(); ii++ )
1426  {
1427  US_ZSolute zsolute = sim.zsolutes[ ii ];
1428 
1430  US_ZSolute::set_mcomp_values( component, zsolute, stype, true );
1431  component.name = QString().sprintf( "SC%04d", ii + 1 );
1432 
1433  US_Model::calc_coefficients( component );
1434  model.components << component;
1435  }
1436  }
1437 
1438  else if ( type != US_Model::DMGA )
1439  { // For other non-DMGA, construct the model from solutes
1440  for ( int ii = 0; ii < sim.solutes.size(); ii++ )
1441  {
1442  const US_Solute* solute = &sim.solutes[ ii ];
1443 
1445  component.s = solute->s;
1446  component.f_f0 = solute->k;
1447  component.name = QString().sprintf( "SC%04d", ii + 1 );
1448  component.vbar20 = (attr_z == ATTR_V) ? vbar20 : solute->v;
1449  component.signal_concentration = solute->c;
1450 
1451  US_Model::calc_coefficients( component );
1452  model.components << component;
1453  }
1454  }
1455 DbgLv(1) << "wrMo: stype" << stype << QString().sprintf("0%o",stype)
1456  << "attr_z vbar20 mco0.v" << attr_z << vbar20 << model.components[0].vbar20;
1457 
1458  QString fext = model.monteCarlo ? ".mdl.tmp" : ".model.xml";
1459  QString fileid = "." + id + "." + mdlid + fext;
1460  QString fn = runID + fileid;
1461  int lenfn = fn.length();
1462 
1463  if ( lenfn > 99 )
1464  { // Insure a model file name less than 100 characters in length (tar limit)
1465  int lenri = runID.length() + 99 - lenfn;
1466  fn = runID.left( lenri ) + fileid;
1467  }
1468 
1469  // Output the model to a file
1470  model.write( fn );
1471 
1472  // Save the model in case needed for noise
1473  data_sets[ current_dataset ]->model = model;
1474 
1475  // Add the file name of the model file to the output list
1476  QFile fileo( "analysis_files.txt" );
1477 
1478  if ( ! fileo.open( QIODevice::WriteOnly | QIODevice::Text
1479  | QIODevice::Append ) )
1480  {
1481  abort( "Could not open 'analysis_files.txt' for writing" );
1482  return;
1483  }
1484 
1485  QTextStream tsout( &fileo );
1486 
1487  QString meniscus = QString::number( meniscus_value, 'e', 4 );
1488  QString variance = QString::number( sim.variance, 'e', 4 );
1489 
1490  int run = 1;
1491 
1492  if ( meniscus_run > 0 )
1493  run = meniscus_run + 1;
1494  else if ( mc_iterations > 0 )
1495  run = mc_iter;
1496 
1497  QString runstring = "Run: " + QString::number( run ) + " " + tripleID;
1498 
1499  tsout << fn << ";meniscus_value=" << meniscus_value
1500  << ";MC_iteration=" << mc_iter
1501  << ";variance=" << sim.variance
1502  << ";run=" << runstring
1503  << "\n";
1504  fileo.close();
1505 }
1506 
1507 // Write noise output at the end of an iteration
1509  const QVector< double >& noise_data )
1510 {
1511  US_DataIO::EditedData* data = &data_sets[ current_dataset ]->run_data;
1512 
1513  QString type_name;
1514  US_Noise noise;
1515 
1516  if ( type == US_Noise::TI )
1517  {
1518  type_name = "ti";
1519  int radii = data->pointCount();
1520  noise.minradius = data->radius( 0 );
1521  noise.maxradius = data->radius( radii - 1 );
1522  }
1523  else
1524  {
1525  type_name = "ri";
1526  }
1527 
1528  // demo1_veloc.1A999.e201101171200_a201101171400_2DSA us3-0000003
1529  // _i01
1530  // _mc001
1531  // _i01-m62345.ri_noise
1532  // .model
1533  // .ri_noise
1534  // runID.tripleID.analysID.recordType
1535  // analysID = editID_analysisDate_analysisType_requestID_iterID
1536  // editID:
1537  // requestID: from lims or 'local'
1538  // analysisType : 2DSA GA others
1539  // iterID: 'i01-m62345' for meniscus,
1540  // 'mc001' for monte carlo, 'i01' default
1541  //
1542  // recordType: ri_noise, ti_noise, model
1543 
1544  QString tripleID = data->cell + data->channel + data->wavelength;
1545  QString dates = "e" + data->editID + "_a" + analysisDate;
1546  QString anType = "_" + data_sets[ current_dataset ]->model.typeText()
1547  + "_";
1548  if ( analysis_type.contains( "CG" ) )
1549  anType = anType.replace( "2DSA", "2DSA-CG" );
1550 
1551  QString iterID;
1552 
1553  if ( mc_iterations > 1 ) // MonteCarlo iteration
1554  iterID.sprintf( "mc%04d", mc_iteration + 1 );
1555 
1556  else if ( meniscus_points > 1 ) // Fit meniscus
1557  iterID.sprintf( "i%02d-m%05d", meniscus_run + 1,
1558  (int)(meniscus_values[ meniscus_run ] * 10000 ) );
1559 
1560  else // Non-iterative single
1561  iterID = "i01";
1562 
1563  QString analysID = dates + anType + requestID + "_" + iterID;
1564 
1565  noise.description = data->runID + "." + tripleID + "." + analysID
1566  + "." + type_name + "_noise";
1567 
1568  noise.type = type;
1569  noise.noiseGUID = US_Util::new_guid();
1570  noise.modelGUID = modelGUID;
1571  noise.values = noise_data;
1572  noise.count = noise_data.size();
1573 
1574  // Add in input noise for associated noise type
1575  // We are not checking for errors here, because that was checked when
1576  // the input noise was applied.
1577 
1578  US_Noise input_noise;
1579  QList< QString > noise_filenames = data_sets[ current_dataset ]->noise_files;
1580 
1581  for ( int j = 0; j < noise_filenames.size(); j++ )
1582  {
1583  QString fn = "../" + noise_filenames[ j ];
1584  input_noise.load( fn );
1585  if ( input_noise.type == type ) noise.sum_noise( input_noise );
1586  }
1587 
1588  QString fn = type_name + ".noise." + noise.noiseGUID + ".xml";
1589  noise.write( fn );
1590 
1591  // Add the file name of the noise file to the output list
1592  QFile f( "analysis_files.txt" );
1593  if ( ! f.open( QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append ) )
1594  {
1595  abort( "Could not open 'analysis_files.txt' for writing", -1 );
1596  return;
1597  }
1598 
1599  QTextStream out( &f );
1600  out << fn << "\n";
1601  f.close();
1602 }
1603 
1605 {
1606  // Find next ready worker by searching status array
1607  int worker = worker_status.indexOf( READY, worknext );
1608 int w1=worker;
1609 int w1n=worknext;
1610  worker = ( worker > 0 ) ? worker :
1611  worker_status.indexOf( READY, 1 );
1612 
1613  // Set index to start with on next search
1614  worknext = ( worker > 0 ) ? ( worker + 1 ) : 1;
1615  worknext = ( worknext > my_workers ) ? 1 : worknext;
1616 DbgLv(1) << "ready_worker w1 w1n worker worknext" << w1 << w1n << worker << worknext;
1617 if(w1<0)
1618 DbgLv(1) << "ready_worker w1234...wmn"
1619  << worker_status[1] << worker_status[2] << worker_status[3] << worker_status[4]
1621 
1622  return worker;
1623 }
1624 
1625 // Find the lowest depth among working jobs
1627 {
1628  int depth = 99; // Default to a depth higher than any reasonable one
1629 
1630  for ( int ii = 1; ii <= my_workers; ii++ )
1631  { // Test all worker statuses and depths
1632  int wdepth = worker_depth[ ii ];
1633 
1634  if ( worker_status[ ii ] == WORKING &&
1635  wdepth < depth )
1636  { // If working and low depth so far, save depth
1637  depth = wdepth;
1638  }
1639  }
1640 
1641  return depth;
1642 }
1643 
1644 // Cache a job result in a depth-ordered list
1646 {
1647  int rdepth = result.depth;
1648 
1649  for ( int ii = 0; ii < cached_results.size(); ii++ )
1650  { // Examine all cached results
1651  int cdepth = cached_results[ ii ].depth;
1652 
1653  if ( rdepth < cdepth )
1654  { // Insert new result before next highest depth
1655  cached_results.insert( ii, result );
1656  return;
1657  }
1658  }
1659 
1660  // If no higher depth cached, append new result to the end
1661  cached_results << result;
1662  return;
1663 }
1664 
1665 // Update the output TAR file after composite job output has been produced
1666 void US_MPI_Analysis::update_outputs( bool is_final )
1667 {
1668  // Get a list of output files, minus any TAR file
1669  QDir odir( "." );
1670  QStringList files = odir.entryList( QStringList( "*" ), QDir::Files );
1671 DbgLv(0) << my_rank << ": UpdOut : final" << is_final
1672  << "files size" << files.size();
1673 
1674  if ( files.size() == 1 && files[ 0 ] == "analysis-results.tar" )
1675  { // If the tar file exists alone, do nothing here
1676 DbgLv(0) << my_rank << ": A single output file, the archive, already exists!!!";
1677  return;
1678  }
1679 
1680  // Otherwise, remove the tar file from the list of output files
1681  files.removeOne( "analysis-results.tar" );
1682 
1683  // Sort file list
1684  files.sort();
1685 
1686  // For Monte Carlo, replace component temporary model files with
1687  // concatenated model files
1688  if ( mc_iterations > 1 )
1689  {
1690  // Get a list of model files currently present
1691  QStringList mfilt;
1692  mfilt << "*.mdl.tmp" << "*.model.xml";
1693  QStringList mfiles = odir.entryList( mfilt, QDir::Files );
1694  QStringList mtrips;
1695  mfiles.sort();
1696 
1697  // Scan for unique triples
1698  for ( int ii = 0; ii < mfiles.size(); ii++ )
1699  {
1700  QString mftrip = QString( mfiles[ ii ] ).section( ".", 0, -4 );
1701  if ( ! mtrips.contains( mftrip ) )
1702  mtrips << mftrip;
1703  }
1704 
1705  // Get a list of files in the text file
1706  QStringList tfiles;
1707  QFile filet( "analysis_files.txt" );
1708  if ( filet.open( QIODevice::ReadOnly | QIODevice::Text ) )
1709  {
1710  QTextStream tstxt( &filet );
1711  while ( ! tstxt.atEnd() )
1712  {
1713  QString line = tstxt.readLine();
1714  QString fname = line.section( ";", 0, 0 );
1715  tfiles << fname;
1716  }
1717  filet.close();
1718  }
1719 
1720  // Open text file for appending composite file names
1721  QFile fileo( "analysis_files.txt" );
1722  if ( ! fileo.open( QIODevice::WriteOnly | QIODevice::Text
1723  | QIODevice::Append ) )
1724  {
1725  abort( "Could not open 'analysis_files.txt' for writing" );
1726  return;
1727  }
1728  QTextStream tsout( &fileo );
1729 
1730  // For each triple, build or update a composite model file
1731  for ( int ii = 0; ii < mtrips.size(); ii++ )
1732  {
1733  // Build a list of model files relating to this triple
1734  QString mftrip = mtrips[ ii ];
1735  QString tripleID = QString( mftrip ).section( ".", -1, -1 );
1736  QStringList mfilt;
1737  mfilt << mftrip + ".mc0*";
1738  QStringList mtfiles = odir.entryList( mfilt, QDir::Files );
1739 
1740  // Build a composite model file and get its name
1741  QString cmfname = US_Model::composite_mc_file( mtfiles, true );
1742 DbgLv(0) << my_rank << ": ii" << ii << "mftrip" << mftrip << "cmfname" << cmfname;
1743 
1744  if ( cmfname.isEmpty() ) continue;
1745 
1746  // Remove iteration (non-composite) files from the list
1747  for ( int jj = 0; jj < mtfiles.size(); jj++ )
1748  {
1749  if ( mtfiles[ jj ] != cmfname )
1750  {
1751  files.removeOne( mtfiles[ jj ] );
1752 DbgLv(0) << my_rank << ": ii,jj" << ii << jj << "REMOVED from list:" << mtfiles[jj];
1753  }
1754  }
1755 
1756  // Add the composite file name to the list if need be
1757  if ( ! files.contains( cmfname ) )
1758  {
1759  files << cmfname;
1760 DbgLv(0) << my_rank << ": files.size" << files.size() << "after cmfname add";
1761  }
1762 
1763  // Add composite name to text file if need be
1764  int f_iters = QString( cmfname ).section( ".", -3, -3 )
1765  .mid( 3 ).toInt();
1766  if ( ! tfiles.contains( cmfname ) &&
1767  ( is_final || f_iters == mc_iterations ) )
1768  {
1769  US_Model model2;
1770 DbgLv(0) << my_rank << ": model2.load(" << cmfname << ")";
1771  model2.load( cmfname );
1772 DbgLv(0) << my_rank << ": model2.description" << model2.description;
1773  QString runstring = "Run: " + QString::number( ii + 1 )
1774  + " " + tripleID;
1775  tsout << cmfname
1776  << ";meniscus_value=" << model2.meniscus
1777  << ";MC_iteration=" << mc_iterations
1778  << ";variance=" << model2.variance
1779  << ";run=" << runstring << "\n";
1780 
1781  if ( analysis_type.contains( "PCSA" ) )
1782  {
1783  int mrx = mrecs[ 2 ].taskx == mrecs[ 0 ].taskx ? 2 : 1;
1784  mrecs[ mrx ].modelGUID = model2.modelGUID;
1785  }
1786  }
1787  }
1788 
1789  fileo.close();
1790  }
1791 
1792  // Create the archive file containing all outputs
1793  US_Tar tar;
1794  tar.create( "analysis-results.tar", files );
1795 for(int jf=0;jf<files.size();jf++)
1796  DbgLv(0) << my_rank << " tar file" << jf << ":" << files[jf];
1797 
1798  // If this is the final call, remove all but the archive file
1799  if ( is_final )
1800  { // Remove the files we just put into the tar archive
1801 DbgLv(0) << my_rank << ": All output files except the archive are now removed.";
1802  QString file;
1803  foreach( file, files ) odir.remove( file );
1804  }
1805 }
1806 
1807 // Return the model type flag for a given analysis type string
1809 {
1811  m_type = US_Model::TWODSA;
1812  if ( a_type.startsWith( "GA" ) )
1813  m_type = US_Model::GA;
1814  else if ( a_type.startsWith( "DMGA" ) )
1815  m_type = US_Model::DMGA;
1816  else if ( a_type.startsWith( "PCSA" ) )
1817  m_type = US_Model::PCSA;
1818 
1819  return m_type;
1820 }
1821