docs: def pso(self, n_particles=10, n_iterations=10, lowerLimit=-0.2, upperLimit=0.2, threadCount=1, mpi=False, print_key='default'):
          """
          returns the best fit for the lense model on catalogue basis with particle swarm optimizer
          """
          init_pos = self.chain.get_args(self.chain.kwargs_data_init)
          num_param = self.chain.num_param
          lowerLimit = [lowerLimit] * num_param
          upperLimit = [upperLimit] * num_param
          if mpi is True:
              pso = MpiParticleSwarmOptimizer(self.chain, lowerLimit, upperLimit, n_particles, threads=1)
          else:
              pso = ParticleSwarmOptimizer(self.chain, lowerLimit, upperLimit, n_particles, threads=threadCount)
          if not init_pos is None:
              pso.gbest.position = init_pos
              pso.gbest.velocity = [0]*len(init_pos)
              pso.gbest.fitness, _ = self.chain.likelihood(init_pos)
          X2_list = []
          vel_list = []
          pos_list = []
          time_start = time.time()
          if pso.isMaster():
              print('Computing the %s ...' % print_key)
          num_iter = 0
          for swarm in pso.sample(n_iterations):
              X2_list.append(pso.gbest.fitness*2)
              vel_list.append(pso.gbest.velocity)
              pos_list.append(pso.gbest.position)
              num_iter += 1
              if pso.isMaster():
                  if num_iter % 10 == 0:
                      print(num_iter)
          if not mpi:
              result = pso.gbest.position
          else:
              result = MpiUtil.mpiBCast(pso.gbest.position)
          kwargs_data = self.chain.update_data(result)
          if mpi is True and not pso.isMaster():
              pass
          else:
              time_end = time.time()
              print("Shifts found: ", result)
              print(time_end - time_start, 'time used for PSO', print_key)
          return kwargs_data, [X2_list, pos_list, vel_list, []]