EddyCommandLineOptions.cpp 73.3 KB
Newer Older
wangkx1's avatar
init  
wangkx1 committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300

// Definitions of a class that does the parsing and
// sanity checking of commnad line options for the
// "eddy" application.
//
// EddyCommandLineOptions.cpp
//
// Jesper Andersson, FMRIB Image Analysis Group
//
// Copyright (C) 2011 University of Oxford
//
/*  Part of FSL - FMRIB's Software Library
    http://www.fmrib.ox.ac.uk/fsl
    fsl@fmrib.ox.ac.uk

    Developed at FMRIB (Oxford Centre for Functional Magnetic Resonance
    Imaging of the Brain), Department of Clinical Neurology, Oxford
    University, Oxford, UK


    LICENCE

    FMRIB Software Library, Release 6.0 (c) 2018, The University of
    Oxford (the "Software")

    The Software remains the property of the Oxford University Innovation
    ("the University").

    The Software is distributed "AS IS" under this Licence solely for
    non-commercial use in the hope that it will be useful, but in order
    that the University as a charitable foundation protects its assets for
    the benefit of its educational and research purposes, the University
    makes clear that no condition is made or to be implied, nor is any
    warranty given or to be implied, as to the accuracy of the Software,
    or that it will be suitable for any particular purpose or for use
    under any specific conditions. Furthermore, the University disclaims
    all responsibility for the use which is made of the Software. It
    further disclaims any liability for the outcomes arising from using
    the Software.

    The Licensee agrees to indemnify the University and hold the
    University harmless from and against any and all claims, damages and
    liabilities asserted by third parties (including claims for
    negligence) which arise directly or indirectly from the use of the
    Software or the sale of any products based on the Software.

    No part of the Software may be reproduced, modified, transmitted or
    transferred in any form or by any means, electronic or mechanical,
    without the express permission of the University. The permission of
    the University is not required if the said reproduction, modification,
    transmission or transference is done without financial return, the
    conditions of this Licence are imposed upon the receiver of the
    product, and all original and amended source code is included in any
    transmitted product. You may be held legally responsible for any
    copyright infringement that is caused or encouraged by your failure to
    abide by these terms and conditions.

    You are not permitted under this Licence to use this Software
    commercially. Use for which any financial return is received shall be
    defined as commercial use, and includes (1) integration of all or part
    of the source code or the Software into a product for sale or license
    by or on behalf of Licensee to third parties or (2) use of the
    Software or any derivative of it for research with the final aim of
    developing software products for sale or license to a third party or
    (3) use of the Software or any derivative of it for research with the
    final aim of developing non-software products for sale or license to a
    third party, or (4) use of the Software to provide any service to an
    external organisation for which payment is received. If you are
    interested in using the Software commercially, please contact Oxford
    University Innovation ("OUI"), the technology transfer company of the
    University, to negotiate a licence. Contact details are:
    fsl@innovation.ox.ac.uk quoting Reference Project 9564, FSL.*/
#include <cstdlib>
#include <string>
#include <vector>
#include <algorithm>
#include <thread>
#include "utils/options.h"
#include "topup/topup_file_io.h"
#include "EddyHelperClasses.h"
#include "EddyUtils.h"
#include "EddyCommandLineOptions.h"

using namespace std;
using namespace EDDY;

EddyCommandLineOptions::diff::diff() try :
  // Parameters related to specification of diffusion weighting
  _bvecs(string("--bvecs"),string(""),string("\tFile containing the b-vectors for all volumes in --imain"),true,Utilities::requires_argument),
  _bvals(string("--bvals"),string(""),string("\tFile containing the b-values for all volumes in --imain"),true,Utilities::requires_argument),
  _bdeltas(string("--bdeltas"),string(""),string("File containing the b-delta values for all volumes in --imain"),false,Utilities::requires_argument),
  _data_is_shelled(string("--data_is_shelled"),false,string("Assume, don't check, that data is shelled (default false)"),false, Utilities::no_argument),
  _brange(string("--b_range"),100,string("Range of b-values considered to be the same shell (default 100)"),false, Utilities::requires_argument),
  // Parameters related to least squares reconstruction of the data
  _resamp(string("--resamp"),string("jac"),string("Final resampling method (jac/lsr, default jac)"),false, Utilities::requires_argument),
  _lsr_lambda(string("--lsr_lambda"),0.01,string("Regularisation weight for LSR-resampling."),false,Utilities::requires_argument),
  // Parameters related to eddy current modelling
  _flm(string("--flm"),string("quadratic"),string("\tFirst level EC model (movement/linear/quadratic/cubic, default quadratic)"),false,Utilities::requires_argument),
  _slm_str(string("--slm"),string("none"),string("\tSecond level EC model (none/linear/quadratic, default none)"),false,Utilities::requires_argument),
  _b0_flm(string("--b0_flm"),string("movement"),string("First level EC model for b0 scans (movement/linear/quadratic, default movement)"),false,Utilities::requires_argument),
  _b0_slm_str(string("--b0_slm"),string("none"),string("Second level EC model for b0 scans (none/linear/quadratic, default none)"),false,Utilities::requires_argument),
  _rep_time(string("--rep_time"),5.0,string("Repetition time (seconds)"),false,Utilities::requires_argument),
  _long_ec_str(string("--lecm"),string("none"),string("Model for long (time-varying) EC (none/weights/tc/jointweights/jointtc default none)"),false,Utilities::requires_argument),
  _long_ec_dont_reest(string("--lecm_dont_reest"),false,string("Don't reestimate EC when estimating long EC (default false)"),false, Utilities::no_argument),
  _long_ec_sep_offs_move(string("--lecm_sep_offs_move"),false,string("Attempt to separate field offset from subject movement when estimating long EC (default false)"),false, Utilities::no_argument),
  // Parameters related to outlier detection/replacement
  _rep_noise(string("--rep_noise"),false,string("Add noise to replaced outliers (default false)"),false, Utilities::no_argument),
  _ol_ss(string("--ol_ss"),string("sw"),string("Summary statistics for OL, shell-wise (sw) or pooled (pooled). (default sw)"),false,Utilities::requires_argument),
  _ol_pos(string("--ol_pos"),false,string("Consider both positive and negative outliers if set (default false)"),false,Utilities::no_argument),
  _ol_sqr(string("--ol_sqr"),false,string("Consider outliers among sums-of-squared differences if set (default false)"),false,Utilities::no_argument),
  // Parameters related to the Gaussian process
  _covfunc(string("--covfunc"),string("spheri"),string("Covariance function for GP (spheri/expo/old, default spheri)"),false, Utilities::requires_argument),
  // Should bvecs be rotated during estimations (probably not)
  _rbvde(string("--rbvde"),false,string("\tRotate b-vecs during estimation (default false)"),false,Utilities::no_argument),
  // Parameters related to "tricky" movements for diffusion
  _dont_sep_offs_move(string("--dont_sep_offs_move"),false,string("Do NOT attempt to separate field offset from subject movement (default false)"),false, Utilities::no_argument),
  _offset_model(string("--offset_model"),string("linear_lag"),string("Second level model for field offset (default linear_lag)"),false, Utilities::requires_argument),
  _dont_peas(string("--dont_peas"),false,string("Do NOT perform a post-eddy alignment of shells (default false)"),false, Utilities::no_argument),
  _use_b0s_for_peas(string("--b0_peas"),false,string("Use interspersed b0s to perform post-eddy alignment of shells (default false)"),false, Utilities::no_argument),
  _session(string("--session"),string(""),string("File containing session indices for all volumes in --imain"),false,Utilities::requires_argument),
  // Parameters related to the writing of various "extra" output
  _fields(string("--fields"),false,string("Write EC fields as images (default false)"),false, Utilities::no_argument),
  _write_cnr_maps(string("--cnr_maps"),false,string("Write shell-wise cnr-maps (default false)"),false, Utilities::no_argument),
  _write_range_cnr_maps(string("--range_cnr_maps"),false,string("Write shell-wise range-cnr-maps (default false)"),false, Utilities::no_argument),
  _write_scatter_brain_predictions(string("--write_scatter_brain_predictions"),false,string("Write predictions obtained with a scattered data approach (in addition to corrected, default false)"),false, Utilities::no_argument),
  // Parameters related to things that can be useful for debugging/development
  _dwi_only(string("--dwi_only"),false,string("Only register the dwi images (default false)"),false, Utilities::no_argument),
  _b0_only(string("--b0_only"),false,string("Only register the b0 images (default false)"),false, Utilities::no_argument),
  _test_rot(string("--test_rot"),std::vector<float>(0),string("Do a large rotation to test b-vecs"),false,Utilities::requires_argument),
  _print_mi_values(string("--pmiv"),false,string("Write text file of MI values between shells (default false)"),false,Utilities::no_argument),
  _print_mi_planes(string("--pmip"),false,string("Write text file of (2D) MI values between shells (default false)"),false,Utilities::no_argument),
  // --mb_offs will soon be deprecated, so we should warn about it
  _mb_offs(string("--mb_offs"),0,string("Multi-band offset (-1 if bottom slice removed, 1 if top slice removed) (DEPRECATED!)"),false,Utilities::requires_argument),
  // The remaining are deprecated parameters
  _sep_offs_move(string("--sep_offs_move"),true,string("Attempt to separate field offset from subject movement (DEPRECATED!)"),false, Utilities::no_argument),
  _peas(string("--peas"),true,string("Perform a post-eddy alignment of shells (DEPRECATED!)"),false, Utilities::no_argument),
  _rms(string("--rms"),true,string("Write movement induced RMS (DEPRECATED!)"),false, Utilities::no_argument),
  _slorder(string("--slorder"),string(""),string("Name of text file defining slice/group order (DEPRECATED!)"),false,Utilities::requires_argument)
{} catch (const EddyInputError& e)
{
  cout << e.what() << endl;
  cout << "Terminating program" << endl;
  exit(EXIT_FAILURE);
} EddyCatch

EddyCommandLineOptions::fmri::fmri() try :
  _session(string("--session"),string(""),string("File containing session indices for all volumes in --imain"),true,Utilities::requires_argument),
  _rep_time(string("--rep_time"),3.0,string("Repetition time (seconds)"),true,Utilities::requires_argument),
  _echo_time(string("--echo_time"),40.0,string("Echo time (milliseconds)"),false,Utilities::requires_argument),
  _covfunc(string("--covfunc"),string("sqexp"),string("Covariance function for GP (sqexp, default sqexp)"),false, Utilities::requires_argument),
  _write_snr_maps(string("--snr_maps"),false,string("Write session-wise SNR maps (default false)"),false, Utilities::no_argument),
  _fast_map(string("--fast_map"),string(""),string("FAST pve map for CSF"),false, Utilities::requires_argument)
{}
catch (const EddyInputError& e)
{
  cout << e.what() << endl;
  cout << "Terminating program" << endl;
  exit(EXIT_FAILURE);
} EddyCatch

EddyCommandLineOptions::EddyCommandLineOptions(int  argc, char *argv[]) try :
  _title("eddy \nCopyright(c) 2015, University of Oxford (Jesper Andersson)"),
  _examples("eddy --monsoon"),
  // Mandatory parameters
  _imain(string("--imain"),string(""),string("\tFile containing all the images to estimate distortions for"),true,Utilities::requires_argument),
  _mask(string("--mask"),string(""),string("\tMask to indicate brain"),true,Utilities::requires_argument),
  _acqp(string("--acqp"),string(""),string("\tFile containing acquisition parameters"),true,Utilities::requires_argument),
  _index(string("--index"),string(""),string("\tFile containing indices for all volumes in --imain into --acqp and --topup"),true,Utilities::requires_argument),
  _out(string("--out"),string(""),string("\tBasename for output"),true,Utilities::requires_argument),
  // Parameters related to input susceptibility field
  _topup(string("--topup"),string(""),string("\tBase name for output files from topup"),false,Utilities::requires_argument),
  _field(string("--field"),string(""),string("\tName of file with susceptibility field (in Hz)"),false,Utilities::requires_argument),
  _field_mat(string("--field_mat"),string(""),string("Name of rigid body transform for susceptibility field"),false,Utilities::requires_argument),
  // Paramaters related to the general running of vanilla eddy
  _niter_tmp(string("--niter"),5,string("\tNumber of iterations (default 5)"),false,Utilities::requires_argument),
  _fwhm_tmp(string("--fwhm"),std::vector<float>(1,0.0),string("\tFWHM for conditioning filter when estimating the parameters (default 0)"),false,Utilities::requires_argument),
  _ref_scan_no(string("--ref_scan_no"),0,string("Zero-offset # of ref (for location) volume (default 0)"),false,Utilities::requires_argument),
  _shape_ref_scan_nos(string("--shape_ref_scan_nos"),std::vector<int>(0),string("Zero-offset #s of refs (for shape) volumes"),false,Utilities::requires_argument),
  _interp(string("--interp"),string("spline"),string("Interpolation model for estimation step (spline/trilinear, default spline)"),false,Utilities::requires_argument),
  _extrap(string("--extrap"),string("periodic"),string("Extrapolation model for estimation step (periodic/mirror, default periodic)"),false,Utilities::requires_argument),
  _epvalid(string("--epvalid"),false,string("Indicates that extrapolation is valid in EP direction (default false)"),false, Utilities::no_argument),
  // Parameters related to outlier detection/replacement
  _rep_ol(string("--repol"),false,string("\tDetect and replace outlier slices (default false))"),false, Utilities::no_argument),
  _ol_nstd(string("--ol_nstd"),4.0,string("Number of std off to qualify as outlier (default 4)"),false,Utilities::requires_argument),
  _ol_nvox(string("--ol_nvox"),250,string("Min # of voxels in a slice for inclusion in outlier detection (default 250)"),false,Utilities::requires_argument),
  _ol_ec(string("--ol_ec"),1,string("\tError type (1 or 2) to keep constant for outlier detection (default 1)"),false,Utilities::requires_argument),
  _ol_type(string("--ol_type"),string("sw"),string("Type of outliers, slicewise (sw), groupwise (gw) or both (both). (default sw)"),false,Utilities::requires_argument),
  _ol_jacut(string("--ol_jacut"),3.0,string("Upper threshold for Jacobian outlier detection mask (default 3)"),false,Utilities::requires_argument),
  // Parameters related to slice-to-volume motion correction
  _mb(string("--mb"),1,string("\tMulti-band factor"),false,Utilities::requires_argument),
  _slspec(string("--slspec"),string(""),string("Name of text file completely specifying slice/group acuistion. N.B. --slspec and --json are mutually exclusive."),false,Utilities::requires_argument),
  _json(string("--json"),string(""),string("\tName of .json text file with information about slice timing. N.B. --json and --slspec are mutually exclusive."),false,Utilities::requires_argument),
  _mp_order(string("--mporder"),std::vector<int>(1,0),string("Order of slice-to-vol movement model (default 0, i.e. vol-to-vol"),false,Utilities::requires_argument),
  _s2v_lambda(string("--s2v_lambda"),std::vector<float>(1,1.0),string("Regularisation weight for slice-to-vol movement. (default 1, reasonable range 1--10"),false,Utilities::requires_argument),
  _s2v_niter(string("--s2v_niter"),std::vector<int>(1,5),string("Number of iterations for slice-to-vol (default 5)"),false,Utilities::requires_argument),
  _s2v_fwhm(string("--s2v_fwhm"),std::vector<float>(1,0.0),string("FWHM for conditioning filter when estimating slice-to-vol parameters (default 0)"),false,Utilities::requires_argument),
  _s2v_interp(string("--s2v_interp"),string("trilinear"),string("Slice-to-vol interpolation model for estimation step (spline/trilinear, default trilinear)"),false,Utilities::requires_argument),
  // Parameters related to susceptibility-by-movement interaction estimation
  _estimate_mbs(string("--estimate_move_by_susceptibility"),false,string("Estimate how susceptibility field changes with subject movement (default false)"),false,Utilities::no_argument),
  _mbs_niter(string("--mbs_niter"),10,string("Number of iterations for MBS estimation (default 10)"),false,Utilities::requires_argument),
  _mbs_lambda(string("--mbs_lambda"),10.0,string("Weighting of regularisation for MBS estimation (default 10)"),false,Utilities::requires_argument),
  _mbs_ksp(string("--mbs_ksp"),10.0,string("Knot-spacing for MBS field estimation (default 10mm)"),false,Utilities::requires_argument),
  // Parameters related to the Gaussian process
  _hyparcostfunc(string("--hpcf"),string("CV"),string("\tCost-function for GP hyperparameters (MML/CV/GPP/CC, default CV)"),false, Utilities::requires_argument),
  _nvoxhp(string("--nvoxhp"),1000,string("# of voxels used to estimate the hyperparameters (default 1000)"),false, Utilities::requires_argument),
  _initrand(string("--initrand"),0,string("Seeds rand for when selecting voxels (default 0=no seeding)"),false, Utilities::optional_argument),
  _hyparfudgefactor(string("--ff"),10.0,string("\tFudge factor for hyperparameter error variance (default 10.0)"),false, Utilities::requires_argument),
  _hypar(string("--hypar"),std::vector<float>(0),string("\tUser specified values for GP hyperparameters"),false,Utilities::requires_argument),
  // Miscallenous parameters related to missing planes/output masking
  _fep(string("--fep"),false,string("\tFill empty planes in x- or y-directions (default false)"),false, Utilities::no_argument),
  _dont_mask_output(string("--dont_mask_output"),false,string("Do not mask output to include only voxels present for all volumes (default false)"),false, Utilities::no_argument),
  // Parameters related to initialisation of movement/distortion parameters
  _init(string("--init"),string(""),string("\tText file with parameters for initialisation"),false,Utilities::requires_argument),
  _init_s2v(string("--init_s2v"),string(""),string("Text file with parameters for initialisation of slice-to-vol movement"),false,Utilities::requires_argument),
  _init_mbs(string("--init_mbs"),string(""),string("4D image file for initialisation of movement-by-susceptibility"),false,Utilities::requires_argument),
  // Parameters related to the writing of various "extra" output
  _dfields(string("--dfields"),false,string("Write total displacement fields (default false)"),false, Utilities::no_argument),
  _residuals(string("--residuals"),false,string("Write residuals (between GP and observations), (default false)"),false, Utilities::no_argument),
  _with_outliers(string("--with_outliers"),false,string("Write corrected data (additionally) with outliers retained (default false)"),false, Utilities::no_argument),
  _history(string("--history"),false,string("Write history of mss and parameter estimates (default false)"),false, Utilities::no_argument),
  _write_slice_stats(string("--wss"),false,string("\tWrite slice-wise stats for each iteration (default false)"),false,Utilities::no_argument),
  _write_predictions(string("--write_predictions"),false,string("Write predicted data (in addition to corrected, default false)"),false, Utilities::no_argument),
  _no_text_files(string("--no_textout"),false,string("Supress writing of separate text output files (default false)"),false, Utilities::no_argument),
  // Parameters related to things that can be useful for debugging/development
  _debug_tmp(string("--debug"),0,string("\tLevel of debug print-outs (default 0)"),false,Utilities::requires_argument),
  _dbg_indx_str(string("--dbgindx"),string(""),string("Indicies (zero offset) of volumes for debug print-outs"),false,Utilities::requires_argument),
  _log_timings(string("--log_timings"),false,string("Write timing information (defaut false)"),false, Utilities::no_argument),
  // Parameters related to architecture/threads/cores etc
  _nthr(string("--nthr"),1,string("# of threads used when running eddy (default 1)"),false, Utilities::requires_argument),
  // Finally some generally helpful parameters
  _very_verbose(string("--very_verbose"),false,string("Switch on detailed diagnostic messages (default false)"),false, Utilities::no_argument),
  _verbose(string("-v,--verbose"),false,string("switch on diagnostic messages"),false, Utilities::no_argument),
  _help(string("-h,--help"),false,string("display this message"),false, Utilities::no_argument),
  _is_fmri(false), _init_rand(false), _fixed_hpar(false), _shape_references_set(false)  
{
  // All the commmon parameters have been initialised. Now we need to initialise also the modality specific ones.
  if (argc > 1) {
    if (std::string(argv[1]) != "fmri") {
      if (std::string(argv[1]) != "diffusion") std::cout << std::endl << "Warning: In a future release the first argument will have to be \"diffusion\" when using eddy on diffusion data, i.e." << std::endl << "eddy diffusion --imain='my_ima' --acqp='my_acqp' ..." << std::endl << std::endl;
      _is_fmri = false;
    }
    else { // This means that the first argument is "fmri"
      _is_fmri = true;
      // Temporarily block the fMRI branch to allow for bugfix release.
      throw EddyInputError("fMRI functionality is currently disabled awaiting more testing");
    }
  }
  else { // This means that someone only typed eddy
    std::cout << std::endl << "Warning: In a future release the first argument will have to be \"diffusion\" when using eddy on diffusion data, i.e." << std::endl << "eddy diffusion --imain='my_ima' --acqp='my_acqp' ..." << std::endl << std::endl;
    std::cout << "If you want to see the help screen for eddy for fmri, just type \"eddy fmri\"" << std::endl << std::endl;
  }

  do_initial_parsing(argc,argv);

  // Do sanity checking and some initial reading of files
  // First warn about use of deprecated parameters
  if (IsDiffusion()) {
    if (_diff._sep_offs_move.set()) std::cout << "Warning: --sep_offs_move parameter is deprecated and will crash a future version" << std::endl << std::endl;
    if (_diff._peas.set()) std::cout << "Warning: --peas parameter is deprecated and will crash a future version" << std::endl << std::endl;
    if (_diff._rms.set()) std::cout << "Warning: --rms parameter is deprecated and will crash a future version" << std::endl << std::endl;
    if (_diff._mb_offs.set()) std::cout << "Warning: --mb_offs parameter is deprecated and will crash a future version" << std::endl << std::endl;
    if (_diff._slorder.set()) throw EddyInputError("--slorder has been deprecated");
  }
  if (!_no_text_files.value()) std::cout << "Warning: Writing of individual text files will be discontinued in favour of a single .json file in future versions" << std::endl << std::endl;

  // Next, check on all common (to diffusion and fmri) parameters.
  // I have tried to keep the order of the checks to be the same as the order in which they are added to options.

  // First mandatory parameters
  // Image file exists?
  NEWIMAGE::volume4D<float> imahdr;
  try { NEWIMAGE::read_volume_hdr_only(imahdr,_imain.value()); }  // Throws if there is a problem
  catch(...) { throw EddyInputError("Error when attempting to read --imain file " + _imain.value()); }
  NEWIMAGE::volume<float> maskhdr;
  try { NEWIMAGE::read_volume_hdr_only(maskhdr,_mask.value()); }  // Throws if there is a problem
  catch(...) { throw EddyInputError("Error when attempting to read --mask file " + _mask.value()); }
  if (!samesize(imahdr[0],maskhdr,3,true)) throw EddyInputError("--imain and --mask images must have the same dimensions");
  if (maskhdr.tsize() != 1) throw EddyInputError("--mask image must be 3D volume");
  // File with acquisition parameters exists and makes sense
  NEWMAT::Matrix acqpM;
  try {
    acqpM = MISCMATHS::read_ascii_matrix(_acqp.value());
    if (acqpM.Ncols() != 4) throw EddyInputError("Each row of the --acqp file must contain 4 values");
  }
  catch (...) { throw EddyInputError("Error when attempting to read --acqp file " + _acqp.value()); }
  // Index file exists and makes sense given image file
  NEWMAT::Matrix index;
  try {
    index = MISCMATHS::read_ascii_matrix(_index.value());
    if (std::min(index.Nrows(),index.Ncols()) != 1 || std::max(index.Nrows(),index.Ncols()) != imahdr.tsize()) {
      throw EddyInputError("--index must be an 1xN or Nx1 matrix where N is the number of volumes in --imain");
    }
  }
  catch (...) { throw EddyInputError("Error when attempting to read --index file " + _index.value()); }
  if (!this->indicies_kosher(index,acqpM)) throw EddyInputError("Mismatch between --index and --acqp");
  if (_out.value() == string("")) throw EddyInputError("--out must be a non-empty string");

  // Parameters related to input susceptibility field
  // Check that not both topup and field files have been specified
  if (_topup.set() && _topup.value() != string("") && _field.set() && _field.value() != string("")) {
    throw EddyInputError("One cannot specify both --field and --topup file");
  }
  // Topup file exists if one has been specified
  if (_topup.set() && _topup.value() != string("")) {
    try { TOPUP::TopupFileReader  tfr(_topup.value()); }
    catch (const TOPUP::TopupFileIOException& e) { cout << e.what() << endl; throw EddyInputError("Error when attempting to read --topup files " + _topup.value() + "_fieldcoef.nii.gz and " + _topup.value() + "_movpar.txt"); }
    catch (...) { throw EddyInputError("Error when attempting to read --topup files " + _topup.value() + "_fieldcoef.nii.gz and " + _topup.value() + "_movpar.txt"); }
  }
  // Field file exists if one has been specified
  if (_field.set() && _field.value() != string("")) {
    try { TOPUP::TopupFileReader  tfr(_field.value()); }
    catch (const TOPUP::TopupFileIOException& e) { cout << e.what() << endl; throw EddyInputError("Error when attempting to read --field file " + _field.value()); }
    catch (...) { throw EddyInputError("Error when attempting to read --field file " + _field.value()); }
  }
  // Field mat file exists if one has been specified
  if (_field_mat.set() && _field_mat.value() != string("")) {
    try {
      NEWMAT::Matrix fieldM = MISCMATHS::read_ascii_matrix(_field_mat.value());
      if (fieldM.Nrows() != 4 || fieldM.Ncols() != 4) throw EddyInputError("--field_mat must be a 4x4 matrix");
      NEWMAT::Matrix ul = fieldM.SubMatrix(1,3,1,3);
      float det = ul.Determinant();
      if (std::abs(det-1.0) > 1e-6) throw EddyInputError("--field_mat must be a rigid body transformation");
    }
    catch (...) { throw EddyInputError("Error when attempting to read --field_mat file" + _field_mat.value()); }
  }

  // Paramaters related to the general running of vanilla eddy
  // FWHM either once and for all or one per iteration
  _niter = static_cast<unsigned int>(_niter_tmp.value());
  _fwhm = _fwhm_tmp.value();
  if (_fwhm.size() != 1 && _fwhm.size() != _niter) throw EddyInputError("--fwhm must be one value or one per iteration");
  if (_fwhm.size() != _niter) _fwhm.resize(_niter,_fwhm[0]);
  // Reasonable FWHM
  for (unsigned int i=0; i<_fwhm.size(); i++) {
    if (_fwhm[i] < 0.0 || _fwhm[i] > 20.0) throw EddyInputError("--fwhm value outside valid range 0-20mm");
  }
  // Location ref value is reasonable.
  if (_ref_scan_no.value() < 0 || _ref_scan_no.value() > imahdr.tsize()-1) throw EddyInputError("--ref_scan_no out of range");
  // Shape reference reasonable if set. Must have exactly one ref per shell. 
  if (_shape_ref_scan_nos.set()) { 
    if (IsfMRI()) { // fMRI counts as a single shell
      if (_shape_ref_scan_nos.value().size() != 1) throw EddyInputError("--shape_ref_scan_nos has wrong number of entries");
      if (_shape_ref_scan_nos.value()[0] < 0 || _shape_ref_scan_nos.value()[0] > imahdr.tsize()-1) throw EddyInputError("--shape_ref_scan_nos out of range");
      _fmri._shape_reference = _shape_ref_scan_nos.value()[0];
      _shape_references_set = true;
    }
    else {  // It is diffusion, so we must check it against shells
      NEWMAT::Matrix bvalsM;
      try { // First read bvals file so we know what the shells are
	bvalsM = MISCMATHS::read_ascii_matrix(_diff._bvals.value());
	if (std::min(bvalsM.Nrows(),bvalsM.Ncols()) != 1 || std::max(bvalsM.Nrows(),bvalsM.Ncols()) != imahdr.tsize()) {
	  throw EddyInputError("--bvals should contain a 1xN or Nx1 matrix where N is the number of volumes in --imain");
	}
      }
      catch (...) { throw EddyInputError("Error when attempting to read --bvals file " + _diff._bvals.value()); }
      // Now create a vector of DiffPara objects so we can use existing functions
      if (bvalsM.Nrows() < bvalsM.Ncols()) bvalsM = bvalsM.t();
      std::vector<EDDY::DiffPara> dpvec;
      for (int i=0; i<bvalsM.Nrows(); i++) dpvec.push_back(EDDY::DiffPara(bvalsM(i+1,1)));
      std::vector<double> shell_b_values;
      std::vector<unsigned int> shell_indicies;
      double original_b_range = EddyUtils::GetbRange(); 
      EddyUtils::SetbRange(_diff._brange.value());      // Temporarily set b-range to value we will check later
      if (!EddyUtils::GetGroups(dpvec,shell_indicies,shell_b_values) && !_diff._data_is_shelled.value()) throw EddyInputError("Data not shelled");
      EddyUtils::SetbRange(original_b_range);
      if (shell_b_values.size() != _shape_ref_scan_nos.value().size()) throw EddyInputError("There must be one index per shell in --shape_ref_scan_nos");
      std::vector<int> shells(_shape_ref_scan_nos.value().size()); // The shell index for each of the shape-refs
      for (unsigned int i=0; i<_shape_ref_scan_nos.value().size(); i++) shells[i] = shell_indicies[_shape_ref_scan_nos.value()[i]];
      for (unsigned int i=0; i<shells.size(); i++) {
	for (unsigned int j=i+1; j<shells.size(); j++) {
	  if (shells[i]==shells[j]) throw EddyInputError("There must be one index per shell in --shape_ref_scan_nos");
	}
      }
      // If we are here, it means all is fine with --shape_ref_scan_nos
      this->set_shape_refs(_shape_ref_scan_nos.value(),shells,shell_b_values);
    }
  }
  // Valid interpolation method?
  if (_interp.value() != string("spline") && _interp.value() != string("trilinear")) throw EddyInputError("Invalid --interp parameter");
  // Valid extrapolation method?
  if (_extrap.value() != string("mirror") && _extrap.value() != string("periodic")) throw EddyInputError("Invalid --extrap parameter");
  // Correct extrapolation method for valid extrapolation
  if (_extrap.value() == string("mirror") && _epvalid.value()) throw EddyInputError("--extrap=mirror cannot be combined with --epvalid");

  // Parameters related to outlier detection/replacement
  // Reasonable values for outlier detection
  if (_ol_nstd.value() < 1.96) throw EddyInputError("--ol_nstd value too low (below 1.96)");
  if (_ol_nvox.value() < 250) throw EddyInputError("--ol_nvox value below 250");
  if (IsDiffusion()) _oldef = EDDY::OutlierDefinition(_ol_nstd.value(),_ol_nvox.value(),_diff._ol_pos.value(),_diff._ol_sqr.value());
  else throw EddyInputError("Must look into outlier definition for eddy-fmri");
  if (_ol_ec.value() != 1 && _ol_ec.value() != 2) throw EddyInputError("--ol_ec value must be 1 or 2");
  if (_ol_type.value() != string("sw") && _ol_type.value() != string("gw") && _ol_type.value() != string("both")) throw EddyInputError("--ol_type must be \"sw\", \"gw\" or \"both\"");
  if ((_ol_type.value() == string("gw") || _ol_type.value() == string("both")) && MultiBand().MBFactor() < 2) throw EddyInputError("--ol_type indicating mb-groups without providing mb structure");
  if (_ol_jacut.value() < 0.0) throw EddyInputError("--ol_jacut must be a positive number");

  // Parameters related to slice-to-volume motion correction
  // Make sure that slices only specified in one way (i.e. that only one of --mb, --slspec and --json has been used)
  if ((_mb.set() + _slspec.set() + _json.set()) > 1) throw EddyInputError("--mb, --slspec and --json mutually exclusive");
  // Reasonable values for mb factor and offset
  if (_mb.value() < 1 || _mb.value() > 10) throw EddyInputError("--mb value outside valid range 1-10");
  if (std::abs(_diff._mb_offs.value()) > 1) throw EddyInputError("--mb_offs must be -1, 0 or 1");
  // MB offset only makes sense in context of MB
  if (!_mb.set() && _diff._mb_offs.set()) throw EddyInputError("--mb_offs makes no sense without --mb");
  // Check for valid multi-band structure
  EDDY::MultiBandGroups mbg(imahdr.zsize());
  if (_mb.set() || _slspec.set() || _json.set()) {
    try {
      mbg = this->MultiBand();
    }
    catch (const std::exception& e) { throw EddyInputError("Failed to decode valid multi-band structure. Exception thrown with message: " + std::string(e.what())); }
    if (mbg.NSlices() != imahdr.zsize()) throw EddyInputError("Mismatch between no of slices in multi-band structure and in image file.");
  }
  // Check that movement model order not greater than number of groups
  if (*std::max_element(_mp_order.value().begin(),_mp_order.value().end()) > mbg.NGroups()) throw EddyInputError("--mporder can not be greater than number of slices/mb-groups");
  // Valid slice-to-vol interpolation method?
  if (_s2v_interp.value() != string("spline") && _s2v_interp.value() != string("trilinear")) throw EddyInputError("Invalid --s2v_interp parameter");
  // Valid and reasonable parameters for slice-to-vol (throws with error message from within constructor)
  _s2vparam = EDDY::S2VParam(_mp_order.value(),_s2v_lambda.value(),_s2v_fwhm.value(),_s2v_niter.value());

  // Parameters related to susceptibility-by-movement interaction
  // Check for reasonable values pertaining to MBS (Movement By Susceptibility).
  if (_init_mbs.set() && _init_mbs.value() != std::string("")) {
    if (_mbs_niter.value() < 0 || _mbs_niter.value() > 50) throw EddyInputError("--mbs_niter value outside valid range 0-50");
  }
  else if (_mbs_niter.value() < 1 || _mbs_niter.value() > 50) throw EddyInputError("--mbs_niter value outside valid range 1-50");
  if (_mbs_lambda.value() < 1.0 || _mbs_lambda.value() > 10000.0) throw EddyInputError("--mbs_lambda value outside valid range 1.0-10000.0");
  if (_mbs_ksp.value() < 2.0 || _mbs_ksp.value() > 30.0) throw EddyInputError("--mbs_ksp value outside valid range 2.0-30.0");

  // Parameters related to the Gaussian process
  // Valid hypar cost-function?
  if (_hyparcostfunc.value() != string("MML") && _hyparcostfunc.value() != string("CV") && _hyparcostfunc.value() != string("GPP") && _hyparcostfunc.value() != string("CC")) throw EddyInputError("Invalid --hpcf parameter");
  // Reasonable # of voxels for estimation of hyperparameters
  if (_nvoxhp.value() < 100  || _nvoxhp.value() > 50000) throw EddyInputError("--nvoxhp value outside valid range 100-50000");
  _nvoxhp_internal = static_cast<unsigned int>(_nvoxhp.value());
  // Mimic old behaviour by setting srand to one if --initrand is set without an explicit value
  if (_initrand.set()) {
    if (_initrand.value() == 0) _init_rand = 1;
    else _init_rand = _initrand.value();
  }
  else _init_rand = 0;
  // Reasonable fudge factor for hyperparameters
  if (_hyparfudgefactor.value() < 1.0 || _hyparfudgefactor.value() > 10.0) throw EddyInputError("--ff value outside valid range 1.0-10.0");
  else _hypar_ff_internal = static_cast<double>(_hyparfudgefactor.value());
  // User specified hyperparameters?
  if (_hypar.value().size()) { _fixed_hpar = true; _hypar_internal = _hypar.value(); } else { _fixed_hpar = false; }

  // Parameters related to initialisation of movement/distortion parameters
  // Initialisation file exists if one has been specified
  if (_init.set() && _init.value() != string("")) {
    NEWMAT::Matrix  tmp = MISCMATHS::read_ascii_matrix(_init.value());
    if (tmp.Nrows() == 0) throw EddyInputError("Error when attempting to read --init file " + _init.value());
  }
  // Initialisation for s2v not set if EC/volumetric initialisation not set
  if (_init_s2v.set() && _init_s2v.value() != string("") && (!_init.set() || _init.value() == string(""))) {
    throw EddyInputError("--init_s2v can not be used without --init");
  }
  // Check that s2v initialisation file exists if one has been specified
  if (_init_s2v.set() && _init_s2v.value() != string("")) {
    NEWMAT::Matrix  tmp = MISCMATHS::read_ascii_matrix(_init_s2v.value());
    if (tmp.Nrows() == 0) throw EddyInputError("Error when attempting to read --init_s2v file " + _init_s2v.value());
  }
  // Check that we are not to run any vol-to-vol iterations if s2v initialisation is set
  if (_init_s2v.set() && _init_s2v.value() != string("") && _niter != 0) {
    throw EddyInputError("--init_s2v does not make sense unless --niter=0");
  }
  // Movement-by-susceptibility file exists and has correct dimensions
  if (_init_mbs.set() && _init_mbs.value() != string("")) {
    NEWIMAGE::volume4D<float> mbshdr;
    try { NEWIMAGE::read_volume_hdr_only(mbshdr,_init_mbs.value()); }  // Throws if there is a problem
    catch(...) { throw EddyInputError("Error when attempting to read --init_mbs file " + _init_mbs.value()); }
    if (!samesize(imahdr[0],mbshdr,3,true)) throw EddyInputError("--imain and --init_mbs images must have the same dimensions");
    if (mbshdr.tsize() != int(this->MoveBySuscParam().size())) throw EddyInputError("--init_mbs has wrong number of volumes");
  }

  // Parameters related to the writing of various "extra" output
  // Check that --with_outliers hasn't been set unless --repol is also set
  if (_with_outliers.value() && !_rep_ol.value()) throw EddyInputError("--with_outliers does not make sense without also specifying --repol");

  // Parameters related to things that can be useful for debugging/development
  // Make sure that debug requests are reasonable
  if (_debug_tmp.value() < 0 || _debug_tmp.value() > 4) { throw EddyInputError("--debug must be a value between 0 and 4"); }
  _debug = static_cast<unsigned int>(_debug_tmp.value());
  if (_debug > 0 && (!_dbg_indx_str.set() || _dbg_indx_str.value() == string(""))) {
    _dbg_indx = DebugIndexClass(0,imahdr.tsize()-1);
  }
  else if (_dbg_indx_str.set() && _dbg_indx_str.value() != string("")) {
    _dbg_indx = DebugIndexClass(_dbg_indx_str.value());
  }
  if (_dbg_indx.Max() > static_cast<unsigned int>(imahdr.tsize()-1)) { throw EddyInputError("--dbg_indx out of range"); }
  // If debug is set we should also set initialisation of rand
  if (_debug > 0) _init_rand = true;

  // Make sure we don't attempt to use multiple CPU threads for GPU version
  #ifdef COMPILE_GPU
  if (_nthr.value() > 1) {
    throw EddyInputError("The version compiled for GPU can only use 1 CPU thread (i.e. --nthr=1)");
  }
  #endif

  // Make sure we don't try to use more CPU threads than the hardware supports
  if (_nthr.value() > std::thread::hardware_concurrency()) {
    if (std::thread::hardware_concurrency()) { // This means that if hardware_concurrency isn't implemented we trust the user.
      _nthr.set_T(std::thread::hardware_concurrency());
    }
  }
  else if (_nthr.value() < 1) _nthr.set_T(1);

  // Finally some generally helpful parameters
  // If very_verbose is set we should also set verbose
  if (_very_verbose.value() && !_verbose.value()) _verbose.set_T(true);

  // The session parameter is mandatory for fMRI, but not for diffusion.
  // That is why there are two different fields for it.
  NEWMAT::Matrix sessM;
  // Check for valid session file
  if (IsfMRI() || _diff._session.set()) { // There is/should be a session file
    try {
      if (IsDiffusion()) sessM = MISCMATHS::read_ascii_matrix(_diff._session.value());
      else sessM = MISCMATHS::read_ascii_matrix(_fmri._session.value());
      if (std::min(sessM.Nrows(),sessM.Ncols()) != 1 || std::max(sessM.Nrows(),sessM.Ncols()) != imahdr.tsize()) {
	throw EddyInputError("--session must be an 1xN or Nx1 matrix where N is the number of volumes in --imain");
      }
    }
    catch (...) { 
      if (IsDiffusion()) throw EddyInputError("Error when attempting to read --session file " + _diff._session.value()); 
      else throw EddyInputError("Error when attempting to read --session file " + _fmri._session.value()); 
    }
    if (!session_indicies_kosher(sessM)) throw EddyInputError("Values in --session file seems dodgy");
  }
  else { // There is no (and should not be) a session file.
    _sessvec.resize(imahdr.tsize(),1); // Set single session
  }
  
  // This ends the checking of common parameters
  // Next, check on diffusion parameters.

  if (IsDiffusion()) {
    // First mandatory parameters
    // bvals and bvecs exists and are consistent with image file
    try {
      NEWMAT::Matrix bvecsM = MISCMATHS::read_ascii_matrix(_diff._bvecs.value());
      if (std::min(bvecsM.Nrows(),bvecsM.Ncols()) != 3 || std::max(bvecsM.Nrows(),bvecsM.Ncols()) != imahdr.tsize()) {
	throw EddyInputError("--bvecs should contain a 3xN or Nx3 matrix where N is the number of volumes in --imain");
      }
    }
    catch (...) { throw EddyInputError("Error when attempting to read --bvecs file " + _diff._bvecs.value()); }
    try {
      NEWMAT::Matrix bvalsM = MISCMATHS::read_ascii_matrix(_diff._bvals.value());
      if (std::min(bvalsM.Nrows(),bvalsM.Ncols()) != 1 || std::max(bvalsM.Nrows(),bvalsM.Ncols()) != imahdr.tsize()) {
	throw EddyInputError("--bvals should contain a 1xN or Nx1 matrix where N is the number of volumes in --imain");
      }
    }
    catch (...) { throw EddyInputError("Error when attempting to read --bvals file " + _diff._bvals.value()); }
    if (_diff._bdeltas.value() != std::string("")) {
      try {
	NEWMAT::Matrix bdeltasM = MISCMATHS::read_ascii_matrix(_diff._bdeltas.value());
	if (bdeltasM.Nrows()>bdeltasM.Ncols()) bdeltasM = bdeltasM.t();
	if (bdeltasM.Nrows()!=1 || bdeltasM.Ncols()!=imahdr.tsize()) {
	  throw EddyInputError("--bdeltas should contain a 1xN or Nx1 matrix where N is the number of volumes in --imain");
	}
	for (int i=0; i<bdeltasM.Ncols(); i++) {
	  if (std::fabs(bdeltasM(1,i+1)-1.0)>1e-4 && std::fabs(bdeltasM(1,i+1)+0.5)>1e-4 && std::fabs(bdeltasM(1,i+1))>1e-4) {
	    throw EddyInputError("--bdeltas should only contain values 1.0, -0.5 or 0.0");
	  }
	}
      }
      catch (...) { throw EddyInputError("Error when attempting to read --bdeltas file " + _diff._bdeltas.value()); }
    }

    // Paramaters related to the general running of vanilla eddy
    // Valid range of b-values within a single shell
    if (_diff._brange.value() < 0.0 || _diff._brange.value() > 200.0) throw EddyInputError("Invalid --brange parameter");
    // Valid final resampling method?
    if (_diff._resamp.value() != string("jac") && _diff._resamp.value() != string("jac_nn") && _diff._resamp.value() != string("lsr")) {
      throw EddyInputError("Invalid --resamp parameter");
    }
    // Check that output masking is not off for LSR resampling
    if (_diff._resamp.value() == string("lsr") && _dont_mask_output.value()) throw EddyInputError("You cannot combine --resamp=lsr with --dont_mask_output");
    // Reasonable LSR regularisation weighting
    if (_diff._lsr_lambda.value() < 0.0 || _diff._lsr_lambda.value() > 1.0) throw EddyInputError("--lsr_lambda value outside valid range");

    // Parameters related to eddy current modelling
    // Valid first level model?
    if (_diff._flm.value() != string("movement") && _diff._flm.value() != string("linear") && _diff._flm.value() != string("quadratic") && _diff._flm.value() != string("cubic")) throw EddyInputError("Invalid --flm parameter");
    // Valid second level model?
    if (_diff._slm_str.value() == string("none")) _diff._slm = EDDY::SecondLevelECModelType::None;
    else if (_diff._slm_str.value() == string("linear")) _diff._slm = EDDY::SecondLevelECModelType::Linear;
    else if (_diff._slm_str.value() == string("quadratic")) _diff._slm = EDDY::SecondLevelECModelType::Quadratic;
    else throw EddyInputError("Invalid --slm parameter");
    // Valid first level b0-model?
    if (_diff._b0_flm.value() != string("movement") && _diff._b0_flm.value() != string("linear") && _diff._b0_flm.value() != string("quadratic")) throw EddyInputError("Invalid --b0_flm parameter");
    // Valid second level b0 model?
    if (_diff._b0_slm_str.value() == string("none")) _diff._b0_slm = EDDY::SecondLevelECModelType::None;
    else if (_diff._b0_slm_str.value() == string("linear")) _diff._b0_slm = EDDY::SecondLevelECModelType::Linear;
    else if (_diff._b0_slm_str.value() == string("quadratic")) _diff._b0_slm = EDDY::SecondLevelECModelType::Quadratic;
    else throw EddyInputError("Invalid --b0_slm parameter");
    // Valid model for long (time varying) EC?
    if (_diff._long_ec_str.value() == string("none")) _diff._long_ec_model = EDDY::LongECModelType::None;
    else if (_diff._long_ec_str.value() == string("weights")) _diff._long_ec_model = EDDY::LongECModelType::Individual;
    else if (_diff._long_ec_str.value() == string("tc")) _diff._long_ec_model = EDDY::LongECModelType::IndividualTimeConstant;
    else if (_diff._long_ec_str.value() == string("jointweights")) _diff._long_ec_model = EDDY::LongECModelType::Joint;
    else if (_diff._long_ec_str.value() == string("jointtc")) _diff._long_ec_model = EDDY::LongECModelType::JointTimeConstant;
    else throw EddyInputError("Invalid --lecm parameter");
    if (_diff._long_ec_model != EDDY::LongECModelType::None && mbg.MBFactor() < 2) {
      throw EddyInputError("--lecm can only be used for multi-band data");
    }
    if (_diff._long_ec_str.value() == string("none") && _diff._long_ec_dont_reest.value() == true) {
      throw EddyInputError("--lecm_dont_reest only makes sense together with --lecm");
    }
    if (_diff._long_ec_str.value() == string("none") && _diff._long_ec_sep_offs_move.value() == true) {
      throw EddyInputError("--lecm_sep_offs_move only makes sense together with --lecm");
    }
    
    // Parameters related to outlier detection/replacement
    if (_diff._ol_ss.value() != string("sw") && _diff._ol_ss.value() != string("pooled")) throw EddyInputError("--ol_ss must be \"sw\" (shell-wise) or \"pooled\"");

    // Parameters related to the Gaussian process
    // Valid covariance function?
    if (_diff._covfunc.value() != string("spheri") && _diff._covfunc.value() != string("expo") && _diff._covfunc.value() != string("old")) throw EddyInputError("Invalid --covfunc parameter");

    // Miscallenous parameters related to missing planes/output masking/bvec-rotation
    // Set "internal" copy of _rbvde to allow us to set/reset internally
    _diff._rbvde_internal = _diff._rbvde.value();

    // Parameters related to "tricky" movements for diffusion
    // Valid offset model?
    if (_diff._offset_model.value() != string("linear") && _diff._offset_model.value() != string("linear_lag") &&
	_diff._offset_model.value() != string("quadratic") && _diff._offset_model.value() != string("linear_lag_quadratic") &&
	_diff._offset_model.value() != string("linear_lag_quadratic_lag")) throw EddyInputError("Invalid --offset_model parameter");
    // Set internal parameter for "dont_sep_offs_move"
    _diff._dont_sep_offs_move_internal = _diff._dont_sep_offs_move.value();
    // Check that peas options are compatible
    if (_diff._dont_peas.value() && _diff._use_b0s_for_peas.value()) throw EddyInputError("--dont_peas and --b0_peas cannot both be set");

    // Parameters related to writing of extra output
    // Check that --write_scatter_brain_predictions hasn't been specified for volume-to-volume registration
    if (_diff._write_scatter_brain_predictions.value() && !IsSliceToVol()) throw EddyInputError("--write_scatter_brain_predictions does not make sense without also specifying --mporder > 0");
    // Check that --write_scatter_brain_predictions hasn't been specified without --write_predictions
    if (_diff._write_scatter_brain_predictions.value() && !_write_predictions.value()) throw EddyInputError("--write_scatter_brain_predictions can only be used together with --write_predictions");

    // Parameters related to things that can be useful for debugging/development
    // Make sure the "only" flags are mutually exclusive
    _diff._rb0 = true; _diff._rdwi = true;
    if (_diff._dwi_only.value() && _diff._b0_only.value()) throw EddyInputError("--dwi_only and --b0_only cannot both be set");
    if (_diff._dwi_only.value()) _diff._rb0 = false;
    else if (_diff._b0_only.value()) _diff._rdwi = false;
    // Make sure that test_rot vector is valid if set
    if (_diff._test_rot.set()) {
      if (_diff._test_rot.value().size() < 1 || _diff._test_rot.value().size() > 3) throw EddyInputError("--test_rot must be one, two or three angles");
    }
  } // Here ends the checking of diffusion parameters
  else if (IsfMRI()) { // Here starts checking of parameters specific for fMRI
    // Check for reasonable repetition time
    if (_fmri._rep_time.value() < 0.5 || _fmri._rep_time.value() > 10.0) throw EddyInputError("--rep_time must be between 0.5 and 10 seconds");
    // Check for reasonable echo time
    if (_fmri._echo_time.set() && (_fmri._echo_time.value() < 10.0 || _fmri._echo_time.value() > 100.0)) throw EddyInputError("--echo_time must be between 10 and 100 milliseconds");
  }
}
catch (const EddyInputError& e)
{
  cout << e.what() << endl;
  cout << "Terminating program" << endl;
  exit(EXIT_FAILURE);
} EddyCatch

EDDY::FinalResamplingType EddyCommandLineOptions::ResamplingMethod() const EddyTry
{
  if (IsfMRI() || _diff._resamp.value() == std::string("jac")) return(FinalResamplingType::Jac);
  else if (_diff._resamp.value() == std::string("jac_nn")) return(FinalResamplingType::Jac_NN);
  else if (_diff._resamp.value() == std::string("lsr")) return(FinalResamplingType::LSR);
  else return(FinalResamplingType::Unknown);
} EddyCatch

EDDY::CovarianceFunctionType EddyCommandLineOptions::CovarianceFunction() const EddyTry
{
  if (IsDiffusion()) {
    if (_diff._covfunc.value() == std::string("spheri")) return(EDDY::CovarianceFunctionType::NewSpherical);
    else if (_diff._covfunc.value() == std::string("expo")) return(EDDY::CovarianceFunctionType::Exponential);
    else if (_diff._covfunc.value() == std::string("old")) return(EDDY::CovarianceFunctionType::Spherical);
    else return(EDDY::CovarianceFunctionType::Unknown);
  }
  else if (IsfMRI()) {
    if (_fmri._covfunc.value() == std::string("sqexp")) return(EDDY::CovarianceFunctionType::SquaredExponential);
    else return(EDDY::CovarianceFunctionType::Unknown);
  }
  else return(EDDY::CovarianceFunctionType::Unknown);
} EddyCatch

EDDY::HyParCostFunctionType EddyCommandLineOptions::HyParCostFunction() const EddyTry
{
  if (_hyparcostfunc.value() == std::string("MML")) return(EDDY::HyParCostFunctionType::MML);
  else if (_hyparcostfunc.value() == std::string("CV")) return(EDDY::HyParCostFunctionType::CV);
  else if (_hyparcostfunc.value() == std::string("GPP")) return(EDDY::HyParCostFunctionType::GPP);
  else if (_hyparcostfunc.value() == std::string("CC")) return(EDDY::HyParCostFunctionType::CC);
  else return(EDDY::HyParCostFunctionType::Unknown);
} EddyCatch

float EddyCommandLineOptions::FWHM(unsigned int iter) const EddyTry
{
  if (_fwhm.size()==1) return(_fwhm[0]);
  else if (iter >= _fwhm.size()) throw EddyException("EddyCommandLineOptions::FWHM: iter out of range");
  return(_fwhm[iter]);
} EddyCatch

void EddyCommandLineOptions::SetNIterAndFWHM(unsigned int niter, const std::vector<float>& fwhm) EddyTry
{
  if (fwhm.size() != 1 && fwhm.size() != niter) throw EddyException("EddyCommandLineOptions::SetNIterAndFWHM: mismatch between niter and fwhm");
  _niter = niter;
  if (fwhm.size() == niter) _fwhm = fwhm;
  else _fwhm = std::vector<float>(niter,fwhm[0]);
} EddyCatch

void EddyCommandLineOptions::SetS2VParam(unsigned int order, float lambda, float fwhm, unsigned int niter) EddyTry
{
  std::vector<int>   lorder(1,static_cast<int>(order));
  std::vector<float> llambda(1,lambda);
  std::vector<float> lfwhm(1,fwhm);
  std::vector<int>   lniter(1,static_cast<int>(niter));

  _s2vparam = EDDY::S2VParam(lorder,llambda,lfwhm,lniter);
} EddyCatch

unsigned int S2VParam::NIter(unsigned int  oi) const EddyTry
{
  if (oi >= _niter.size()) throw EddyException("S2VParam::NIter: oi out of range");
  return(static_cast<unsigned int>(_niter[oi]));
} EddyCatch

unsigned int S2VParam::NOrder() const EddyTry
{
  if (_order.size() > 1) return(_order.size());
  else return(static_cast<unsigned int>(_order[0] > 0 ? 1 : 0));
} EddyCatch

unsigned int S2VParam::Order(unsigned int oi) const EddyTry
{
  if (oi >= _order.size()) throw EddyException("S2VParam::Order: oi out of range");
  return(static_cast<unsigned int>(_order[oi]));
} EddyCatch

std::vector<unsigned int> S2VParam::Order() const EddyTry
{
  std::vector<unsigned int> rval(_order.size());
  for (unsigned int i=0; i<_order.size(); i++) rval[i] = static_cast<unsigned int>(_order[i]);
  return(rval);
} EddyCatch

double S2VParam::Lambda(unsigned int oi) const EddyTry
{
  if (oi >= _lambda.size()) throw EddyException("S2VParam::Lambda: oi out of range");
  return(static_cast<double>(_lambda[oi]));
} EddyCatch

std::vector<double> S2VParam::Lambda() const EddyTry
{
  std::vector<double> rval(_lambda.size());
  for (unsigned int i=0; i<_lambda.size(); i++) rval[i] = static_cast<double>(_lambda[i]);
  return(rval);
} EddyCatch

float S2VParam::FWHM(unsigned int oi, unsigned int iter) const EddyTry
{
  if (oi >= this->NOrder()) throw EddyException("S2VParam::FWHM: oi out of range");
  if (iter >= this->NIter(oi)) throw EddyException("S2VParam::FWHM: iter out of range");
  if (_fwhm.size()==1) return(_fwhm[0]);
  else {
    unsigned int indx=0;
    for (unsigned int i=0; i<oi; i++) indx += this->NIter(i);
    return(_fwhm[indx+iter]);
  }
} EddyCatch

std::vector<float> S2VParam::FWHM(unsigned int oi) const EddyTry
{
  if (oi >= NOrder()) throw EddyException("S2VParam::FWHM: oi out of range");
  if (_fwhm.size()==1) {
    std::vector<float> rval(this->NIter(oi),_fwhm[0]);
    return(rval);
  }
  else {
    unsigned int indx=0;
    for (unsigned int i=0; i<oi; i++) indx += this->NIter(i);
    std::vector<float> rval(this->NIter(oi),0.0);
    for (unsigned int i=0; i<this->NIter(oi); i++) rval[i] = _fwhm[indx+i];
    return(rval);
  }
} EddyCatch

EDDY::MultiBandGroups EddyCommandLineOptions::MultiBand() const EddyTry
{
  NEWIMAGE::volume<float> mhdr;
  NEWIMAGE::read_volume_hdr_only(mhdr,_mask.value());
  if (_slspec.set()) {
    EDDY::MultiBandGroups mbg(_slspec.value());
    return(mbg);
  }
  else if (_json.set()) {
    EDDY::JsonReader jr(_json.value());
    EDDY:: MultiBandGroups mbg(jr.SliceOrdering());
    return(mbg);
  }
  else {
    EDDY::MultiBandGroups mbg(mhdr.zsize(),static_cast<unsigned int>(_mb.value()),static_cast<unsigned int>(_diff._mb_offs.value()));
    if (IsDiffusion()) { if (_diff._slorder.set() && _diff._slorder.value() != string("")) mbg.SetTemporalOrder(get_slorder(_diff._slorder.value(),mbg.NGroups())); }
    return(mbg);
  }
} EddyCatch

EDDY::ECModelType EddyCommandLineOptions::FirstLevelModel() const EddyTry
{
  if (IsDiffusion()) {
    if (_diff._flm.value() == string("movement")) return(EDDY::ECModelType::NoEC);
    else if (_diff._flm.value() == string("linear")) return(EDDY::ECModelType::Linear);
    else if (_diff._flm.value() == string("quadratic")) return(EDDY::ECModelType::Quadratic);
    else if (_diff._flm.value() == string("cubic")) return(EDDY::ECModelType::Cubic);
    return(EDDY::ECModelType::Unknown);
  }
  return(EDDY::ECModelType::NoEC); // No EC for fMRI
} EddyCatch

EDDY::ECModelType EddyCommandLineOptions::b0_FirstLevelModel() const EddyTry
{
  if (IsDiffusion()) {
    if (_diff._b0_flm.value() == string("movement")) return(EDDY::ECModelType::NoEC);
    else if (_diff._b0_flm.value() == string("linear")) return(EDDY::ECModelType::Linear);
    else if (_diff._b0_flm.value() == string("quadratic")) return(EDDY::ECModelType::Quadratic);
    return(EDDY::ECModelType::Unknown);
  }
  return(EDDY::ECModelType::NoEC); // No EC for fMRI
} EddyCatch

std::string EddyCommandLineOptions::LongECModelString() const EddyTry
{
  LongECModelType lmt = this->LongECModel();
  switch (lmt) {
  case LongECModelType::None:
    return("No modelling of long EC");
  case LongECModelType::Individual:
    return("Modelling weight for each MB-group of each volume separately.");
  case LongECModelType::Joint:
    return("Modelling weight for each MB-group pooled across all volumes.");
  case LongECModelType::IndividualTimeConstant:
    return("Modelling a separate time constant for each volume.");
  case LongECModelType::JointTimeConstant:
    return("Modelling a single time constant across all volumes.");
  default:
    throw EddyException("EddyCommandLineOptions::LongECModelString: Unknown model type.");
  }
} EddyCatch

enum class OffsetModelType { Linear, LinearPlusLag, Quadratic, QuadraticPlusLinearLag, QuadraticPlusLag, Unknown };

EDDY::OffsetModelType EddyCommandLineOptions::OffsetModel() const EddyTry
{
  if (IsDiffusion()) {
    if (_diff._offset_model.value() == string("linear")) return(EDDY::OffsetModelType::Linear);
    else if (_diff._offset_model.value() == string("linear_lag")) return(EDDY::OffsetModelType::LinearPlusLag);
    else if (_diff._offset_model.value() == string("quadratic")) return(EDDY::OffsetModelType::Quadratic);
    else if (_diff._offset_model.value() == string("linear_lag_quadratic")) return(EDDY::OffsetModelType::QuadraticPlusLinearLag);
    else if (_diff._offset_model.value() == string("linear_lag_quadratic_lag")) return(EDDY::OffsetModelType::QuadraticPlusLag);
    return(EDDY::OffsetModelType::Unknown);
  }
  return(EDDY::OffsetModelType::Unknown);
} EddyCatch

EDDY::OLSumStats EddyCommandLineOptions::OLSummaryStatistics() const EddyTry 
{ if (IsDiffusion()) { 
    if (_diff._ol_ss.value()==std::string("sw")) return(OLSumStats::ShellWise);
    else if (_ol_type.value()==std::string("pooled")) return(OLSumStats::Pooled);
    else return(OLSumStats::Unknown);
  }
  else return(OLSumStats::Unknown); 
} EddyCatch

NEWMAT::ColumnVector EddyCommandLineOptions::HyperParValues() const EddyTry
{
  NEWMAT::ColumnVector rval(_hypar_internal.size());
  for (unsigned int i=0; i<_hypar_internal.size(); i++) rval(i+1) = _hypar_internal[i];
  return(rval);
} EddyCatch

void EddyCommandLineOptions::SetHyperParValues(const NEWMAT::ColumnVector& p) EddyTry
{
  _hypar_internal.resize(p.Nrows());
  for (unsigned int i=0; i<_hypar_internal.size(); i++) _hypar_internal[i] = p(i+1);
  this->SetHyperParFixed(true);
  return;
} EddyCatch

std::vector<float> EddyCommandLineOptions::TestRotAngles() const EddyTry
{
  std::vector<float> rot = _diff._test_rot.value();
  if (rot.size() < 3) rot.resize(3,0.0);
  return(rot);
} EddyCatch

std::pair<int,double> EddyCommandLineOptions::ShellShapeReference(unsigned int i) const EddyTry
{
  if (IsDiffusion()) {
    if (i >= _diff._shell_shape_references.size()) throw EddyException("EddyCommandLineOptions::ShellShapeReference: Index out of bounds");
    return(std::pair<int,double>(_diff._shell_shape_references[i]._indx,_diff._shell_shape_references[i]._b_val));
  }
  return(std::pair<int,double>(-1,-999.0)); // Not difusion
} EddyCatch

NEWIMAGE::interpolation EddyCommandLineOptions::InterpolationMethod() const EddyTry
{
  if (_interp.value() == string("spline")) return(NEWIMAGE::spline);
  else if (_interp.value() == string("trilinear")) return(NEWIMAGE::trilinear);
  else throw EddyException("EddyCommandLineOptions::InterpolationMethod: Invalid interpolation option.");
} EddyCatch

NEWIMAGE::extrapolation EddyCommandLineOptions::ExtrapolationMethod() const EddyTry
{
  if (_extrap.value() == string("periodic")) return(NEWIMAGE::periodic);
  else if (_extrap.value() == string("mirror")) return(NEWIMAGE::mirror);
  else throw EddyException("EddyCommandLineOptions::ExtrapolationMethod: Invalid extrapolation option.");
} EddyCatch

NEWIMAGE::interpolation EddyCommandLineOptions::S2VInterpolationMethod() const EddyTry
{
  if (_s2v_interp.value() == string("spline")) return(NEWIMAGE::spline);
  else if (_s2v_interp.value() == string("trilinear")) return(NEWIMAGE::trilinear);
  else throw EddyException("EddyCommandLineOptions::S2VInterpolationMethod: Invalid interpolation option.");
} EddyCatch

DebugIndexClass::DebugIndexClass(const std::string& in) EddyTry
{
  _indx = parse_commaseparated_numbers(in);
} EddyCatch

std::vector<unsigned int> DebugIndexClass::parse_commaseparated_numbers(const std::string& list) const EddyTry
{
  std::vector<std::string> str_list = parse_commaseparated_list(list);
  std::vector<unsigned int> number_list(str_list.size(),0);
  for (unsigned int i=0; i<str_list.size(); i++) {
    number_list[i] = atoi(str_list[i].c_str());
  }

  return(number_list);
} EddyCatch

std::vector<std::string> DebugIndexClass::parse_commaseparated_list(const std::string&  list) const EddyTry
{
  std::vector<std::string> ostr;

  size_t cur_pos = 0;
  size_t new_pos = 0;
  unsigned int n=0;
  while ((new_pos = list.find_first_of(',',cur_pos)) != string::npos) {
    ostr.resize(++n);
    ostr[n-1] = list.substr(cur_pos,new_pos-cur_pos);
    cur_pos = new_pos+1;
  }
  ostr.resize(++n);
  ostr[n-1] = list.substr(cur_pos,string::npos);

  return(ostr);
} EddyCatch

S2VParam::S2VParam(const std::vector<int>& order, const std::vector<float>& lambda, const std::vector<float>& fwhm,
		   const std::vector<int>& niter) EddyTry : _order(order), _lambda(lambda), _fwhm(fwhm), _niter(niter)
{
  if (_order.size() != _lambda.size()) throw EddyException("Size of --s2v_lambda must match size of --mporder");
  if (_order.size() != _niter.size()) throw EddyException("Size of --s2v_niter must match size of --mporder");
  if (_fwhm.size() != 1 && _fwhm.size() != this->total_niter()) throw EddyException("--s2v_fwhm value must be given once or once per iteration");
  if (this->total_niter() > 100) throw EddyException("You have asked for more than 100 slice-to-volume iterations. Seriously?");
  // Reasonable slice-to-vol FWHM
  for (unsigned int i=0; i<_fwhm.size(); i++) {
    if (_fwhm[i] < 0.0 || _fwhm[i] > 20.0) throw EddyException("--s2v_fwhm value outside valid range 0-20mm");
  }
} EddyCatch

void EddyCommandLineOptions::do_initial_parsing(int argc, char *argv[]) EddyTry
{
  Utilities::OptionParser options(_title,_examples);

  try {
    // First mandatory parameters
    options.add(_imain);
    options.add(_mask);
    options.add(_acqp);
    options.add(_index);
    options.add(_out);
    if (IsDiffusion()) {
      options.add(_diff._bvecs);
      options.add(_diff._bvals);
      options.add(_diff._bdeltas);         // Not mandatory, but groups naturally with bvals and bvecs
      options.add(_diff._data_is_shelled); // Not mandatory, but groups naturally with bvals and bvecs
    }
    if (IsfMRI()) {
      options.add(_fmri._session);
      options.add(_fmri._rep_time);
      options.add(_fmri._echo_time);       // Not mandatory, but groups naturally with repetition time
    }
    // Parameters related to input susceptibility field
    options.add(_topup);
    options.add(_field);
    options.add(_field_mat);
    // Paramaters related to the general running of vanilla eddy
    options.add(_niter_tmp);
    options.add(_fwhm_tmp);
    options.add(_ref_scan_no);
    options.add(_interp);
    options.add(_extrap);
    options.add(_epvalid);
    if (IsDiffusion()) {
      options.add(_diff._resamp);
      options.add(_diff._lsr_lambda);
      options.add(_diff._brange);
    }
    // Parameters related to eddy current modelling
    if (IsDiffusion()) {
      options.add(_diff._flm);
      options.add(_diff._slm_str);
      options.add(_diff._b0_flm);
      options.add(_diff._b0_slm_str);
      options.add(_diff._rep_time);
      options.add(_diff._long_ec_str);
      options.add(_diff._long_ec_dont_reest);
      options.add(_diff._long_ec_sep_offs_move);	
    }
    // Parameters related to outlier detection/replacement
    options.add(_rep_ol);
    if (IsDiffusion()) options.add(_diff._rep_noise);
    options.add(_ol_nstd);
    options.add(_ol_nvox);
    options.add(_ol_ec);
    options.add(_ol_type);
    options.add(_ol_jacut);
    if (IsDiffusion()) {
      options.add(_diff._ol_ss);
      options.add(_diff._ol_pos);
      options.add(_diff._ol_sqr);
    }
    // Parameters related to slice-to-volume motion correction
    options.add(_mb);
    options.add(_slspec);
    options.add(_json);
    options.add(_mp_order);
    options.add(_s2v_lambda);
    options.add(_s2v_niter);
    options.add(_s2v_fwhm);
    options.add(_s2v_interp);
    options.add(_shape_ref_scan_nos);
    // Parameters related to susceptibility-by-movement interaction estimation
    options.add(_estimate_mbs);
    options.add(_mbs_niter);
    options.add(_mbs_lambda);
    options.add(_mbs_ksp);
    if (IsfMRI()) options.add(_fmri._fast_map);
    // Parameters related to the Gaussian process
    if (IsDiffusion()) options.add(_diff._covfunc);
    else if (IsfMRI()) options.add(_fmri._covfunc);
    options.add(_hyparcostfunc);
    options.add(_nvoxhp);
    options.add(_initrand);
    options.add(_hyparfudgefactor);
    options.add(_hypar);
    // Miscallenous parameters related to missing planes/output masking/bvec-rotation
    options.add(_fep);
    options.add(_dont_mask_output);
    options.add(_diff._rbvde);
    // Parameters related to initialisation of movement/distortion parameters
    options.add(_init);
    options.add(_init_s2v);
    options.add(_init_mbs);
    // Parameters related to "tricky" movements for diffusion
    if (IsDiffusion()) {
      options.add(_diff._dont_sep_offs_move);
      options.add(_diff._offset_model);
      options.add(_diff._dont_peas);
      options.add(_diff._use_b0s_for_peas);
      options.add(_diff._session);
    }
    // Parameters related to the writing of various "extra" output
    if (IsDiffusion()) {
      options.add(_diff._fields);
      options.add(_diff._write_cnr_maps);
      options.add(_diff._write_range_cnr_maps);
      options.add(_diff._write_scatter_brain_predictions);
    }
    if (IsfMRI()) options.add(_fmri._write_snr_maps);
    options.add(_dfields);
    options.add(_residuals);
    options.add(_with_outliers);
    options.add(_history);
    options.add(_write_slice_stats);
    options.add(_write_predictions);
    options.add(_no_text_files);
    // Parameters related to things that can be useful for debugging/development
    if (IsDiffusion()) {
      options.add(_diff._dwi_only);
      options.add(_diff._b0_only);
      options.add(_diff._test_rot);
      options.add(_diff._print_mi_values);
      options.add(_diff._print_mi_planes);
    }
    options.add(_debug_tmp);
    options.add(_dbg_indx_str);
    options.add(_log_timings);
    options.add(_nthr);
    // Finally some generally helpful parameters
    options.add(_verbose);
    options.add(_very_verbose);
    options.add(_help);
    if (IsDiffusion()) { // Here we add all the deprecated parameters from the "old" diffusion eddy
      options.add(_diff._sep_offs_move);
      options.add(_diff._peas);
      options.add(_diff._rms);
      options.add(_diff._mb_offs);
      options.add(_diff._slorder);
    }

    // Copies of argc and argv
    char **cargv = argv;
    int cargc = argc;
    // Remove fmri/diffusion tags if they are there
    if (argc > 1) {
      std::string tag(argv[1]);
      std::for_each(tag.begin(),tag.end(),[](char& c){c = std::tolower(c);});
      if (tag==std::string("fmri") || tag==std::string("diffusion")) {
	cargc = argc-1;
	cargv = new char*[cargc];
	cargv[0] = new char[strlen(argv[0])+1];
	std::strcpy(cargv[0],argv[0]);
	for (int i=1; i<cargc; i++) {
	  cargv[i] = new char[strlen(argv[i+1])+1];
	  std::strcpy(cargv[i],argv[i+1]);
	}
      }
    }
    // Parse command line
    // cout << "cargv[0] = " << cargv[0] << ", cargv[1] = " << cargv[1] << ", cargv[2] = " << cargv[2] << endl;
    int i=options.parse_command_line(cargc, cargv);
    if (i < cargc) {
      for (; i<cargc; i++) {
        cerr << "Unknown input: " << cargv[i] << endl;
      }
      exit(EXIT_FAILURE);
    }
    // Clean up if we modified argv
    if (cargv != argv) {
      for (int i=0; i<cargc; i++) delete[] cargv[i];
      delete[] cargv;
    }
    if (_help.value() || !options.check_compulsory_arguments(true)) {
      options.usage();
      exit(EXIT_FAILURE);
    }
  }
  catch(const Utilities::X_OptionError& e) {
    options.usage();
    cerr << endl << e.what() << endl;
    exit(EXIT_FAILURE);
  }

  // Write text files with
  // a. The command that the user used with any expansions performed by the shell
  // b. A file with values for all the parameters.
  ofstream cf;  // Command file
  cf.exceptions(ofstream::failbit | ofstream::badbit);
  try {
    cf.open(_out.value()+std::string(".eddy_command_txt"));
    cf << argv[0];
    for (int ii=1; ii<argc; ii++) cf << " " << argv[ii];
    cf << endl;
    cf.close();
  }
  catch(const ofstream::failure& e) {
    cout << "Writing file " << _out.value()+std::string(".eddy_command_txt") << " failed with message " << e.what() << endl;
    throw EddyException("EddyCommandLineOptions::do_initial_parsing: Writing file failed. Likely misspecification of --out");
  }
  ofstream dpf; // Detailed Parameter File
  dpf.exceptions(ofstream::failbit | ofstream::badbit);
  try {
    Utilities::detailed_output(dpf);
    dpf.open(_out.value()+std::string(".eddy_values_of_all_input_parameters"));
    dpf << "This file contains values (including defaults) for all input parameters to eddy." << endl;
    dpf << options << endl;
    dpf.close();
  }
  catch(const ofstream::failure& e) {
    cout << "Writing file " << _out.value()+std::string(".eddy_values_of_all_input_parameters") << " failed with message " << e.what() << endl;
    throw EddyException("EddyCommandLineOptions::do_initial_parsing: Writing file failed. Likely misspecification of --out");
  }
} EddyCatch

// Makes sure that the indicies makes sense w.r.t. pointing in to
// the acquistition parameter file, and stores them in _indvec.
// N.B. It is _not_ a mistake that the matrix is passed by value.
bool EddyCommandLineOptions::indicies_kosher(NEWMAT::Matrix indx, NEWMAT::Matrix acqp) EddyTry
{
  if (indx.Ncols() > indx.Nrows()) indx = indx.t();
  _indvec.resize(indx.Nrows());
  unsigned int max_indx = static_cast<unsigned int>(indx(1,1));
  for (int i=0; i<indx.Nrows(); i++) {
    _indvec[i] = static_cast<unsigned int>(indx(i+1,1));
    if (fabs(static_cast<double>(_indvec[i])-indx(i+1,1)) > 1e-6) return(false);
    max_indx = std::max(max_indx,_indvec[i]);
  }
  if (max_indx > static_cast<unsigned int>(acqp.Nrows())) return(false);
  else return(true);
} EddyCatch

// Makes sure that the session indicies makes sense and stores them 
// in _sessvec. N.B. It is _not_ a mistake that the matrix is passed 
// by value. 
bool EddyCommandLineOptions::session_indicies_kosher(NEWMAT::Matrix indx) EddyTry
{
  if (indx.Ncols() > indx.Nrows()) indx = indx.t();
  _sessvec.resize(indx.Nrows());
  std::vector<unsigned int> bins;
  for (int i=0; i<indx.Nrows(); i++) {
    unsigned int ii = static_cast<unsigned int>(indx(i+1,1));
    _sessvec[i] = ii;
    if (bins.size() < ii) bins.resize(ii,0);
    bins[ii-1]++;
  }
  // Checks that all sessions have a "reasonable" number of volumes and
  // that they are not too unevenly distributed.
  unsigned int maxbin = 0; unsigned int minbin = 1e6;
  double meanbin = 0.0;
  for (int i=0; i<bins.size(); i++) {
    maxbin = std::max(maxbin,bins[i]);
    minbin = std::min(minbin,bins[i]);
    meanbin += static_cast<double>(bins[i]);
  }
  meanbin /= static_cast<double>(bins.size());
  if (minbin==0 || maxbin > 3*minbin || maxbin > 6*meanbin) return(false);
  return(true);
} EddyCatch

/****************************************************************//**
*
* \brief Routine for sorting shape-reference indicies
*
* Sorts the indices in 'shape_refs' using the shell indicies
* in 'shells' as a key.
*
********************************************************************/
void EddyCommandLineOptions::set_shape_refs(const std::vector<int>&   shape_refs,
					    const std::vector<int>&   shells,
					    const std::vector<double> shell_b_vals) EddyTry
{
  struct shape_ref_shell_pair {
    shape_ref_shell_pair(int ri, int si) : _ri(ri), _si(si) {}
    int _ri; // Index (into vector of all scans) of refs
    int _si; // Index of shells
  };
  std::vector<shape_ref_shell_pair> ri_si_v;
  for (unsigned int i=0; i<shape_refs.size(); i++) ri_si_v.push_back(shape_ref_shell_pair(shape_refs[i],shells[i]));
  std::sort(ri_si_v.begin(),ri_si_v.end(),[](shape_ref_shell_pair& p1,shape_ref_shell_pair& p2){ return(p1._si < p2._si); });
  unsigned int offset = 0;
  if (EddyUtils::Isb0(EDDY::DiffPara(shell_b_vals[0]))) {
    _diff._b0_shape_reference = ri_si_v[0]._ri;
    offset = 1;
  }
  _diff._shell_shape_references.clear();
  for (unsigned int i=offset; i<ri_si_v.size(); i++) {
    _diff._shell_shape_references.push_back(diff::shell_shape_ref_struct(ri_si_v[i]._ri,shell_b_vals[i]));
  }
  _shape_references_set = true;
} EddyCatch

/****************************************************************//**
*
* \brief Routine for reading slice-orders-spec file
*
* The file should be a text-file with as many entries as there are
* slice-groups in the data. In the simplest case that is simply the
* # of slices, and for example in the case of multi-band 3 it would
* be #of_slices/3.
* If the content of the file is for example
* 1 3 5 ... 2 4 6 ...
* It means that the first group (that containing the most basal slice)
* was acquired first followed by the third group (that containing the
* third most basal slice) etc.
*
********************************************************************/
std::vector<unsigned int> EddyCommandLineOptions::get_slorder(const std::string& fname,
							      unsigned int       ngrp) const EddyTry
{
  std::vector<unsigned int> rval;
  try {
    NEWMAT::Matrix tmp = MISCMATHS::read_ascii_matrix(fname);
    if (tmp.Ncols() > tmp.Nrows()) tmp = tmp.t();
    int n = tmp.Nrows();
    int one = tmp.Ncols();
    rval.resize(n);
    if (n != int(ngrp) || one != 1) throw EddyException("Size mismatch between --imain and --slorder file");
    for (int i=0; i<n; i++) {
      if (tmp(i+1,1)<0 || tmp(i+1,1)>ngrp-1) throw EddyException("--slorder file contains invalid entry");
      rval[i] = tmp(i+1,1);
    }
  }
  catch(...) { throw EddyException("Error when attempting to read --slorder file " + fname); }

  return(rval);
} EddyCatch