{
  stdenv
, lib
, stdexp
, bsc
, targetMachine
, stages
, garlicTools

# Options for the experiment
, enableCTF ? false
# Number of cases tested
, steps ? 6
# nbody iterations
, timesteps ? 10
# nbody total number of particles
, particles ? null
, loops ? 10
, nblocks0 ? null
}:

with lib;
with garlicTools;

let

  defaultOpt = var: def: if (var != null) then var else def;

  machineConfig = targetMachine.config;
  inherit (machineConfig) hw;

  # Initial variable configuration
  varConf = with bsc; {
    # Create a list with values 2^n with n from 0 to (steps - 1) inclusive
    i = expRange 2 0 (steps - 1);
    nodes = [ 1 2 4 8 16 ];
    gitBranch = [
      "garlic/tampi+send+oss+task"
      "garlic/tampi+isend+oss+task"
      "garlic/mpi+send+oss+task"
    ];
  };

  # Generate the complete configuration for each unit
  genConf = var: fix (self: var // targetMachine.config // {
    expName = "nbody-scaling";
    unitName = self.expName +
      "-nb${toString self.nblocks}"+
      "-nodes${toString self.nodes}";

    inherit (machineConfig) hw;

    # nbody options
    particles = defaultOpt particles (4096 * self.hw.cpusPerSocket);
    nblocks0 = defaultOpt nblocks0 (self.hw.cpusPerSocket / 2);
    # The number of blocks is then computed from the multiplier "i" and
    # the initial number of blocks "nblocks0"
    nblocks = self.i * self.nblocks0;

    totalTasks = self.ntasksPerNode * self.nodes;
    particlesPerTask = self.particles / self.totalTasks;
    blocksize = self.particlesPerTask / self.nblocks;
    cc = bsc.icc;
    mpi = bsc.impi;
    cflags = "-g";
    inherit timesteps enableCTF loops;

    # Resources
    qos = "debug";
    cpusPerTask = self.hw.cpusPerSocket;
    ntasksPerNode = self.hw.socketsPerNode;
    jobName = self.unitName;
  });

  # Compute the array of configurations
  configs = stdexp.buildConfigs {
    inherit varConf genConf;
  };

  perf = {nextStage, conf, ...}: with conf; stages.perf {
    inherit nextStage;
    perfOptions = "record --call-graph dwarf -o \\$\\$.perf";
  };

  ctf = {nextStage, conf, ...}: with conf; stages.exec {
    inherit nextStage;
    env = optionalString (conf.enableCTF) ''
      export NANOS6_CONFIG_OVERRIDE="version.instrument=ctf,\
        instrument.ctf.converter.enabled=false"
    '';
  };

  exec = {nextStage, conf, ...}: with conf; stages.exec {
    inherit nextStage;
    argv = [ "-t" timesteps "-p" particles ];
  };

  program = {nextStage, conf, ...}: with conf;
    let
      customPkgs = stdexp.replaceMpi conf.mpi;
    in
      customPkgs.apps.nbody.override ({
        inherit (conf) cc blocksize mpi gitBranch cflags;
      });

  pipeline = stdexp.stdPipeline ++ [ ctf exec program ];

in
 
  stdexp.genExperiment { inherit configs pipeline; }