docs: def run_simulations(self, param_list, show_progress=True):
          """
          Run several simulations specified by a list of parameter combinations.
  
          Note: this function does not verify whether we already have the
          required simulations in the database - it just runs all the parameter
          combinations that are specified in the list.
  
          Args:
              param_list (list): list of parameter combinations to execute.
                  Items of this list are dictionaries, with one key for each
                  parameter, and a value specifying the parameter value (which
                  can be either a string or a number).
              show_progress (bool): whether or not to show a progress bar with
                  percentage and expected remaining time.
          """
  
          # Make sure we have a runner to run simulations with.
          # This can happen in case the simulation campaign is loaded and not
          # created from scratch.
          if self.runner is None:
              raise Exception("No runner was ever specified"
                              " for this CampaignManager.")
  
          # Return if the list is empty
          if param_list == []:
              return
  
          # Check all parameter combinations fully specify the desired simulation
          desired_params = self.db.get_params()
          for p in param_list:
              # Besides the parameters that were actually passed, we add the ones
              # that are always available in every script
              passed = list(p.keys())
              available = ['RngRun'] + desired_params
              if set(passed) != set(available):
                  raise ValueError("Specified parameter combination does not "
                                   "match the supported parameters:\n"
                                   "Passed: %s\nSupported: %s" %
                                   (sorted(passed), sorted(available)))
  
          # Check that the current repo commit corresponds to the one specified
          # in the campaign
          if self.check_repo:
              self.check_repo_ok()
  
          # Build ns-3 before running any simulations
          # At this point, we can assume the project was already configured
          self.runner.configure_and_build(skip_configuration=True)
  
          # Shuffle simulations
          # This mixes up long and short simulations, and gives better time
          # estimates.
          shuffle(param_list)
  
          # Offload simulation execution to self.runner
          # Note that this only creates a generator for the results, no
          # computation is performed on this line.
          results = self.runner.run_simulations(param_list,
                                                self.db.get_data_dir())
  
          # Wrap the result generator in the progress bar generator.
          if show_progress:
              result_generator = tqdm(results, total=len(param_list),
                                      unit='simulation',
                                      desc='Running simulations')
          else:
              result_generator = results
  
          # Insert result object in db. Using the generator here ensures we
          # save results as they are finalized by the SimulationRunner, and
          # that they are kept even if execution is terminated abruptly by
          # crashes or by a KeyboardInterrupt.
          for result in result_generator:
              self.db.insert_result(result)