// creates a plan, with specified start time and state-of-charge
 void createPlan(Tariff tariff, double initialCharging) {
   needs = getFutureEnergyNeeds(start, size, initialCharging);
   // update size to use all of last ShiftEnergy instance
   int newSize = 0;
   for (ShiftEnergy need : needs) newSize += need.getDuration();
   size = newSize;
   LpPlan plan = new LpPlan(tariff, needs, size);
   usage = plan.getSolution();
   slack = plan.getSlack();
   updateNeeds();
 }
 // returns the ShiftEnergy array used to create the plan,
 // decorated with the most recent solution
 ShiftEnergy[] updateNeeds() {
   if (null == needs) return null;
   int usageIndex = 0;
   for (int i = 0; i < needs.length; i++) {
     ShiftEnergy se = needs[i];
     se.setRecommendedUsage(
         Arrays.copyOfRange(usage, usageIndex, usageIndex + se.getDuration()));
     usageIndex += se.getDuration();
     se.setSlack(slack[i]);
   }
   return needs;
 }
  // Computes energy use by chargers in the current timeslot.
  // Remember that the plan has computed usage in terms of AC power,
  // while the energy going into the batteries is lower by
  // the chargeEfficiency value.
  double useEnergy(double regulation) {
    TariffSubscription subscription = getSubscription();
    Tariff tariff = subscription.getTariff();
    ensureCapacityPlan(tariff);

    // positive regulation means we lost energy in the last timeslot
    // and should make it up in the remainder of the shift
    addEnergyCharging(-regulation * chargeEfficiency);
    ShiftEnergy need = plan.getCurrentNeed(getNowInstant());
    if (need.getDuration() <= 0) {
      log.error(getName() + " negative need duration " + need.getDuration());
    }
    need.addEnergy(-regulation);

    // Compute the max and min we could possibly use in this timeslot
    // -- start with max and avail for remainder of shift
    double max = nChargers * maxChargeKW * need.getDuration(); //  shift
    double avail = // for remainder of shift
        nBatteries * batteryCapacity - getCapacityInUse() - getEnergyCharging();
    double maxUsable = Math.min(max, avail) / chargeEfficiency;
    double needed = need.getEnergyNeeded();

    double used = 0;
    RegulationCapacity regCapacity = null;
    if (needed >= maxUsable) {
      // we just use the max, and allow no regulation capacity
      log.info(
          getName()
              + ": no slack - need "
              + needed
              + ", max "
              + max
              + ", avail "
              + avail
              + ", dur "
              + need.getDuration());
      used = Math.min(maxUsable, (needed / need.getDuration()));
      regCapacity = new RegulationCapacity(subscription, 0.0, 0.0);
    } else if (tariff.isTimeOfUse() || tariff.isVariableRate()) {
      // if the current tariff is not a flat rate, we will just use the
      // planned amout, without offering regulation capacity
      // TODO - figure out how to combine variable prices with regulation
      used = need.getRecommendedUsage()[need.getUsageIndex()];
      regCapacity = new RegulationCapacity(subscription, 0.0, 0.0);
    } else {
      // otherwise use energy to maximize regulation capacity
      double slack = (maxUsable - needed) / need.getDuration() / 2.0;
      log.info(
          getName()
              + " needed "
              + needed
              + ", maxUsable "
              + maxUsable
              + ", duration "
              + need.getDuration());
      used = needed / need.getDuration() + slack;
      regCapacity = new RegulationCapacity(subscription, slack, -slack);
    }

    // use it
    addEnergyCharging(used * chargeEfficiency);
    getSubscription().setRegulationCapacity(regCapacity);
    log.info(
        getName()
            + " uses "
            + used
            + "kWh, reg cap ("
            + regCapacity.getUpRegulationCapacity()
            + ", "
            + regCapacity.getDownRegulationCapacity()
            + ")");
    need.tick();
    need.addEnergy(used);
    return used;
  }