docs: def _read_inv_ctr(self, tomodir):
          """Read in selected results of the inv.ctr file
  
          Parameters
          ----------
          tomodir: string
              directory path to a tomodir
  
          Returns
          -------
          inv_ctr:    ?
              structure containing inv.ctr data
  
          """
          invctr_file = tomodir + os.sep + 'inv' + os.sep + 'inv.ctr'
          if not os.path.isfile(invctr_file):
              print('inv.ctr not found: {0}'.format(invctr_file))
              print(os.getcwd())
              return 1
  
          # read header
          with open(invctr_file, 'r') as fid:
              lines = fid.readlines()
  
          # check for robust inversion
          is_robust_inversion = False
          nr_of_data_points = None
          for i, line in enumerate(lines):
              if line.startswith('***PARAMETERS***'):
                  raw_value = lines[i + 7].strip()[0]
                  if raw_value == 'T':
                      is_robust_inversion = True
              if line.startswith('# Data points'):
                  nr_of_data_points = int(line[15:].strip())
  
          print('is robust', is_robust_inversion)
  
          # find section that contains the iteration data
          for i, line in enumerate(lines):
              if line.strip().startswith('ID it.'):
                  break
  
          # TODO: check for robust iteration
  
          # we have three types of lines:
          # 1. first iteration line
          # 2. other main iteration lines
          # 3. update lines
  
          # prepare regular expressions for these three types, each in two
          # flavors: robust and non-robust
  
          """
  ! first iteration, robust
  100 FORMAT (t1,a3,t5,i3,t11,g10.4,t69,g10.4,t81,g10.4,t93,i4,t105,g9.3)
  ! first iteration, non-robust
  101 FORMAT (t1,a3,t5,i3,t11,g10.4,t69,g10.4,t81,g10.4,t93,i4)
  
  ! other iterations, robust
  110 FORMAT (t1,a3,t5,i3,t11,g10.4,t23,g10.4,t34,g10.4,t46,g10.4,t58,&
  i6,t69,g10.4,t81,g10.4,t93,i4,t105,g9.3,t117,f5.3)
  ! other iterations, non-robust
  111 FORMAT (t1,a3,t5,i3,t11,g10.4,t23,g10.4,t34,g10.4,t46,g10.4,t58,&
  i6,t69,g10.4,t81,g10.4,t93,i4,t105,f5.3)
  
  ! update iterations, non-robust
  105 FORMAT (t1,a3,t5,i3,t11,g10.4,t23,g9.3,t34,g10.4,t46,g10.4,t58,&
  i6,t105,f5.3)
  ! update iterations, robust
  106 FORMAT (t1,a3,t5,i3,t11,g10.4,t23,g9.3,t34,g10.4,t46,g10.4,t58,&
  i6,t105,g9.3,t117,f5.3)
  
          """
  
          # this identifies a float number, or a NaN value
          reg_float = ''.join((
              '((?:[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?)',
              '|',
              '(?:NaN))'
          ))
  
          reg_int = '(\d{1,3})'
  
          # (t1,a3,t5,i3,t11,g10.4,t69,g10.4,t81,g10.4,t93,i4)
          # first iteration line of non-robust inversion
          reg_it1_norob = ''.join((
              '([a-zA-Z]{1,3})',
              ' *' + reg_int,
              ' *' + reg_float,
              ' *' + reg_float,
              ' *' + reg_float,
              ' *' + reg_int,
          ))
          # first iteration line of robust inversion
          reg_it1_robust = ''.join((
              '([a-zA-Z]{1,3})',
              ' *(\d{1,3})',
              ' *' + reg_float,   # data RMS
              ' *' + reg_float,   # mag RMS
              ' *' + reg_float,   # pha RMS
              ' *' + reg_int,     # nr excluded data
              ' *' + reg_float,   # L1-ratio
          ))
  
          # second-to-last iterations, robust
          reg_it2plus_rob = ''.join((
              '([a-zA-Z]{1,3})',
              ' *(\d{1,3})',
              ' *' + reg_float,   # data RMS
              ' *' + reg_float,   # stepsize
              ' *' + reg_float,   # lambda
              ' *' + reg_float,   # roughness
              ' *' + reg_int,     # CG-steps
              ' *' + reg_float,   # mag RMS
              ' *' + reg_float,   # pha RMS
              ' *' + reg_int,     # nr excluded data
              ' *' + reg_float,   # l1-ratio
              ' *' + reg_float,   # steplength
          ))
  
          # second-to-last iterations, non-robustk
          # (t1,a3,t5,i3,t11,g10.4,t23,g10.4,t34,g10.4,t46,g10.4,t58,&
          # i6,t69,g10.4,t81,g10.4,t93,i4,t105,f5.3)
          reg_it2plus_norob = ''.join((
              '([a-zA-Z]{1,3})',
              ' *(\d{1,3})',
              ' *' + reg_float,   # data RMS
              ' *' + reg_float,   # stepsize
              ' *' + reg_float,   # lambda
              ' *' + reg_float,   # roughness
              ' *' + reg_int,     # CG-steps
              ' *' + reg_float,   # mag RMS
              ' *' + reg_float,   # pha RMS
              ' *' + reg_int,     # nr excluded data
              ' *' + reg_float,   # steplength
          ))
  
          # update robust
          reg_update_rob = ''.join((
              '([a-zA-Z]{1,3})',
              ' *(\d{1,3})',
              ' *' + reg_float,  # data RMS
              ' *' + reg_float,  # stepsize
              ' *' + reg_float,  # lambda
              ' *' + reg_float,  # roughness
              ' *' + reg_int,  # CG-steps
              ' *' + reg_float,  # l1ratio
          ))
          # update non-robust
          reg_update_norob = ''.join((
              '([a-zA-Z]{1,3})',
              ' *(\d{1,3})',
              ' *' + reg_float,  # data RMS
              ' *' + reg_float,  # stepsize
              ' *' + reg_float,  # lambda
              ' *' + reg_float,  # roughness
              ' *' + reg_int,  # CG-steps
              ' *' + reg_float,  # steplength
          ))
  
          # iteration counter
          current_iteration = 0
          iterations = []
  
          for line in lines[i:]:
              linec = line.strip()
              if linec.startswith('IT') or linec.startswith('PIT'):
                  if linec[0:3].strip() == 'IT':
                      it_type = 'DC/IP'
                  else:
                      it_type = 'FPI'
  
                  values = None
  
                  # main iterations
                  if is_robust_inversion:
                      if current_iteration == 0:
                          # first iteration, robust
                          g = re.compile(reg_it1_robust).search(linec).groups()
                          keyfuncs = [
                              (None, None),
                              ('iteration', int),
                              ('dataRMS', float),
                              ('magRMS', float),
                              ('phaRMS', float),
                              ('nrdata', int),
                              ('l1ratio', float),
                          ]
                          values = {}
                          for value, (key, func) in zip(g, keyfuncs):
                              if key is not None:
                                  values[key] = func(value)
                      else:
                          # second-to-last iterations, robust
                          g = re.compile(
                              reg_it2plus_rob
                          ).search(linec).groups()
                          keyfuncs = [
                              (None, None),
                              ('iteration', int),
                              ('dataRMS', float),
                              ('stepsize', float),
                              ('lambda', float),
                              ('roughness', float),
                              ('cgsteps', int),
                              ('magRMS', float),
                              ('phaRMS', float),
                              ('nrdata', int),
                              ('l1ratio', float),
                              ('steplength', float),
                          ]
                          values = {}
                          for value, (key, func) in zip(g, keyfuncs):
                              if key is not None:
                                  values[key] = func(value)
                      values['type'] = 'main'
                      values['main_iteration'] = current_iteration
                      values['it_type'] = it_type
                      iterations.append(values)
                      current_iteration += 1
                  else:
                      if current_iteration == 0:
                          # non-robust, first iteration
                          g = re.compile(reg_it1_norob).search(linec).groups()
  
                          keyfuncs = [
                              (None, None),
                              ('iteration', int),
                              ('dataRMS', float),
                              ('magRMS', float),
                              ('phaRMS', float),
                              ('nrdata', int)
                          ]
                          values = {}
                          for value, (key, func) in zip(g, keyfuncs):
                              if key is not None:
                                  values[key] = func(value)
                      else:
                          g = re.compile(
                              reg_it2plus_norob
                          ).search(linec).groups()
                          keyfuncs = [
                              (None, None),
                              ('iteration', int),
                              ('dataRMS', float),
                              ('stepsize', float),
                              ('lambda', float),
                              ('roughness', float),
                              ('cgsteps', int),
                              ('magRMS', float),
                              ('phaRMS', float),
                              ('nrdata', int),
                              ('steplength', float),
                          ]
                          values = {}
                          for value, (key, func) in zip(g, keyfuncs):
                              if key is not None:
                                  values[key] = func(value)
                      values['type'] = 'main'
                      values['it_type'] = it_type
                      values['main_iteration'] = current_iteration
                      iterations.append(values)
                      current_iteration += 1
              elif linec.startswith('UP'):
                  # update iterations
                  if is_robust_inversion:
                      # robust
                      g = re.compile(
                          reg_update_rob
                      ).search(linec).groups()
                      keyfuncs = [
                          (None, None),
                          ('iteration', int),
                          ('dataRMS', float),
                          ('stepsize', float),
                          ('lambda', float),
                          ('roughness', float),
                          ('cgsteps', int),
                          ('l1-ratio', float),
                      ]
                      values = {}
                      for value, (key, func) in zip(g, keyfuncs):
                          if key is not None:
                              values[key] = func(value)
                  else:
                      g = re.compile(
                          reg_update_norob
                      ).search(linec).groups()
                      keyfuncs = [
                          (None, None),
                          ('iteration', int),
                          ('dataRMS', float),
                          ('stepsize', float),
                          ('lambda', float),
                          ('roughness', float),
                          ('cgsteps', int),
                          ('steplength', float),
                      ]
                      values = {}
                      for value, (key, func) in zip(g, keyfuncs):
                          if key is not None:
                              values[key] = func(value)
                  values['type'] = 'update'
                  values['it_type'] = it_type
                  values['main_iteration'] = current_iteration
                  iterations.append(values)
  
          df = pd.DataFrame(iterations)
          df = df.reindex_axis([
              'iteration',
              'main_iteration',
              'it_type',
              'type',
              'dataRMS',
              'magRMS',
              'phaRMS',
              'lambda',
              'roughness',
              'cgsteps',
              'nrdata',
              'steplength',
              'stepsize',
              'l1ratio',
          ], axis=1)
  
          df['nrdata'] = nr_of_data_points - df['nrdata']
          return df