1#!/usr/local/bin/python3.8
2
3# python3 status: compatible
4
5# system libraries
6import sys, os
7
8try:    sys.path.extend(['.', '%s/abin' % os.getenv('HOME')])
9except: pass
10
11# AFNI libraries
12from afnipy import option_list as OPT
13from afnipy import afni_util as UTIL
14from afnipy import lib_subjects as SUBJ
15from afnipy import lib_vars_object as VO
16from afnipy import lib_surf_clustsim as CLUST
17
18# ----------------------------------------------------------------------
19# globals
20
21g_command_help = """
22=============================================================================
23slow_surf_clustsim.py    - generate a tcsh script to run clustsim on surface
24
25------------------------------------------
26
27   examples: ~1~
28
29   1. basic: give 3 required inputs, all else is default ~2~
30
31      While a blur of 4.0 is the default, it is included for clarity.
32
33        slow_surf_clustsim.py -save_script surf.clustsim        \\
34            -uvar spec_file sb23_lh_141_std.spec                \\
35            -uvar surf_vol sb23_SurfVol_aligned+orig            \\
36            -uvar blur 4.0                                      \\
37            -uvar vol_mask mask_3mm+orig                        \\
38
39
40   2. more advanced, but still based on EPI analysis ~2~
41
42      Specify p-values, blur size and number of iterations, along with the
43      script name and results directory, use 10000 iterations, instead of
44      the default 1000.
45
46        slow_surf_clustsim.py -save_script surf.clustsim        \\
47            -uvar spec_file sb23_lh_141_std.spec                \\
48            -uvar surf_vol sb23_SurfVol_aligned+orig            \\
49            -uvar vol_mask mask_3mm+orig                        \\
50            -uvar pthr_list 0.05 0.01 0.002 0.001 0.0002 0.0001 \\
51            -uvar blur 8.0                                      \\
52            -uvar niter 10000                                   \\
53            -save_script csim.10000                             \\
54            -uvar results_dir clust.results.10000
55
56
57   3. basic, but on the surface (so no vol_mask is provided) ~2~
58
59        slow_surf_clustsim.py -save_script surf.sim.3           \\
60            -on_surface yes                                     \\
61            -uvar blur 3.0                                      \\
62            -uvar spec_file sb23_lh_141_std.spec                \\
63            -uvar surf_vol sb23_SurfVol_aligned+orig
64
65      One can also add a surface mask via '-uvar surf_mask smask_lh.gii'.
66
67
68   Note: it is appropriate to use a volume mask on the same grid as the data to
69         be analyzed, which is to say either the EPI grid (for functional
70         analysis) or perhaps the anatomical grid (for anatomical analysis,
71         such as of thickness measures).
72
73   Note: the niter values should match between this program and
74         quick.alpha.vals.py.
75
76------------------------------------------
77
78   applying the results: ~1~
79
80      The result of processing should be one z.max.* file for each uncorrected
81      p-value input to the program (or each default).  These files contain the
82      maximum cluster sizes (in mm^2), per z-score/p-value, and are named using
83      the corresponding p-value, e.g. z.max.area.0.001 corresponds to p=0.001.
84
85      To get the cluster size required for some uncorrected p-value, run
86      quick.alpha.vals.py on the z.max.area file corresponding to the desired
87      p-value, and note the cluster area required for the chosen corrected p.
88
89      For example, running this:
90
91           quick.alpha.vals.py -niter 1000 z.max.area.0.001
92
93      might show that a minimum cluster size of 113 mm^2 would correspond to a
94      corrected p=0.05.
95
96      Use of -niter should match that from slow_surf_clustsim.py.
97
98------------------------------------------
99
100   script outline: ~1~
101
102        set control variables
103        create and enter results directory
104        convert p-value list (pthr_list) to z-scores (zthr_list)
105        create dummy time series of length itersize
106        for each iter ( iteration list )
107            3dcalc: generate noise volume
108            3dVol2Surf: map noise to surface
109            SurfSmooth: blur to FWHM
110            for each index ( itersize list )
111                for each zthr ( zthr_list )
112                    SurfClust: make clust file clust.out.$iter.$index.$zthr
113        extract lists of maximum areas
114
115------------------------------------------
116
117   terminal options: ~1~
118
119      -help                     : show this help
120      -hist                     : show module history
121      -show_default_cvars       : list default control variables
122      -show_default_uvars       : list default user variables
123      -show_valid_opts          : list valid options
124      -ver                      : show current version
125
126   other options: ~1~
127      -on_surface yes/no        : if yes, start from noise on the surface
128                                  (so no volume data is involved)
129      -print_script             : print script to terminal
130      -save_script FILE         : save script to given file
131      -uvar value ...           : set the user variable
132                                  (use -show_default_uvars to see user vars)
133      -verb LEVEL               : set the verbosity level
134
135-----------------------------------------------------------------------------
136R Reynolds    June 2011
137=============================================================================
138"""
139
140
141class MyInterface:
142   """interface class for MyLibrary (whatever that is)
143
144      This uses lib_1D.py as an example."""
145   def __init__(self, verb=1):
146      # main variables
147      self.status          = 0                       # exit value
148      self.valid_opts      = None
149      self.user_opts       = None
150
151      # general variables
152      self.verb            = verb
153
154      # initialize valid_opts
155      self.valid_opts = self.get_valid_opts()
156
157   def get_valid_opts(self):
158      vopts = OPT.OptionList('valid opts')
159
160      # short, terminal arguments
161      vopts.add_opt('-help', 0, [], helpstr='display program help')
162      vopts.add_opt('-hist', 0, [], helpstr='display the modification history')
163      vopts.add_opt('-show_default_cvars',0,[],helpstr='show default cvars')
164      vopts.add_opt('-show_default_uvars',0,[],helpstr='show default uvars')
165      vopts.add_opt('-show_valid_opts',0,[],helpstr='display all valid options')
166      vopts.add_opt('-ver', 0, [],helpstr='display the current version number')
167
168      vopts.add_opt('-cvar', -2, [], helpstr='set control variable')
169      vopts.add_opt('-uvar', -2, [], helpstr='set user variable to value')
170
171      # general options
172      vopts.add_opt('-on_surface', 1, [],
173                    acplist=['yes', 'no'],
174                    helpstr='work directly on the surface (yes/no)')
175      vopts.add_opt('-print_script', 0, [],
176                    helpstr='print script to terminal window')
177      vopts.add_opt('-save_script', 1, [],
178                    helpstr='write script to given file')
179      vopts.add_opt('-verb', 1, [],helpstr='set the verbose level (default=1)')
180
181      return vopts
182
183   def process_options(self):
184
185      argv = sys.argv
186
187      if len(argv) == 0:        # non-gui out
188         print(g_command_help)
189         return 1
190
191      # process any optlist_ options
192      self.valid_opts.check_special_opts(argv)
193
194      # process terminal options without the option_list interface
195      # (so that errors are not reported)
196
197      # if no arguments are given, apply -help
198      if len(argv) <= 1 or '-help' in argv:
199         print(g_command_help)
200         return 1
201
202      if '-hist' in argv:
203         print(CLUST.g_history)
204         return 1
205
206      if '-show_default_cvars' in argv:
207         CLUST.g_ctrl_defs.show('')
208         return 1
209
210      if '-show_default_uvars' in argv:
211         CLUST.g_user_defs.show('')
212         return 1
213
214      if '-show_valid_opts' in argv:
215         self.valid_opts.show('', 1)
216         return 1
217
218      if '-ver' in argv:
219         print(CLUST.g_version)
220         return 1
221
222      # ============================================================
223      # read options specified by the user
224      self.user_opts = OPT.read_options(argv, self.valid_opts)
225      uopts = self.user_opts            # convenience variable
226      if not uopts: return -1           # error condition
227
228      # ------------------------------------------------------------
229      # init subject options struct
230
231      self.cvars = VO.VarsObject('control vars from command line')
232      self.uvars = VO.VarsObject('user vars from command line')
233
234      val, err = uopts.get_type_opt(int, '-verb')
235      if val != None and not err: self.verb = val
236
237      SUBJ.set_var_str_from_def('cvars', 'verb', ['%d'%self.verb], self.cvars,
238                                 defs=CLUST.g_ctrl_defs)
239
240      # first process all setup options
241      errs = 0
242      for opt in uopts.olist:
243         # skip -verb (any terminal option should block getting here)
244         if opt.name == '-verb':                continue
245
246         # and skip and post-setup options (print command, save, etc.)
247         elif opt.name == '-print_script':      continue
248         elif opt.name == '-save_script':       continue
249
250         # now go after "normal" options
251
252         elif opt.name == '-on_surface':
253            val, err = uopts.get_string_list('', opt=opt)
254            if val == None or err: return -1
255            if self.cvars.set_var_with_defs(opt.name[1:],val,CLUST.g_ctrl_defs,
256                        as_type=1, oname='cvars', verb=self.verb) < 0:
257               errs += 1
258               continue
259
260         # cvar requires at least 2 parameters, name and value
261         elif opt.name == '-cvar':
262            val, err = uopts.get_string_list('', opt=opt)
263            if val == None or err: return -1
264            # go after verb, in particular
265            if val[0] == 'verb':
266               try: self.verb = int(val[1])
267               except:
268                  print("** failed to set 'verb' level")
269                  errs += 1
270                  continue
271            # and set it from the form name = [value_list]
272            if SUBJ.set_var_str_from_def('cvars', val[0], val[1:], self.cvars,
273                        CLUST.g_ctrl_defs, verb=self.verb) < 0:
274               errs += 1
275               continue
276
277         # uvar requires at least 2 parameters, name and value
278         elif opt.name == '-uvar':
279            val, err = uopts.get_string_list('', opt=opt)
280            if val == None or err: return -1
281            # and set it from the form name = [value_list]
282            if SUBJ.set_var_str_from_def('uvars', val[0], val[1:], self.uvars,
283                        CLUST.g_user_defs, verb=self.verb) < 0:
284               errs += 1
285               continue
286
287         else:
288            print('** unknown option %s' % opt.name)
289            errs += 1
290
291      if self.verb > 2:
292         print('-' * 75)
293         self.uvars.show('post-init uvars', name=0)
294         self.cvars.show('post-init cvars', name=0)
295         print('-' * 75)
296
297      if errs:    return -1
298      else:       return  0     # no error, and continue on return
299
300   def execute(self):
301
302      if not self.ready_for_action(): return 1
303
304      if self.verb > 1: print('-- processing...')
305
306      uopts = self.user_opts
307
308      if uopts.find_opt('-print_script'): self.print_script()
309
310      opt = uopts.find_opt('-save_script')
311      if opt != None:
312         val, err = uopts.get_string_opt('', opt=opt)
313         if val != None and not err: self.save_script(val)
314
315      return 0
316
317   def ready_for_action(self):
318      """perform any final tests before execution"""
319
320      ready = 1
321
322      return ready
323
324   def print_script(self):
325      """create script and print to terminal"""
326
327      ctest, cmd = self.get_script()
328      print(cmd)
329
330   def save_script(self, fname):
331
332      ctest, cmd = self.get_script()
333      if cmd == '': return
334
335      if ctest.write_script(fname):
336         print('** failed to write slow_surf_clustsim script to disk')
337
338   def get_script(self):
339      """return the SurfClust object and script
340         (print warnings and errors to screen)"""
341
342      ctest = CLUST.SurfClust(self.cvars, self.uvars, argv=sys.argv)
343
344      nwarn, wstr = ctest.get_warnings()
345      status, mesg = ctest.get_script()
346
347      if status:        # only show errors
348         print('%s\nERRORS:\n\n%s\n' % (75*'*', mesg))
349         cmd = ''
350      else:
351         if wstr: print('%s\n**** Warnings:\n\n%s\n%s\n' % (75*'-',wstr,75*'-'))
352         cmd = '### surf clust script:\n\n%s\n' % mesg
353
354      return ctest, cmd
355
356   def test(self, verb=3):
357      print('------------------------ initial tests -----------------------')
358      self.verb = verb
359
360      print('------------------------ reset files -----------------------')
361
362      print('------------------------ should fail -----------------------')
363
364      print('------------------------ more tests ------------------------')
365
366      return None
367
368def main():
369   me = MyInterface()
370   if not me: return 1
371   if me.status: return me.status
372
373   rv = me.process_options()
374   if rv > 0:  return 0 # terminal success
375   if rv < 0:  return 1 # terminal failure
376   # else rv == 0, so continue
377
378   rv = me.execute()
379   if rv > 0: return 1
380
381   return me.status
382
383if __name__ == '__main__':
384   sys.exit(main())
385
386
387