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

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