docs: def createExperiment(uuid: str):
      """ Execute an experiment over the model"""
      data = request.get_json()
      G = DelphiModel.query.filter_by(id=uuid).first().model
      if os.environ.get("TRAVIS") is not None:
          config_file="bmi_config.txt"
      else:
          if not os.path.exists("/tmp/delphi"):
              os.makedirs("/tmp/delphi", exist_ok=True)
          config_file="/tmp/delphi/bmi_config.txt"
  
      G.initialize(initialize_indicators = False, config_file=config_file)
      for n in G.nodes(data=True):
          rv = n[1]["rv"]
          rv.partial_t = 0.0
          for variable in data["interventions"]:
              if n[1]["id"] == variable["id"]:
                  # TODO : Right now, we are only taking the first value in the
                  # "values" list. Need to generalize this so that you can have
                  # multiple interventions at different times.
  
                  # TODO : The subtraction of 1 is a TEMPORARY PATCH to address
                  # the mismatch in semantics between the ICM API and the Delphi
                  # model. MUST FIX ASAP.
                  rv.partial_t = variable["values"]["value"]["value"] - 1
                  for s0 in G.s0:
                      s0[f"∂({n[0]})/∂t"] = rv.partial_t
                  break
  
      id = str(uuid4())
      experiment = ForwardProjection(baseType="ForwardProjection", id=id)
      db.session.add(experiment)
      db.session.commit()
  
      result = ForwardProjectionResult(id=id, baseType="ForwardProjectionResult")
      db.session.add(result)
      db.session.commit()
  
      d = dateutil.parser.parse(data["projection"]["startTime"])
  
      n_timesteps = data["projection"]["numSteps"]
  
      for i in range(n_timesteps):
          if data["projection"]["stepSize"] == "MONTH":
              d = d + relativedelta(months=1)
          elif data["projection"]["stepSize"] == "YEAR":
              d = d + relativedelta(years=1)
  
          for n in G.nodes(data=True):
              CausalVariable.query.filter_by(
                  id=n[1]["id"]
              ).first().lastUpdated = d.isoformat()
              result.results.append(
                  {
                      "id": n[1]["id"],
                      "baseline": {
                          "active": "ACTIVE",
                          "time": d.isoformat(),
                          "value": {"baseType": "FloatValue", "value": 1.0},
                      },
                      "intervened": {
                          "active": "ACTIVE",
                          "time": d.isoformat(),
                          "value": {
                              "baseType": "FloatValue",
                              "value": np.median([s[n[0]] for s in G.s0]),
                          },
                      },
                  }
              )
  
          G.update(update_indicators=False)
  
          # Hack for 12-month evaluation - have the partial derivative decay over
          # time to restore equilibrium
  
          tau = 1.0 # Time constant to control the rate of the decay
          for n in G.nodes(data=True):
              for variable in data["interventions"]:
                  if n[1]["id"] == variable["id"]:
                      rv = n[1]["rv"]
                      for s0 in G.s0:
                          s0[f"∂({n[0]})/∂t"] = rv.partial_t * exp(-tau*i)
  
      db.session.add(result)
      db.session.commit()
  
      return jsonify(
          {
              "id": experiment.id,
              "message": "Forward projection sent successfully",
          }
      )