docs: def parameter_scan(self):
          """
          Scan the database of the model instance to generate and expose parameters.
          
          This is called by other functions when items are added/removed from the model, but can be run by itself if you like
          """
          
          #self.parameter_map = {}
          #self.params = OrderedDict()
  
          cr_list = []
          items = self.database['items']
          #print(items)
  
          for key in items.keys():
              i = items[key]
              #print(i['name'], i['type'])
              if i['type'] == 'product':
                  cr_list.append(i['code'])
  
          no_products = len(cr_list)
  
          self.names = [self.get_name(x) for x in cr_list]
  
          self.matrix = np.zeros((no_products, no_products))
          
          for key in items.keys():
              i = items[key]
              if i['type'] == 'process':
                  inputs = []
                  #print(i['name'])
                  #print([(e['comment'], e['type']) for e in i['exchanges']])
                  for e in i['exchanges']:
                      if e['type'] == 'production':
                          col_code = cr_list.index(e['input'][1])
                          if not 'p_{}_production'.format(col_code) in self.production_params:
                              self.production_params['p_{}_production'.format(col_code)] = {
                                  'function': None,
                                  'description': 'Production parameter for {}'.format(self.get_name(e['input'][1])),
                                  'unit': self.get_unit(e['input'][1]),
                                  'from': e['input'],
                                  'from_name': self.get_name(e['input'][1]),
                                  'type': 'production',
                              }
                          if not 'p_{}_allocation'.format(col_code) in self.allocation_params:
                              self.allocation_params['p_{}_allocation'.format(col_code)] = {
                                  'function': None,
                                  'description': 'Allocation parameter for {}'.format(self.get_name(e['input'][1])),
                                  'unit': "% (as decimal)", 
                                  'from': e['input'],
                                  'from_name': self.get_name(e['input'][1]),
                                  'type': 'allocation',
                              }
  
                      elif e['type'] == 'technosphere':
                          #print(cr_list)
                          row_code = cr_list.index(e['input'][1])
                          inputs.append((row_code, e['amount']))
  
                  for ip in inputs:
                      self.matrix[(ip[0], col_code)] = ip[1]
  
          param_check_list = []
  
          for c, column in enumerate(self.matrix.T):
              for r, i in enumerate(column):
                  if i > 0:
                      p_from = cr_list[r]
                      p_to = cr_list[c]
                      coords = (r, c)
  
                      from_item_type = self.database['items'][(self.database['name'], p_from)]['lcopt_type']
                      #print('{}\t| {} --> {}'.format(coords, self.get_name(p_from), self.get_name(p_to)))
  
                      param_check_list.append('p_{}_{}'.format(coords[0], coords[1]))
  
                      if not 'p_{}_{}'.format(coords[0], coords[1]) in self.params:
                          self.params['p_{}_{}'.format(coords[0], coords[1])] = {
                              'function': None,
                              'normalisation_parameter': '(p_{}_production / p_{}_allocation)'.format(coords[1], coords[1]),
                              'description': 'Input of {} to create {}'.format(self.get_name(p_from), self.get_name(p_to)),
                              'coords': coords,
                              'unit': self.get_unit(p_from),
                              'from': p_from,
                              'from_name': self.get_name(p_from),
                              'to': p_to,
                              'to_name': self.get_name(p_to),
                              'type': from_item_type,
                          }
  
                      #elif 'normalisation_parameter' not in self.params['p_{}_{}'.format(coords[0], coords[1])].keys():
                          #print("Adding normalisation_parameter to {}".format('p_{}_{}'.format(coords[0], coords[1])))
                          #self.params['p_{}_{}'.format(coords[0], coords[1])]['normalisation_parameter'] = '(p_{}_production / p_{}_allocation)'.format(coords[1], coords[1])
                          
                          #print('p_{}_{} already exists'.format(coords[0],coords[1]))
  
                      else:
                          pass  # print("SOMETHING WRONG HERE\n{}\n".format(self.params['p_{}_{}'.format(coords[0], coords[1])]))
  
                      # make sure the parameter is being normalised and allocated properly
                      self.params['p_{}_{}'.format(coords[0], coords[1])]['normalisation_parameter'] = '(p_{}_production / p_{}_allocation)'.format(coords[1], coords[1])
  
                      if not 'p_{}_{}'.format(coords[0], coords[1]) in self.parameter_map:
                          self.parameter_map[(p_from, p_to)] = 'p_{}_{}'.format(coords[0], coords[1])
  
          kill_list = []
          for k in self.params.keys():
              if k not in param_check_list:
                  #print("{} may be obsolete".format(k))
                  kill_list.append(k)
  
          for p in kill_list:
              #print("deleting parameter {}".format(p))
              del self.params[p]
  
          return True