Layers
Diffusion in heterogeneous environments
fit-layer.c
Go to the documentation of this file.
1 
44 // includes
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <getopt.h>
48 #include <string.h>
49 #include <strings.h>
50 #include <time.h>
51 #include <gsl/gsl_multimin.h>
52 #include "header.h"
53 
55 typedef struct {
56  int nt;
57  int nd;
58  int nz;
59  int nr;
60  int iprobe;
61  int jprobe;
62  int iz1;
63  int iz2;
64  int nolayer;
66  double dt;
67  double dr;
68  double sd;
69  double st;
70  double alpha_so;
71  double theta_so;
72  double kappa_so;
73  double alpha_sp;
74  double theta_sp;
75  double kappa_sp;
76  double alpha_sr;
77  double theta_sr;
78  double kappa_sr;
79  double minalpha;
80  double maxalpha;
81  double mintheta;
82  double maxtheta;
83  double minkappa;
84  double maxkappa;
85  double dfree;
86  double *t;
87  double *s;
88  double *invr;
89  double *t_data;
90  double *p_data;
91  double *p;
93 
94 
116 double calc_mse_fit_layer(const gsl_vector *x, void *params)
117 {
118  int i = -1;
119  param_struct_type *p = (param_struct_type *) params;
120  int nt = p->nt;
121  int nd = p->nd;
122  int p_index = -1;
123  double index_scale = -1.;
124 
125 
126  p->alpha_sp = gsl_vector_get(x, 0);
127  p->theta_sp = gsl_vector_get(x, 1);
128  p->kappa_sp = gsl_vector_get(x, 2);
129  if (p->alpha_sp <= 0.001) p->alpha_sp = 0.001;
130  if (p->theta_sp <= 0.001) p->theta_sp = 0.001;
131 
132  if (p->opt_global_kappa) {
133  p->kappa_sr = p->kappa_sp;
134  p->kappa_so = p->kappa_sp;
135  }
136 
138  p->iprobe, p->jprobe, p->iz1, p->iz2,
139  p->nolayer, p->dt, p->dr, p->sd, p->st,
140  p->alpha_so, p->theta_so, p->kappa_so,
141  p->alpha_sp, p->theta_sp, p->kappa_sp,
142  p->alpha_sr, p->theta_sr, p->kappa_sr,
143  p->dfree, p->t, p->s, p->invr, p->p);
144 
145  double mse = 0.;
146 
147  if (nt > nd) {
148  index_scale = (double) nt / (double) nd;
149  for (i=1; i<nd; i++) {
150  p_index = (lround) (i * index_scale);
151  mse += SQR(p->p[p_index] - p->p_data[i]);
152  }
153  mse /= nd;
154  } else {
155  index_scale = (double) nd / (double) nt;
156  for (i=1; i<nt; i++) {
157  p_index = (lround) (i * index_scale);
158  mse += SQR(p->p[i] - p->p_data[p_index]);
159  }
160  mse /= nt;
161  }
162 
163  double penalty_factor = 10.; // Hard-coding this
164 
165  if (p->alpha_sp < p->minalpha)
166  mse += (p->minalpha - p->alpha_sp) * penalty_factor;
167  if (p->alpha_sp > p->maxalpha)
168  mse += (p->alpha_sp - p->maxalpha) * penalty_factor;
169  if (p->theta_sp < p->mintheta)
170  mse += (p->mintheta - p->theta_sp) * penalty_factor;
171  if (p->theta_sp > p->maxtheta)
172  mse += (p->theta_sp - p->maxtheta) * penalty_factor;
173  if (p->kappa_sp < p->minkappa)
174  mse += (p->minkappa - p->kappa_sp) * penalty_factor;
175  if (p->kappa_sp > p->maxkappa)
176  mse += (p->kappa_sp - p->maxkappa) * penalty_factor;
177 
178  return mse;
179 }
180 
181 
183 int main(int argc, char *argv[])
184 {
185  // Input parameters
186  int opt = -1;
187  int opt_index = -1;
188  int opt_help = FALSE;
189  int opt_verbose = FALSE;
190  int opt_pathfile = FALSE;
191  int num_args_left = -1;
192  FILE *file_ptr = NULL;
193  FILE *pathfile_ptr = NULL;
194  char basename[FILENAME_MAX];
195  memset(basename, '\0', FILENAME_MAX);
196  char infilename[FILENAME_MAX];
197  memset(infilename, '\0', FILENAME_MAX);
198  char outfilename[FILENAME_MAX];
199  memset(outfilename, '\0', FILENAME_MAX);
200  char pathfilename[FILENAME_MAX];
201  memset(pathfilename, '\0', FILENAME_MAX);
202  char string[MAX_LINELENGTH];
203  memset(string, '\0', MAX_LINELENGTH);
204  char parameter[MAX_LINELENGTH];
205  memset(parameter, '\0', MAX_LINELENGTH);
206  char value[MAX_LINELENGTH];
207  memset(value, '\0', MAX_LINELENGTH);
208  char junk_char = '\0';
209  int i, j, k, l;
210  i = j = k = -1;
211  int linelength = -1;
212  int found_header_end = FALSE;
213  int found_data_end = FALSE;
214 
215  time_t start_time = -1;
216  time_t end_time = -1;
217  double total_time = -1.;
218  double program_version = 0.2;
219 
220  // struct for holding comments from the input parameter file
221  struct {
222  int n;
224  char command[MAX_COMMAND_LENGTH];
225  } comments;
226  comments.n = 0;
227  memset(comments.command, '\0', MAX_COMMAND_LENGTH);
228 
229  // Geometry
230  double rmax = 1000.0e-6; // Maximum extent in r (m)
231  double zmax = 2000.0e-6; // Maximum extent in z (m)
232  int specified_zmax = FALSE;
233  double lz1 = -1.; // Lower boundary of SP in z direction (m)
234  int specified_lz1 = FALSE; // True if user specifies lz1
235  double lz2 = -1.; // Upper boundary of SP in z direction (m)
236  int specified_lz2 = FALSE; // True if user specifies lz1
237  int iz1 = -1; // z-index of lower boundary of SP
238  int iz2 = -1; // z-index of upper boundary of SP
239  int nolayer = FALSE; // Flag for no layer (homogenous environment)
240 
241  double ez1 = -1.; // Location of lower edge of cylinder
242  int specified_ez1 = FALSE;
243  double ez2 = -1.; // Location of upper edge of cylinder
244  int specified_ez2 = FALSE;
245 
246  // Discretization steps in r, z, and t
247  int nr = 500; // Number of discrete samples in r
248  int nz = 1000; // Number of discrete samples in z
249  int nt = -1; // Number of discrete samples in t
250  // Default determined from von Neumann stability criterion
251 
252  int specified_nt = FALSE;
253  double nt_scale = -1.;
254  int specified_nt_scale = FALSE;
255  int ns = -1;
256  int nds = -1; // Number of time points in delay period before source
257  double dr = -1.;
258  double dz = -1.;
259  double dt = -1.;
260 
261  // Source
262  double trn = 0.35; // Transport number of the source electrode
263  double crnt = 80.0e-9; // Iontophoretic current (A)
264  double sa = -1.; // Source amplitude
265  double tmax = 150.0; // Maximum extent in time (s)
266  double sd = 10.0; // Delay before source begins (s)
267  double st = 50.0; // Source duration (s)
268  double sr = 0.0; // Source r-position (m) (always 0)
269  double sz = -1.; // Source z-position (m) (should be input as 0)
270  /* In previous incarnations of this program, the user could
271  specify the source position, but in this version it is 0
272  (source position defines the origin). Since we're transitioning
273  from one convention to another, the user should input sz
274  explicitly and the value given here won't matter. */
275 
276  int isource, jsource; // z- and r-indices of source position
277  isource = jsource = -1;
278 
279  // Probe
280  double pr = 0.0; // Probe r coordinate
281  double pz = -1.; // Probe z coordinate
282  int specified_pz = FALSE; // True if user specifies pz
283  int iprobe, jprobe; // z- and r-indices of probe position
284  iprobe = jprobe = -1;
285 
286  // ECS parameters -- got defaults from paper -- p. 12 and Table 1
287  // of manuscript submitted in summer 2011
288  int opt_global_kappa = FALSE; // True if user specifies that kappa be the same in all layers
289  double alpha_so = 0.218;
290  double theta_so = 0.447;
291  double kappa_so = 0.007;
292  double alpha_sp = 0.2;
293  double theta_sp = 0.4;
294  double kappa_sp = 0.01;
295  double alpha_sr = 0.218;
296  double theta_sr = 0.447;
297  double kappa_sr = 0.007;
298  double kappa_outside = 0.007; // Used when user specifies kappa outside the SP layer (= kappa_so = kappa_sr)
299  int specified_kappa_outside = FALSE; // True when user specifies
300  // kappa outside the SP layer
301  // dstar = dfree * theta
302  // dstar_max is used in calculating dt from the von Neumann criterion
303  double dstar_so, dstar_sp, dstar_sr, dstar_max;
304  dstar_so = dstar_sp = dstar_sr = dstar_max = -1.;
305 
306  double dfree = 1.24e-09; // Free diffusion coefficient
307 
308  // Arrays
309  double *t = NULL; // Time
310 // double *s = NULL; // Source(z,r)
311  double *p = NULL; // Probe (calculated from 3-layer model)
312  double *alphas = NULL; // alpha(z,r)
313 // double *invr = NULL; // 1/r
314 
315  // Data arrays
316  double *tdata = NULL; // Time (for data values, from input file)
317  double *pdata = NULL; // Data (from input file)
318  int nd; // Number of data points
319 
320  // Parameters to send to calc_mse_fit_layer
321  param_struct_type param_struct;
322 
323  param_struct.nt = -1;
324  param_struct.nd = -1;
325  param_struct.nz = -1;
326  param_struct.nr = -1;
327  param_struct.iprobe = -1;
328  param_struct.jprobe = -1;
329  param_struct.iz1 = -1;
330  param_struct.iz2 = -1;
331  param_struct.nolayer = -1;
332  param_struct.opt_global_kappa = -1;
333 
334  param_struct.dt = -1.;
335  param_struct.dr = -1.;
336  param_struct.sd = -1.;
337  param_struct.st = -1.;
338  param_struct.alpha_so = -1.;
339  param_struct.theta_so = -1.;
340  param_struct.kappa_so = -1.;
341  param_struct.alpha_sp = -1.;
342  param_struct.theta_sp = -1.;
343  param_struct.kappa_sp = -1.;
344  param_struct.alpha_sr = -1.;
345  param_struct.theta_sr = -1.;
346  param_struct.kappa_sr = -1.;
347  param_struct.minalpha = -1.;
348  param_struct.maxalpha = -1.;
349  param_struct.mintheta = -1.;
350  param_struct.maxtheta = -1.;
351  param_struct.minkappa = -1.;
352  param_struct.maxkappa = -1.;
353  param_struct.dfree = -1.;
354 
355  param_struct.t = NULL;
356  param_struct.s = NULL;
357  param_struct.invr = NULL;
358  param_struct.p = NULL;
359  param_struct.t_data = NULL;
360  param_struct.p_data = NULL;
361 
362 
363  // Parameters for curve fitting
364  double minalpha = 0.001; // Add penalty if alpha_sp outside range
365  double maxalpha = 0.25;
366  double mintheta = 0.001; // Add penalty if theta_sp outside range
367  double maxtheta = 0.75;
368  double minkappa = 0.0; // Add penalty if kappa_sp outside range
369  double maxkappa = 0.1;
370  double alpha_fit = -1.; // Value of alpha_sp from fit
371  double theta_fit = -1.; // Value of theta_sp from fit
372  double kappa_fit = -1.; // Value of kappa_sp from fit
373  double mse = -1.; // Mean squared error from fit
374  gsl_vector *steps = NULL; // Step sizes for simplex
375  gsl_vector *simplex = NULL; // Simplex for minimization
376  double alpha_step = 0.1; // Initial step size for alpha_sp
377  double theta_step = 0.2; // Initial step size for theta_sp
378  double kappa_step = 0.002; // Initial step size for kappa_sp
379  // The following are used by the GSL minimization algorithm
380  const gsl_multimin_fminimizer_type *fit_algorithm =
381  gsl_multimin_fminimizer_nmsimplex;
382  gsl_multimin_fminimizer *fit_state = NULL;
383  gsl_multimin_function fit_func;
384  size_t fit_iter = 0; // Counter for iterations of minimization algo.
385  int itermax = 100; // Maximum number of iterations of min. algorithm
386  int fit_status = -1;
387  double fit_size = -1.;
388  double fit_tol = 1.e-4; // Fit tolerance: stopping criterion;
389  // a lower fit_tol gives a more precise
390  // (but not necessarily more accurate) fit
391 
392 
393  // Get start time of program
394  start_time = time(NULL);
395 
396  /* If no arguments are given, or if there is on argument
397  beginning with - (like -h), print the usage statement
398  ((argc == 2) &&(argv[argc-1][0] == '-')))
399 Note: Previously it parsed the command line and then the input file.
400 It used the last argument as the name of the input file.
401 Now it parses the input file first, because that makes it easy for
402 the command-line parameters to override the parameters in the input
403 file (the desired behavior). But now it doesn't handle options that
404 come after the input filename.
405 Currently it just checks if the first character of the last arg is -
406 */
407  if ((argc < 2) || (argv[argc-1][0] == '-'))
408  print_usage_fit_layer(argv[0]);
409 
410 
411 /****************************************************
412  Read the parameters and the data from the input file
413  ****************************************************/
414 
415  /* Get the name of the input file or the basename. Possibilities:
416  User specifies Name of input file Name of output file
417  -------------- ------------------ -------------------
418  <basename> <basename>.txt <basename>.dat
419  <basename>.txt <basename>.txt <basename>.dat
420  Search for the '.' in the input name. If none was found,
421  the basename was specified; append .txt to get the
422  input filename and append .dat to get the output filename.
423  If the '.' was found, the input filename was specified;
424  for the output filename, replace the suffix with ".out". */
425  check_filename(argv[argc-1], infilename);
426  strcpy(outfilename, infilename);
427 
428  char *strng;
429  strng = index(outfilename, '.'); // Find the '.' in the filename
430  if (strng == NULL) { // If no '.', then add the extensions
431  strcat(infilename, ".txt");
432  strcat(outfilename, ".dat");
433  } else {
434  strcpy(strng, ".dat"); // Replace the suffix with ".dat"
435  }
436 
437 
438  // Open file
439  if ((file_ptr = fopen(infilename,"r")) == NULL) {
440  fprintf(stderr, "Error opening input file %s\n", infilename);
441  exit(EXIT_FAILURE);
442  }
443 
444  // Read input parameter file and parse the parameters
445  comments.n = 0; // counter for the comment lines in the parameter file
446  for (i=0; i<MAXNUM_LINES; i++) {
447  if (fgets(string,MAX_LINELENGTH,file_ptr) == NULL)
448  break; // Assume that EOF was found, rather than an error
449 
450  // If the line that was read into 'string' was not EOF
451  // and there was no error, continue; get length of 'string'
452  linelength = strlen(string);
453 
454  // If the line starts with '#', it's a comment;
455  // copy the comment, but don't parse it
456  if (string[0] == '#') {
457  if (comments.n > MAXNUM_COMMENTLINES)
458  fprintf(stderr, "Warning: Maximum # of comment lines "
459  "exceeded.\nWill not copy more comment lines to "
460  "the output file.\n");
461  else
462  strcpy(comments.line[comments.n], string);
463  comments.n++;
464  // If the line is 1 character long, stop reading header
465  } else if (linelength < 3) {
466  found_header_end = TRUE;
467  break;
468  // If the line is too long, just skip it
469  } else if (linelength >= MAX_LINELENGTH-1) {
470  fprintf(stderr, "Warning: Line %d seems to be too long\n", i+1);
471  // Put a null character at end of string and feel good
472  string[MAX_LINELENGTH-1] = '\0';
473  // Ignore the rest of the line
474  if (fscanf(file_ptr, "%*[^\n] %[\n]", &junk_char) == EOF)
475  error("fscanf returned EOF");
476  // Read parameters
477  } else {
478  if (sscanf(string, "%s = %s", parameter, value) == EOF)
479  error("scanf returned EOF");
480  if (STREQ(parameter, "dfree")) {
481  dfree = atof(value);
482  /* Normally dfree = 1.24e-9 or so. However, in some
483  parameter files dfree might be read as 1.24 instead
484  (e.g. if the parameter file has 1.24e_09, or if
485  the user just inputs 1.24). So if the value for
486  dfree is unrealistically high, assume that it
487  needs to be multiplied by 1e-9. */
488  if (dfree > 0.01) dfree *= 1e-9;
489  }
490  if (STREQ(parameter, "trn")) trn = atof(value);
491  if (STREQ(parameter, "current")) {
492  crnt = atof(value);
493  crnt *= 1e-9; // Input in nA; convert to A
494  }
495  if (STREQ(parameter, "delay")) sd = atof(value);
496  if (STREQ(parameter, "duration")) st = atof(value);
497  if (STREQ(parameter, "source_z")) {
498  sz = atof(value);
499  // sz *= 1e-6; // Input in microns; convert to m
500  if (! IS_ZERO(sz)) // The source location should be 0.
501  error("source_z = %f microns but should be 0 "
502  "(or not specified in the output file)", sz);
503  }
504  if (STREQ(parameter, "probe_z")) {
505  pz = atof(value);
506  specified_pz = TRUE;
507  pz *= 1e-6; // Input in microns; convert to m
508  }
509  if (STREQ(parameter, "probe_r")) {
510  pr = atof(value);
511  pr *= 1e-6; // Input in microns; convert to m
512  }
513  if (STREQ(parameter, "nolayer")) nolayer = atoi(value);
514  if (STREQ(parameter, "lz1")) {
515  lz1 = atof(value);
516  specified_lz1 = TRUE;
517  lz1 *= 1e-6; // Input in microns; convert to m
518  }
519  if (STREQ(parameter, "lz2")) {
520  lz2 = atof(value);
521  specified_lz2 = TRUE;
522  lz2 *= 1e-6; // Input in microns; convert to m
523  }
524  if (STREQ(parameter, "ez1")) {
525  ez1 = atof(value);
526  specified_ez1 = TRUE;
527  ez1 *= 1e-6; // Input in microns; convert to m
528  }
529  if (STREQ(parameter, "ez2")) {
530  ez2 = atof(value);
531  specified_ez2 = TRUE;
532  ez2 *= 1e-6; // Input in microns; convert to m
533  }
534  if (STREQ(parameter, "alpha_so")) alpha_so = atof(value);
535  if (STREQ(parameter, "alpha_sr")) alpha_sr = atof(value);
536  if (STREQ(parameter, "theta_so")) theta_so = atof(value);
537  if (STREQ(parameter, "theta_sr")) theta_sr = atof(value);
538  if (STREQ(parameter, "kappa_so")) kappa_so = atof(value);
539  if (STREQ(parameter, "kappa_sr")) kappa_sr = atof(value);
540 /* Removing kappa_outside option from input file
541  - could lead to confusion
542  - not very useful
543  - I probably have not used it in the input file
544  if (STREQ(parameter, "kappa_outside")) {
545  kappa_outside = atof(value);
546  specified_kappa_outside = TRUE;
547  }
548 */
549  if (STREQ(parameter, "nt")) {
550  nt = atoi(value);
551  specified_nt = TRUE;
552  }
553  if (STREQ(parameter, "nt_scale")) {
554  nt_scale = atof(value);
555  specified_nt_scale = TRUE;
556  }
557  if (STREQ(parameter, "nr")) nr = atoi(value);
558  if (STREQ(parameter, "nz")) nz = atoi(value);
559  if (STREQ(parameter, "rmax")) {
560  rmax = atof(value);
561  rmax *= 1e-6; // Input in microns; convert to m
562  }
563  if (STREQ(parameter, "zmax")) {
564  zmax = atof(value);
565  zmax *= 1e-6; // Input in microns; convert to m
566  }
567  if (STREQ(parameter, "tmax")) tmax = atof(value);
568  }
569  }
570 
571  if (!found_header_end)
572  error("Did not find blank line after header");
573 else
574 if (opt_verbose)
575 printf("Found end of header\n");
576 
577  // The next line should also be blank
578  if (fgets(string,MAX_LINELENGTH,file_ptr) == NULL)
579  error("EOF (or error) reached before reading data");
580 
581  linelength = strlen(string);
582  if (linelength > 2)
583  error("The line after the header has %d characters "
584  "(should be 1 or 2)", linelength);
585 
586  // The next line should be the header for the data
587  if (fgets(string,MAX_LINELENGTH,file_ptr) == NULL)
588  error("EOF (or error) reached before reading data");
589 
590 
591  // Read data
592  // Make the data arrays long enough to hold MAXNUM_LINES values
593  tdata = create_array(MAXNUM_LINES, "tdata");
594  pdata = create_array(MAXNUM_LINES, "pdata");
595  if (opt_verbose)
596  printf("Reading data from file\n");
597  for (j=0; j<MAXNUM_LINES; j++)
598  if (fscanf(file_ptr, "%lf%lf%*[^\n] %[\n]",
599  &tdata[j], &pdata[j], &junk_char) == EOF)
600  {
601  found_data_end = TRUE;
602  break; // Stop reading at EOF
603  }
604 
605  if (!found_data_end)
606  error("Read maximum number of lines in output file (%d)\n"
607  "but did not reach end of file", j);
608 else
609 if (opt_verbose)
610 printf("Found end of data\n");
611 
612  nd = j; // nd = Number of data points
613 
614 if (opt_verbose)
615 printf("The number of data points is %d\n", nd);
616 
617  // Close the file
618  fclose(file_ptr);
619 
620 
621 /******************************
622  Parse the command line options
623  ******************************/
624  static struct option long_opts[] = {
625  {"help", no_argument, NULL, 'h'},
626  {"verbose", no_argument, NULL, 'v'},
627  {"global_kappa", no_argument, NULL, 'g'},
628  {"nr", required_argument, NULL, 0},
629  {"nz", required_argument, NULL, 0},
630  {"nt", required_argument, NULL, 0},
631  {"nt_scale", required_argument, NULL, 0},
632  {"ez1", required_argument, NULL, 0},
633  {"ez2", required_argument, NULL, 0},
634  {"alpha_so", required_argument, NULL, 0},
635  {"alpha_sp", required_argument, NULL, 0},
636  {"alpha_sr", required_argument, NULL, 0},
637  {"theta_so", required_argument, NULL, 0},
638  {"theta_sp", required_argument, NULL, 0},
639  {"theta_sr", required_argument, NULL, 0},
640  {"kappa_so", required_argument, NULL, 0},
641  {"kappa_sp", required_argument, NULL, 0},
642  {"kappa_sr", required_argument, NULL, 0},
643  {"kappa_outside", required_argument, NULL, 0},
644  {"alpha_step", required_argument, NULL, 0},
645  {"theta_step", required_argument, NULL, 0},
646  {"kappa_step", required_argument, NULL, 0},
647  {"minalpha", required_argument, NULL, 0},
648  {"maxalpha", required_argument, NULL, 0},
649  {"mintheta", required_argument, NULL, 0},
650  {"maxtheta", required_argument, NULL, 0},
651  {"minkappa", required_argument, NULL, 0},
652  {"maxkappa", required_argument, NULL, 0},
653  {"tmax", required_argument, NULL, 0},
654  {"fit_tol", required_argument, NULL, 0},
655  {"itermax", required_argument, NULL, 0},
656  {"outfile", required_argument, NULL, 0},
657  {"pathfile", required_argument, NULL, 0},
658  {NULL, no_argument, NULL, 0}
659  };
660 
661  while ((opt = getopt_long(argc, argv, "hvg",
662  long_opts, &opt_index)) != -1) {
663  switch (opt) {
664 
665  case 'h': // user needs help -- print usage
666  opt_help = TRUE;
667  break;
668 
669  case 'v': // user wants verbose output
670  opt_verbose = TRUE;
671  break;
672 
673  case 'g': // user wants kappa to be the same in all regions
674  opt_global_kappa = TRUE;
675  break;
676 
677  case 0: // long option has no corresponding short option
678  if (STREQ("nr", long_opts[opt_index].name)) {
679  nr = atoi(optarg);
680  } else if (STREQ("nz", long_opts[opt_index].name)) {
681  nz = atoi(optarg);
682  } else if (STREQ("nt", long_opts[opt_index].name)) {
683  nt = atoi(optarg);
684  specified_nt = TRUE;
685  } else if (STREQ("nt_scale", long_opts[opt_index].name)) {
686  nt_scale = atof(optarg);
687  specified_nt_scale = TRUE;
688  } else if (STREQ("ez1", long_opts[opt_index].name)) {
689  ez1 = atof(optarg);
690  specified_ez1 = TRUE;
691  ez1 *= 1e-6; // Input in microns; convert to m
692  } else if (STREQ("ez2", long_opts[opt_index].name)) {
693  ez2 = atof(optarg);
694  specified_ez2 = TRUE;
695  ez2 *= 1e-6; // Input in microns; convert to m
696  } else if (STREQ("alpha_so", long_opts[opt_index].name)) {
697  alpha_so = atof(optarg);
698  } else if (STREQ("alpha_sp", long_opts[opt_index].name)) {
699  alpha_sp = atof(optarg);
700  } else if (STREQ("alpha_sr", long_opts[opt_index].name)) {
701  alpha_sr = atof(optarg);
702  } else if (STREQ("theta_so", long_opts[opt_index].name)) {
703  theta_so = atof(optarg);
704  } else if (STREQ("theta_sp", long_opts[opt_index].name)) {
705  theta_sp = atof(optarg);
706  } else if (STREQ("theta_sr", long_opts[opt_index].name)) {
707  theta_sr = atof(optarg);
708  } else if (STREQ("kappa_so", long_opts[opt_index].name)) {
709  kappa_so = atof(optarg);
710  } else if (STREQ("kappa_sp", long_opts[opt_index].name)) {
711  kappa_sp = atof(optarg);
712  } else if (STREQ("kappa_sr", long_opts[opt_index].name)) {
713  kappa_sr = atof(optarg);
714  } else if (STREQ("kappa_outside", long_opts[opt_index].name)) {
715  kappa_outside = atof(optarg);
716  specified_kappa_outside = TRUE;
717  } else if (STREQ("alpha_step", long_opts[opt_index].name)) {
718  alpha_step = atof(optarg);
719  } else if (STREQ("theta_step", long_opts[opt_index].name)) {
720  theta_step = atof(optarg);
721  } else if (STREQ("kappa_step", long_opts[opt_index].name)) {
722  kappa_step = atof(optarg);
723  } else if (STREQ("minalpha", long_opts[opt_index].name)) {
724  minalpha = atof(optarg);
725  } else if (STREQ("maxalpha", long_opts[opt_index].name)) {
726  maxalpha = atof(optarg);
727  } else if (STREQ("mintheta", long_opts[opt_index].name)) {
728  mintheta = atof(optarg);
729  } else if (STREQ("maxtheta", long_opts[opt_index].name)) {
730  maxtheta = atof(optarg);
731  } else if (STREQ("minkappa", long_opts[opt_index].name)) {
732  minkappa = atof(optarg);
733  } else if (STREQ("maxkappa", long_opts[opt_index].name)) {
734  maxkappa = atof(optarg);
735  } else if (STREQ("tmax", long_opts[opt_index].name)) {
736  tmax = atof(optarg);
737  } else if (STREQ("fit_tol", long_opts[opt_index].name)) {
738  fit_tol = atof(optarg);
739  } else if (STREQ("itermax", long_opts[opt_index].name)) {
740  itermax = atoi(optarg);
741  } else if (STREQ("outfile", long_opts[opt_index].name)) {
742  check_filename(optarg, outfilename);
743  } else if (STREQ("pathfile", long_opts[opt_index].name)) {
744  check_filename(optarg, pathfilename);
745  opt_pathfile = TRUE;
746  }
747  break;
748 
749  case ':': // missing argument for an option
750  error("Missing argument for a command-line option '-%c'",
751  optopt);
752  break;
753 
754  case '?': // unknown option
755  error("Unknown command line option '-%c'", optopt);
756  break;
757 
758  default:
759  error("Problem in parsing command line options");
760  break;
761  }
762  }
763 
764  // See if any options are left
765  num_args_left = argc - optind;
766  if ((num_args_left != 1) || opt_help)
767  print_usage_fit_layer(argv[0]);
768 
769 
770  if (opt_verbose) {
771  printf("The name of the input file is %s\n", infilename);
772  printf("The name of the output file will be %s\n", outfilename);
773  if (opt_pathfile)
774  printf("The name of the simplex path file will be %s\n",
775  pathfilename);
776  }
777 
778  // Check for conflicts
779  if (STREQ(infilename, outfilename))
780  error("The input and output filenames cannot be the same.");
781  if (STREQ(infilename, pathfilename))
782  error("The input and simplex path filenames cannot be the same.");
783  if (STREQ(outfilename, pathfilename))
784  error("The output and simplex path filenames cannot be the same.");
785 
786  if (specified_ez1 && !specified_ez2)
787  error("You specified ez1 but did not specify ez2");
788  if (specified_ez2 && !specified_ez1)
789  error("You specified ez2 but did not specify ez1");
790  if (specified_ez1 && specified_zmax)
791  error("You specified ez1 and ez2, so you should not specify zmax");
792 
793  if (specified_ez1) {
794  if (ez1 > 0) error("Bottom of cylinder ez1 = %f > 0\n", ez1);
795  if (ez2 < 0) error("Top of cylinder ez2 = %f < 0\n", ez2);
796  if (ez1 > lz1) error("Bottom of cylinder ez1 = %f > lz1 = %f\n",
797  ez1, lz1);
798  if (ez2 < lz2) error("Top of cylinder ez2 = %f < lz2 = %f\n",
799  ez2, lz2);
800  }
801 
802 
803  // Default values of parameters that depend on zmax
804  if (specified_pz == FALSE) {
805  pz = 120.0e-6; // m
806 if (opt_verbose)
807  printf("Warning: probe location set to default value of %g m = "
808  "%f microns\n (relative to source)",
809  pz, 1e6*pz);
810  }
811 
812  if (specified_lz1 == FALSE) {
813  lz1 = - 50.0e-6 / 2.0;
814 if (opt_verbose)
815  printf("Warning: lz1 set to default value of %g m = "
816  "%f microns\n (relative to source)",
817  lz1, 1e6*lz1);
818  }
819 
820  if (specified_lz2 == FALSE) {
821  lz2 = lz1 + 50.0e-6;
822 if (opt_verbose)
823  printf("Warning: lz2 set to default value of %g m = "
824  "%f microns\n (relative to source)",
825  lz2, 1e6*lz2);
826  }
827 
828  // Set kappa in SR and SO to kappa_outside
829  if (specified_kappa_outside) {
830  kappa_sr = kappa_outside;
831  kappa_so = kappa_outside;
832  if (opt_global_kappa) {
833  error("You're fitting for global kappa but specified "
834  "kappa_outside.\nWhen you fit for global kappa, "
835  "kappa_sr and kappa_so are set = kappa_sp.");
836  }
837  }
838 
839  /* If nolayer option, the SR parameters are used for the whole
840  environment; the SO and SP parameters are not used. However,
841  their values are still listed in the output file. Set their
842  values equal to the SR values here so that the correct values
843  are printed to the output file. */
844  if (nolayer) {
845  alpha_so = alpha_sr;
846  alpha_sp = alpha_sr;
847  theta_so = theta_sr;
848  theta_sp = theta_sr;
849  kappa_so = kappa_sr;
850  kappa_sp = kappa_sr;
851  if (opt_verbose)
852  printf("\nNOTE: nolayer option given; the diffusion "
853  "parameters of \nthe homogeneous environment "
854  "are set to the SR values\n");
855  }
856 
857  // If using a global value for kappa, set kappa_sr=kappa_so=kappa_sp
858  if (opt_global_kappa) {
859  kappa_sr = kappa_sp;
860  kappa_so = kappa_sp;
861  if (opt_verbose)
862  printf("NOTE: kappa will be the same in all layers (-g)\n"
863  "kappa_sr and kappa_so set to kappa_sp\n");
864  }
865 
866 /*
867  * Change coordinates. In the coordinate system used in the
868  * input file, source_z is always 0, so lz1, lz2, and pz are
869  * measured relative to the source. This was done because in
870  * the experiments distances are measured relative to the
871  * source.
872  *
873  * In the coordinate system used in the model, the bottom of
874  * the cylinder is at z=0 and the top of the cylinder is at
875  * z=zmax. The length of the cylinder is therefore zmax.
876  * So we shift the z-coordinates here.
877  *
878  * By default the z-coordinates are shifted such that the
879  * SP layer is centered in the volume, ie the midplane of SP is
880  * at z=zmax/2. However, the user might instead want to specify
881  * the location of the ends of the cylinder relative to the
882  * source (ez1 and ez2), which requires a different z-shift.
883  */
884  double coord_shift;
885 
886  if (specified_ez1) {
887  zmax = ez2 + (-ez1); // Calculate the cylinder length
888  coord_shift = (-ez1);
889  } else {
890  coord_shift = (zmax - (lz1+lz2))/2.;
891  }
892  sz = coord_shift;
893  pz += coord_shift;
894  lz1 += coord_shift;
895  lz2 += coord_shift;
896 
897 
898  // Discretization intervals in r, z
899  dr = rmax / nr;
900  dz = zmax / nz;
901  if (fabs(dr - dz) > 1.0e-15) {
902  dr = dz;
903  rmax = dr * nr;
904  }
905 
906  sz = round(sz / dz) * dz;
907  pz = round(pz / dz) * dz;
908  pr = round(pr / dr) * dr;
909 
910  // Layer geometry
911  iz1 = (long) (lz1 / dz);
912  lz1 = iz1 * dz + dz / 2.0;
913 
914  iz2 = (long) (lz2 / dz);
915  lz2 = iz2 * dz + dz / 2.0;
916 
917 
918  // D*
919  dstar_so = theta_so * dfree;
920  dstar_sp = theta_sp * dfree;
921  dstar_sr = theta_sr * dfree;
922  dstar_max = MAX(dstar_so, dstar_sp);
923  dstar_max = MAX(dstar_max, dstar_sr);
924 
925 
926  // Check if layer thickness is numerically reasonable
927  if ( ((iz2 - iz1) < 2) && (nolayer == 0) )
928  error("Layer has too few discrete steps to continue.");
929 
930  // Calculate time step from nt or from von Neumann criterion
931  if (specified_nt == TRUE)
932  dt = tmax / nt;
933  else
934  dt = 0.9 * dr*dr / (6.0 * dstar_max);
935 
936  // Scale nt if the user specified to do so -- but do it via dt
937  if (specified_nt_scale == TRUE) {
938  if (IS_ZERO(nt_scale)) error("nt_scale = 0");
939  if (nt_scale < 0.) error("nt_scale < 0");
940  dt /= nt_scale;
941  }
942 
943  // Calculate tmax and st as multiples of dt
944  nt = lround(tmax / dt);
945  tmax = dt * nt;
946  ns = lround(st / dt);
947  st = dt * ns;
948  nds = lround(sd / dt);
949  sd = dt * nds;
950 
951  if (sd >= tmax)
952  error("Source delay (%f) should be < tmax (%f)", sd, tmax);
953  if (st >= tmax)
954  error("Source duration (%f) should be < tmax (%f)", st, tmax);
955  if (sd+st >= tmax)
956  error("Source delay (%f) + duration (%f) should be < tmax (%f)",
957  sd, st, tmax);
958 
959 
960  // Calculate source amplitude in mol/s from current and transport number
961  sa = crnt * trn / FARADAY; // source strength in mol/s
962  // (not a concentration)
963 
964 
965  // Assemble string with command that user input
966  i = assemble_command(argc, argv, comments.command);
967  if (opt_verbose)
968  printf("\nIn main(): The command used was\n\t%s\n(%d words)\n\n",
969  comments.command, i);
970 
971 
972  // Put start time in string
973  strncpy(string, ctime(&start_time), MAX_LINELENGTH-1);
974 
975  // Feedback to user to check adjusted values
976  if (opt_verbose) {
977  printf("Output from fit-layer.c, version %.1f:\n", program_version);
978  printf("# Note that the z-values (sz, pz, lz1, and lz2) ");
979  printf("have been shifted \n# by %f microns ", 1.0e6*coord_shift);
980  if (specified_ez1) {
981  printf("to have the volume go from z=0 to z=zmax.\n");
982  } else {
983  printf("to center the SP layer in the volume.\n");
984  }
985  printf("nr x nz = %d x %d\n", nr, nz);
986  printf("rmax x zmax = %f x %f microns\n", 1.0e6 * rmax, 1.0e6 * zmax);
987  printf("dr x dz = %f x %f microns\n", 1.0e6 * dr, 1.0e6 * dz);
988  printf("(sr, sz) = (%f, %f) microns\n", 1.0e6 * sr, 1.0e6 * sz);
989  printf("(pr, pz) = (%f, %f) microns\n", 1.0e6 * pr, 1.0e6 * pz);
990  printf("Electrode distance = %f microns\n",
991  1.0e6 * sqrt(SQR(pr-sr) + SQR(pz-sz)));
992  printf("(iz1, iz2) = (%d, %d)\n", iz1, iz2);
993  printf("(lz1, lz2) = (%f, %f) microns\n", 1.0e6 * lz1, 1.0e6 * lz2);
994  printf("Layer thickness = %f microns\n", 1.0e6 * (lz2 - lz1));
995  printf("Layer discrete steps = %d\n", iz2 - iz1);
996  printf("Nolayer flag = %d\n" , nolayer);
997  printf("dfree = %g m^2/s\n", dfree);
998  printf("alpha_so = %.4f, theta_so = %.4f, "
999  "lambda_so = %.4f, kappa_so = %.6f\n",
1000  alpha_so, theta_so, 1.0/sqrt(theta_so), kappa_so);
1001  printf("Starting alpha_sp = %.4f, theta_sp = %.4f, "
1002  "lambda_sp = %.4f, kappa_sp = %.6f\n",
1003  alpha_sp, theta_sp, 1.0/sqrt(theta_sp), kappa_sp);
1004  printf("Starting alpha_step = %.4f, theta_step = %.4f\n",
1005  alpha_step, theta_step);
1006  printf("Constraints: minalpha = %.8f, maxalpha = %.8f\n",
1007  minalpha, maxalpha);
1008  printf("Constraints: mintheta = %.8f, maxtheta = %.8f\n",
1009  mintheta, maxtheta);
1010  printf("Constraints: minkappa = %.8f, maxkappa = %.8f\n",
1011  minkappa, maxkappa);
1012  printf("Stopping criteria: simplex size < %g or # iterations = %d\n",
1013  fit_tol, itermax);
1014  printf("alpha_sr = %.4f, theta_sr = %.4f, "
1015  "lambda_sr = %.4f, kappa_sr = %.6f\n",
1016  alpha_sr, theta_sr, 1.0/sqrt(theta_sr), kappa_sr);
1017  if (opt_global_kappa)
1018  printf("NOTE: kappa_sr and kappa_so set to kappa_sp (-g)\n");
1019  printf("nt = %d\n", nt);
1020  printf("tmax = %f s\n", tmax);
1021  printf("dt = %f ms\n", 1.0e3 * dt);
1022  printf("von Neumann dt / (dr^2/(6*dstar)) = %f\n",
1023  dt * 6.0 * dstar_max / (dr*dr));
1024  printf("ns = %d\n", ns);
1025  printf("source delay sd = %f s\n", sd);
1026  printf("source duration st = %f s\n", st);
1027  printf("Current = %g nA\n", 1.0e9 * crnt);
1028  printf("Transport number = %f\n", trn);
1029  printf("Start time = %s", string);
1030  }
1031 
1032 
1033  // Open output data file
1034  if ((file_ptr = fopen(outfilename,"w")) == NULL) {
1035  fprintf(stderr, "Error opening output file %s\n", outfilename);
1036  exit(EXIT_FAILURE);
1037  }
1038 
1039  // Write out command line to output file
1040  fprintf(file_ptr, "# Fit-layer Output File\n");
1041  fprintf(file_ptr, "# ~~~~~~~~~~~~~~~~~~~~~\n");
1042  fprintf(file_ptr, "# Command used to run program:\n");
1043  fprintf(file_ptr, "# %s\n", comments.command);
1044 
1045  // Write comments from input file to output file
1046  if (comments.n > 0) {
1047  fprintf(file_ptr, "# --------------------------------------\n");
1048  fprintf(file_ptr, "# Comments from input parameter file:\n");
1049  for (j=0; j<comments.n; j++)
1050  fprintf(file_ptr, "%s", comments.line[j]);
1051  fprintf(file_ptr, "# --------------------------------------\n");
1052  }
1053 
1054 
1055  // Write parameters to output file
1056  fprintf(file_ptr, "# Output from fit-layer.c, version %.1f:\n", program_version);
1057  fprintf(file_ptr, "# Note that the z-values (sz, pz, lz1, and lz2) ");
1058  fprintf(file_ptr, "have been shifted \n# by %f microns ", 1.0e6*coord_shift);
1059  if (specified_ez1) {
1060  fprintf(file_ptr, "to have the volume go from z=0 to z=zmax.\n");
1061  } else {
1062  fprintf(file_ptr, "to center the SP layer in the volume.\n");
1063  }
1064  fprintf(file_ptr, "# nr x nz = %d x %d\n", nr, nz);
1065  fprintf(file_ptr, "# rmax x zmax = %f x %f microns\n", 1.0e6 * rmax, 1.0e6 * zmax);
1066  fprintf(file_ptr, "# dr x dz = %f x %f microns\n", 1.0e6 * dr, 1.0e6 * dz);
1067  fprintf(file_ptr, "# (sr, sz) = (%f, %f) microns\n", 1.0e6 * sr, 1.0e6 * sz);
1068  fprintf(file_ptr, "# (pr, pz) = (%f, %f) microns\n", 1.0e6 * pr, 1.0e6 * pz);
1069  fprintf(file_ptr, "# Electrode distance = %f microns\n",
1070  1.0e6 * sqrt(SQR(pr-sr) + SQR(pz-sz)));
1071  fprintf(file_ptr, "# (iz1, iz2) = (%d, %d)\n", iz1, iz2);
1072  fprintf(file_ptr, "# (lz1, lz2) = (%f, %f) microns\n", 1.0e6 * lz1, 1.0e6 * lz2);
1073  fprintf(file_ptr, "# Layer thickness = %f microns\n", 1.0e6 * (lz2 - lz1));
1074  fprintf(file_ptr, "# Layer discrete steps = %d\n", iz2 - iz1);
1075  fprintf(file_ptr, "# Nolayer flag = %d\n" , nolayer);
1076  fprintf(file_ptr, "# dfree = %g m^2/s\n", dfree);
1077  fprintf(file_ptr, "# alpha_so = %.4f, theta_so = %.4f, "
1078  "lambda_so = %.4f, kappa_so = %.6f\n",
1079  alpha_so, theta_so, 1.0/sqrt(theta_so), kappa_so);
1080  fprintf(file_ptr, "# Starting alpha_sp = %.4f, theta_sp = %.4f, "
1081  "lambda_sp = %.4f, kappa_sp = %.6f\n",
1082  alpha_sp, theta_sp, 1.0/sqrt(theta_sp), kappa_sp);
1083  fprintf(file_ptr, "# Starting alpha_step = %.4f, theta_step = %.4f\n",
1084  alpha_step, theta_step);
1085  fprintf(file_ptr, "# Constraints: minalpha = %.8f, maxalpha = %.8f\n",
1086  minalpha, maxalpha);
1087  fprintf(file_ptr, "# Constraints: mintheta = %.8f, maxtheta = %.8f\n",
1088  mintheta, maxtheta);
1089  fprintf(file_ptr, "# Constraints: minkappa = %.8f, maxkappa = %.8f\n",
1090  minkappa, maxkappa);
1091  fprintf(file_ptr, "# Stopping criteria: simplex size < %g or # iterations = %d\n",
1092  fit_tol, itermax);
1093  fprintf(file_ptr, "# alpha_sr = %.4f, theta_sr = %.4f, "
1094  "lambda_sr = %.4f, kappa_sr = %.6f\n",
1095  alpha_sr, theta_sr, 1.0/sqrt(theta_sr), kappa_sr);
1096  if (opt_global_kappa)
1097  fprintf(file_ptr, "# NOTE: kappa_sr and kappa_so set to kappa_sp (-g)\n");
1098  fprintf(file_ptr, "# nt = %d\n", nt);
1099  fprintf(file_ptr, "# tmax = %f s\n", tmax);
1100  fprintf(file_ptr, "# dt = %f ms\n", 1.0e3 * dt);
1101  fprintf(file_ptr, "# von Neumann dt / (dr^2/(6*dstar)) = %f\n",
1102  dt * 6.0 * dstar_max / (dr*dr));
1103  fprintf(file_ptr, "# ns = %d\n", ns);
1104  fprintf(file_ptr, "# Source delay sd = %f s\n", sd);
1105  fprintf(file_ptr, "# Source duration st = %f s\n", st);
1106  fprintf(file_ptr, "# Current = %g nA\n", 1.0e9 * crnt);
1107  fprintf(file_ptr, "# Transport number = %f\n", trn);
1108  fprintf(file_ptr, "# Start time = %s", string); // ctime() added the \n
1109 
1110  // Close the file
1111  fclose(file_ptr);
1112 
1113  // Array of alpha values -- use 1D index to access elements of
1114  // the 2D array, so a[i*(nr+1)+j] = a[i][j] (use macro INDEX(i,j)
1115  alphas = create_array(nz*(nr+1), "alphas array");
1116  for (j=0; j<nr+1; j++) {
1117  for (i=0; i<iz1+1; i++) alphas[INDEX(i,j)] = alpha_sr;
1118  for (i=iz1+1; i<iz2+1; i++) alphas[INDEX(i,j)] = alpha_sp;
1119  for (i=iz2+1; i<nz; i++) alphas[INDEX(i,j)] = alpha_so;
1120  }
1121 
1122  // Array of 1/r values, except it is 0 for r=0
1123  // - in layer.pro it's a 2D array with all column identical,
1124  // but it doesn't have to be; I'm doing 1D (i only)
1125  param_struct.invr = create_array(nr+1, "param invr array");
1126  param_struct.invr[0] = 1.0 / dr;
1127  param_struct.invr[1] = 0.0;
1128  for (j=2; j<nr+1; j++) {
1129  param_struct.invr[j] = 1.0 / ((j-1.)*dr);
1130  }
1131 
1132 
1133  // Source
1134  param_struct.s = create_array(nz*(nr+1), "param s array");
1135  isource = lround(sz/dz); // index to z position of source
1136  jsource = 1+lround(sr/dr); // index to r position of source
1137  param_struct.s[INDEX(isource,jsource)]
1138  = (1.0 / alphas[INDEX(isource,jsource)]) *
1139  sa * dt * 4.0 / (PI * SQR(dr) * dz);
1140 
1141 
1142  // Time and probe arrays
1143  t = create_array(nt, "time");
1144  p = create_array(nt, "p");
1145  for (k=0; k<nt; k++)
1146  t[k] = dt * k;
1147 
1148  iprobe = lround(pz/dz); // index to z position of probe
1149  jprobe = 1+lround(pr/dr); // index to r position of probe
1150 
1151 
1152 
1153  // Fit parameters
1154  if (opt_verbose)
1155  printf("About to fit parameters\n");
1156 
1157 
1158  // Fit the model p[] to the data pdata[]
1159 
1160  if (opt_verbose) {
1161  printf("\nSimplex fitting -- vertex changes:\n");
1162  printf("Iter\talpha_fit\ttheta_fit\tkappa_fit\tmse \tfit size\n");
1163  printf("%d\t%f\t%f\t%f\n",
1164  0, alpha_sp, theta_sp, kappa_sp);
1165  }
1166 
1167  if (opt_pathfile) {
1168  if ((pathfile_ptr = fopen(pathfilename,"w")) == NULL) {
1169  fprintf(stderr, "Error opening simplex path file %s\n",
1170  pathfilename);
1171  exit(EXIT_FAILURE);
1172  }
1173  fprintf(pathfile_ptr, "Simplex fitting -- vertex changes:\n");
1174  fprintf(pathfile_ptr,
1175  "Iter\talpha_fit\ttheta_fit\tkappa_fit\tmse \tfit size\n");
1176  fprintf(pathfile_ptr, "%d\t%f\t%f\t%f\n",
1177  0, alpha_sp, theta_sp, kappa_sp);
1178  }
1179 
1180  param_struct.nt = nt;
1181  param_struct.nd = nd;
1182  param_struct.nz = nz;
1183  param_struct.nr = nr;
1184  param_struct.iprobe = iprobe;
1185  param_struct.jprobe = jprobe;
1186  param_struct.iz1 = iz1;
1187  param_struct.iz2 = iz2;
1188  param_struct.nolayer = nolayer;
1189  param_struct.opt_global_kappa = opt_global_kappa;
1190 
1191  param_struct.dt = dt;
1192  param_struct.dr = dr;
1193  param_struct.sd = sd;
1194  param_struct.st = st;
1195  param_struct.alpha_so = alpha_so;
1196  param_struct.theta_so = theta_so;
1197  param_struct.kappa_so = kappa_so;
1198  param_struct.alpha_sp = alpha_sp;
1199  param_struct.theta_sp = theta_sp;
1200  param_struct.kappa_sp = kappa_sp;
1201  param_struct.alpha_sr = alpha_sr;
1202  param_struct.theta_sr = theta_sr;
1203  param_struct.kappa_sr = kappa_sr;
1204  param_struct.minalpha = minalpha;
1205  param_struct.maxalpha = maxalpha;
1206  param_struct.mintheta = mintheta;
1207  param_struct.maxtheta = maxtheta;
1208  param_struct.minkappa = minkappa;
1209  param_struct.maxkappa = maxkappa;
1210  param_struct.dfree = dfree;
1211 
1212  param_struct.t = create_array(nt, "param t array");
1213 
1214  param_struct.p = create_array(nt, "param p array");
1215  param_struct.t_data = create_array(nd, "param t_data array");
1216  param_struct.p_data = create_array(nd, "param p_data array");
1217  for (k=0; k<nt; k++) {
1218  param_struct.t[k] = t[k];
1219  param_struct.p[k] = p[k];
1220  }
1221 
1222  // Fill t_data and p_data
1223  for (k=0; k<nd; k++) {
1224  param_struct.t_data[k] = tdata[k];
1225  param_struct.p_data[k] = pdata[k];
1226  }
1227 
1228 /*****************************************
1229  Fit the model to determine the parameters
1230  *****************************************/
1231  // Initialize the simplex
1232  simplex = gsl_vector_alloc(3);
1233  gsl_vector_set(simplex, 0, alpha_sp);
1234  gsl_vector_set(simplex, 1, theta_sp);
1235  gsl_vector_set(simplex, 2, kappa_sp);
1236 
1237  // Initialize step sizes
1238  steps = gsl_vector_alloc(3);
1239  gsl_vector_set(steps, 0, alpha_step);
1240  gsl_vector_set(steps, 1, theta_step);
1241  gsl_vector_set(steps, 2, kappa_step);
1242 
1243  // Set up minimization method
1244  fit_func.n = 3; // 3 parameters to fit
1245  fit_func.f = calc_mse_fit_layer; // function to minimize
1246  fit_func.params = &param_struct; // extra parameters to function
1247 
1248  fit_state = gsl_multimin_fminimizer_alloc(fit_algorithm, 3);
1249  gsl_multimin_fminimizer_set(fit_state, &fit_func, simplex, steps);
1250 
1251  // Run minimization
1252  do {
1253  fit_iter++;
1254  fit_status = gsl_multimin_fminimizer_iterate(fit_state);
1255 
1256  if (fit_status) break;
1257 
1258  fit_size = gsl_multimin_fminimizer_size(fit_state);
1259  fit_status = gsl_multimin_test_size(fit_size, fit_tol);
1260 
1261  if (opt_verbose)
1262  if (fit_status == GSL_SUCCESS) printf("Finished fit\n");
1263 
1264  alpha_fit = gsl_vector_get(fit_state->x, 0);
1265  theta_fit = gsl_vector_get(fit_state->x, 1);
1266  kappa_fit = gsl_vector_get(fit_state->x, 2);
1267  mse = fit_state->fval;
1268 
1269  if (opt_verbose)
1270  printf("%d\t%f\t%f\t%f\t%g\t%g\n",
1271  (int) fit_iter, alpha_fit, theta_fit, kappa_fit, mse, fit_size);
1272 
1273  if (opt_pathfile)
1274  fprintf(pathfile_ptr, "%d\t%f\t%f\t%f\t%g\t%g\n",
1275  (int) fit_iter, alpha_fit, theta_fit, kappa_fit, mse, fit_size);
1276 
1277  } while (fit_status == GSL_CONTINUE && fit_iter < itermax);
1278 
1279  if (fit_status != GSL_SUCCESS) {
1280  printf("Warning: failed to converge, status = %d, "
1281  "# iterations = %zd\n", fit_status, fit_iter);
1282  if (opt_pathfile)
1283  fprintf(pathfile_ptr, "Warning: failed to converge, "
1284  "status = %d, # iterations = %zd\n", fit_status, fit_iter);
1285  }
1286 
1287  if (opt_pathfile)
1288  fclose(pathfile_ptr);
1289 
1290  // Output results
1291  double lambda_fit = 1./sqrt(theta_fit);
1292  if (opt_verbose) {
1293  printf("Fitted alpha = %f\n", alpha_fit);
1294  printf("Fitted theta = %f (lambda = %f)\n",
1295  theta_fit, lambda_fit);
1296  if (opt_global_kappa)
1297  printf("Fitted kappa = %f s^-1 (in all layers)\n", kappa_fit);
1298  else
1299  printf("Fitted kappa = %f s^-1\n", kappa_fit);
1300  }
1301 
1302 
1303  // Get end time of program
1304  end_time = time(NULL);
1305  strncpy(string, ctime(&end_time), MAX_LINELENGTH-1);
1306 if (opt_verbose)
1307  printf("End time = %s", string);
1308 
1309  total_time = difftime(end_time, start_time);
1310 if (opt_verbose)
1311  printf("Total time = %d seconds = %f minutes = %f hours\n",
1312  (int) round(total_time), total_time/60., total_time/3600.);
1313 
1314 
1315  // Write results to output file. Two concentration columns,
1316  // the concentration from the layer model and also the data.
1317  // Each concentration column has its own time column preceding it.
1318  // (The data was input with their own time values.
1319  // The concentration curve calculated from the model typically
1320  // has >> 1000 points and is downsampled to 1000.
1321  // The time columns are close, especially for high resolution.)
1322  if ((file_ptr = fopen(outfilename,"a")) == NULL) {
1323  fprintf(stderr, "Error opening output file %s\n", outfilename);
1324  exit(EXIT_FAILURE);
1325  }
1326 
1327  fprintf(file_ptr, "# End time = %s", string); // ctime() added the \n
1328  fprintf(file_ptr, "# Total time = %d seconds = %f minutes = %f hours\n",
1329  (int) round(total_time), total_time/60., total_time/3600.);
1330  fprintf(file_ptr, "# --------------------------------------\n");
1331  fprintf(file_ptr, "# Results of fitting:\n");
1332  fprintf(file_ptr, "# Number of iterations = %d\n", (int)fit_iter);
1333  fprintf(file_ptr, "# Fitted alpha = %f\n", alpha_fit);
1334  fprintf(file_ptr, "# Fitted theta = %f (lambda = %f)\n",
1335  theta_fit, lambda_fit);
1336  if (opt_global_kappa)
1337  fprintf(file_ptr, "# Fitted kappa = %f s^-1 (in all layers)\n", kappa_fit);
1338  else
1339  fprintf(file_ptr, "# Fitted kappa = %f s^-1\n", kappa_fit);
1340  fprintf(file_ptr, "# Final mean squared error = %g\n", mse);
1341  fprintf(file_ptr, "# Final simplex size = %g\n", fit_size);
1342 
1343  fprintf(file_ptr, "# Solution: alpha_sp\ttheta_sp\tlambda_sp\tkappa_sp"
1344  "\t MSE\tsimplex size\t# iter.\tTime (s)"
1345  "\tTime (m)\tTime (h) \n");
1346  fprintf(file_ptr, "# Solution: %f\t%f\t%f\t%f\t%f\t%g \t%7d\t%8d\t%f\t%f\n",
1347  alpha_fit, theta_fit, lambda_fit, kappa_fit,
1348  mse, fit_size, (int) fit_iter,
1349  (int) round(total_time), total_time/60., total_time/3600.);
1350  fprintf(file_ptr, "# --------------------------------------\n");
1351  fprintf(file_ptr, "# Probe concentration data:\n");
1352  fprintf(file_ptr, "# time \t c (model) \t t (data) "
1353  "\t c (data) \n");
1354 
1355 
1356  // Print concentration arrays to output file
1357  // If there are more than 1000 points in the concentration values
1358  // from the model (normally nt >> 1000), downsample to 1000 points
1359  if (nt > 1000)
1360  for (i=0; i<1000; i++) {
1361  k = (i * nt) / 1000;
1362  l = (i * nd) / 1000;
1363  fprintf(file_ptr, "%#12.8g\t%#12.8g\t%#12.8g\t%#12.8g\n", param_struct.t[k], param_struct.p[k],
1364  param_struct.t_data[l], param_struct.p_data[l]);
1365  }
1366  else
1367  for (i=0; i<nt; i++) {
1368  fprintf(file_ptr, "%#12.8g\t%#12.8g\t%#12.8g\t%#12.8g\n", param_struct.t[i], param_struct.p[i],
1369  param_struct.t_data[i], param_struct.p_data[i]);
1370  }
1371  fprintf(file_ptr, "\n\n\n");
1372 
1373  // Close the file
1374  fclose(file_ptr);
1375 
1376 
1377 
1378  // Deallocate arrays
1379  free(t);
1380  free(p);
1381 // free(s);
1382  free(tdata);
1383  free(pdata);
1384  free(alphas);
1385 // free(invr);
1386 
1387  free(param_struct.t);
1388  free(param_struct.s);
1389  free(param_struct.invr);
1390  free(param_struct.t_data);
1391  free(param_struct.p_data);
1392  free(param_struct.p);
1393 
1394  gsl_vector_free(simplex);
1395  gsl_vector_free(steps);
1396  gsl_multimin_fminimizer_free(fit_state);
1397 
1398 if (opt_verbose)
1399  printf("All done\n");
1400 
1401  return(EXIT_SUCCESS);
1402 }
#define MAX(x, y)
Computes the maximum of x and y.
Definition: header.h:94
double * p_data
Probe concentration data.
Definition: fit-layer.c:90
int iprobe
z-index of probe location.
Definition: fit-layer.c:60
#define SQR(x)
Computes the square of x.
Definition: header.h:88
#define MAXNUM_COMMENTLINES
Maximum number of comment lines of input file to copy to output file.
Definition: header.h:34
double st
Duration of source.
Definition: fit-layer.c:69
#define IS_ZERO(x)
Evaluates to TRUE if |x| < SMALLNUM, FALSE otherwise.
Definition: header.h:74
double * s
Source array.
Definition: fit-layer.c:87
void calc_diffusion_curve_layer_fit_layer(int nt, int nz, int nr, int iprobe, int jprobe, int iz1, int iz2, int nolayer, double dt, double dr, double sd, double st, double alpha_so, double theta_so, double kappa_so, double alpha_sp, double theta_sp, double kappa_sp, double alpha_sr, double theta_sr, double kappa_sr, double dfree, double *t, double *s, double *invr, double *p)
Calculates the concentration as a function of space and time and returns the probe concentration as a...
Definition: model.c:94
#define STREQ(s1, s2)
Compares strings s1 and s2; evaluates to TRUE if strings are equal, FALSE if not. ...
Definition: header.h:82
double dfree
Free diffusion coefficient.
Definition: fit-layer.c:85
double maxkappa
Upper boundary for kappa_sp (add penalty if kappa_sp is out of bounds).
Definition: fit-layer.c:84
double theta_sr
Permeability in SR layer.
Definition: fit-layer.c:77
int nz
Number of support points in z (rows of concentration matrix).
Definition: fit-layer.c:58
Header file for program fit-layer.
void error(char *errorstring,...)
Print an error message to stderr and exit with status EXIT_FAILURE.
Definition: extras.c:20
double * invr
Array of 1/r values.
Definition: fit-layer.c:88
double * t_data
Time array for data.
Definition: fit-layer.c:89
double maxtheta
Upper boundary for theta_sp (add penalty if theta_sp is out of bounds).
Definition: fit-layer.c:82
#define MAX_COMMAND_LENGTH
Maximum number of characters of command to copy to output file.
Definition: header.h:37
double dr
Spacing in r (in this program, same as spacing in z).
Definition: fit-layer.c:67
double * create_array(int N, char *string)
Create an array of doubles of the specified size.
Definition: extras.c:105
double theta_sp
Permeability in SP layer.
Definition: fit-layer.c:74
double minalpha
Lower boundary for alpha_sp (add penalty if alpha_sp is out of bounds).
Definition: fit-layer.c:79
int nr
Number of support points in r (columns of concentration matrix).
Definition: fit-layer.c:59
int nt
Number of support points in time.
Definition: fit-layer.c:56
double alpha_so
Extracellular volume fraction in SO layer.
Definition: fit-layer.c:70
double sd
Source delay (time before source starts).
Definition: fit-layer.c:68
void check_filename(char *in, char *out)
Make sure that the filename is not too long.
Definition: extras.c:103
int iz2
z-index of SP-SO boundary.
Definition: fit-layer.c:63
int main(int argc, char *argv[])
Main program.
Definition: fit-layer.c:183
int iz1
z-index of SR-SP boundary.
Definition: fit-layer.c:62
double dt
Spacing in time.
Definition: fit-layer.c:66
#define INDEX(i, j)
Computes the 1D index for concentration or alpha arrays, given pseudo-2D indices i (z index) and j (r...
Definition: header.h:112
double theta_so
Permeability in SO layer.
Definition: fit-layer.c:71
double maxalpha
Upper boundary for alpha_sp (add penalty if alpha_sp is out of bounds).
Definition: fit-layer.c:80
#define FARADAY
Faraday constant in C/mol.
Definition: header.h:56
int assemble_command(int argc, char *argv[], char *command)
Create a string containing the command used to run the program.
Definition: io.c:91
double alpha_sp
Extracellular volume fraction in SP layer.
Definition: fit-layer.c:73
int jprobe
r-index of probe location.
Definition: fit-layer.c:61
double minkappa
Lower boundary for kappa_sp (add penalty if kappa_sp is out of bounds).
Definition: fit-layer.c:83
Typedef for struct for passing parameters and arrays to mse function.
Definition: fit-layer.c:55
double mintheta
Lower boundary for theta_sp (add penalty if theta_sp is out of bounds).
Definition: fit-layer.c:81
double * p
Probe concentration calculated from model.
Definition: fit-layer.c:91
#define MAX_LINELENGTH
Maximum length of any line in input file.
Definition: header.h:31
#define PI
Sets constant PI to M_PI if that is defined; otherwise, computes PI as .
Definition: header.h:52
void print_usage_fit_layer(char *program)
Print usage message and exit with status EXIT_FAILURE.
Definition: extras.c:43
double kappa_sr
Nonspecific clearance factor in SR layer.
Definition: fit-layer.c:78
int nolayer
Flag for no layer (homogenous environment).
Definition: fit-layer.c:64
double kappa_sp
Nonspecific clearance factor in SP layer.
Definition: fit-layer.c:75
#define FALSE
FALSE assigned to 0.
Definition: header.h:43
double kappa_so
Nonspecific clearance factor in SO layer.
Definition: fit-layer.c:72
double * t
Time array for model.
Definition: fit-layer.c:86
double calc_mse_fit_layer(const gsl_vector *x, void *params)
Mean squared error function for simplex fitting.
Definition: fit-layer.c:116
double alpha_sr
Extracellular volume fraction in SR layer.
Definition: fit-layer.c:76
int nd
Number of data points.
Definition: fit-layer.c:57
#define TRUE
TRUE assigned to 1.
Definition: header.h:46
#define MAXNUM_LINES
Maximum number of lines in input file.
Definition: header.h:28
int opt_global_kappa
True if user specifies that kappa be the same in all layers.
Definition: fit-layer.c:65