@Override
    public org.powertac.common.CapacityProfile getCapacityProfile(Tariff tariff) {
      double usageSign = bundle.getPowerType().isConsumption() ? +1 : -1;

      // New code: shifted prediction - correct for tariff-eval purposes
      // ===============================================================
      double[] newForecast;
      // new code
      HashMap<CapacityOriginator, double[]> originator2usage = new HashMap<>();
      for (CapacityOriginator capacityOriginator : bundle.getCapacityOriginators()) {
        double[] usageForecast = new double[CapacityProfile.NUM_TIMESLOTS];
        // BUG FIX: this function is called from forecastCost() and used
        // by TariffEvaluationHelper, which assumes the forcast starts
        // at the next timeslot
        CapacityProfile forecast = capacityOriginator.getForecastForNextTimeslot();
        for (int i = 0; i < CapacityProfile.NUM_TIMESLOTS; ++i) {
          double hourlyUsage = usageSign * forecast.getCapacity(i);
          usageForecast[i] = hourlyUsage; // don't divide yet / bundle.getPopulation();
        }
        originator2usage.put(capacityOriginator, usageForecast);
      }

      // Refactored the following code for LearningUtilityOptimizer - shift profile
      //
      // create dummy subscription for the above usage vector:
      // 1 population member under 'tariff', (consuming the sum
      // of the originators' usage)
      TariffSubscription dummySubscription = new DummyTariffSubscription(getCustomerInfo(), tariff);
      newForecast = adjustForecastPerTariff(originator2usage, dummySubscription, bundle);

      double[] result = newForecast;
      Instant start = service.getTimeService().getCurrentTime().plus(TimeService.HOUR);
      return new org.powertac.common.CapacityProfile(result, start);
    }
  DefaultUtilityOptimizer(CustomerStructure customerStructure, List<CapacityBundle> bundles) {
    this.customerStructure = customerStructure;
    this.capacityBundles = bundles;
    this.evaluatorMap = new HashMap<>();

    // create evaluation wrappers and tariff evaluators for each bundle
    for (CapacityBundle bundle : bundles) {
      TariffSubscriberStructure subStructure = bundle.getSubscriberStructure();
      TariffEvaluator evaluator =
          new TariffEvaluator(new TariffEvaluationWrapper(bundle))
              .withChunkSize(Math.max(1, bundle.getPopulation() / 1000))
              .withTariffSwitchFactor(subStructure.getTariffSwitchFactor())
              .withPreferredContractDuration(subStructure.getExpectedDuration())
              .withInconvenienceWeight(subStructure.getInconvenienceWeight())
              .withRationality(subStructure.getLogitChoiceRationality())
              .withEvaluateAllTariffs(true);
      evaluator.initializeCostFactors(
          subStructure.getExpMeanPriceWeight(),
          subStructure.getMaxValuePriceWeight(),
          subStructure.getRealizedPriceWeight(),
          subStructure.getTariffVolumeThreshold());
      evaluator.initializeInconvenienceFactors(
          subStructure.getTouFactor(),
          subStructure.getTieredRateFactor(),
          subStructure.getVariablePricingFactor(),
          subStructure.getInterruptibilityFactor());
      evaluatorMap.put(bundle, evaluator);
    }
  }
 // @StateChange
 private void subscribe(Tariff tariff, CapacityBundle bundle, int customerCount, boolean verbose) {
   getTariffMarket().subscribeToTariff(tariff, bundle.getCustomerInfo(), customerCount);
   if (verbose) {
     log.info(
         bundle.getName()
             + ": Subscribed "
             + customerCount
             + " customers to tariff "
             + tariff.getId()
             + " successfully");
   }
 }
 // Tariff Evaluation --------------------------------
 @Override
 public void evaluateTariffs() {
   for (CapacityBundle bundle : capacityBundles) {
     TariffEvaluator evaluator = evaluatorMap.get(bundle);
     if (bundle.getSubscriberStructure().getInertiaDistribution() != null) {
       evaluator.withInertia(
           bundle.getSubscriberStructure().getInertiaDistribution().drawSample());
     } else {
       log.warn("no inertia distro, using default value 0.7");
       evaluator.withInertia(0.7);
     }
     evaluator.evaluateTariffs();
   }
 }
 @Override
 public void initialize(CustomerStructure structure) {
   log.info("Initializing customer " + customerStructure.name);
   NodeList capacityBundleNodes =
       customerStructure.getConfigXml().getElementsByTagName("capacityBundle");
   for (int i = 0; i < capacityBundleNodes.getLength(); ++i) {
     Element capacityBundleElement = (Element) capacityBundleNodes.item(i);
     CapacityBundle capacityBundle = createCapacityBundle(structure, capacityBundleElement);
     capacityBundle.initialize(structure, capacityBundleElement);
     capacityBundles.add(capacityBundle);
     customerRepo.add(capacityBundle.getCustomerInfo());
   }
   utilityOptimizer = createUtilityOptimizer(structure, capacityBundles);
   utilityOptimizer.initialize();
   log.info("Successfully initialized customer " + customerStructure.name);
 }
 private double useCapacity(TariffSubscription subscription, CapacityBundle bundle) {
   double capacity = 0;
   for (CapacityOriginator capacityOriginator : bundle.getCapacityOriginators()) {
     capacity += capacityOriginator.useCapacity(subscription);
   }
   return capacity;
 }
 /**
  * Is it correct to sum inconveniences over originators? currently every shifting customer has 1
  * originator so this doesn't matter, but it might change in the future.
  */
 @Override
 public double getShiftingInconvenienceFactor(Tariff tariff) {
   double inconv = 0;
   for (CapacityOriginator capacityOriginator : bundle.getCapacityOriginators()) {
     inconv += capacityOriginator.getShiftingInconvenienceFactor(tariff);
   }
   return inconv;
 }
 /** @Override hook */
 protected void subscribeDefault() {
   for (CapacityBundle bundle : capacityBundles) {
     PowerType powerType = bundle.getPowerType();
     if (getTariffMarket().getDefaultTariff(powerType) != null) {
       log.info(
           bundle.getName()
               + ": Subscribing "
               + bundle.getPopulation()
               + " customers to default "
               + powerType
               + " tariff");
       subscribe(
           getTariffMarket().getDefaultTariff(powerType), bundle, bundle.getPopulation(), false);
     } else {
       log.info(
           bundle.getName()
               + ": No default tariff for power type "
               + powerType
               + "; trying generic type");
       PowerType genericType = powerType.getGenericType();
       if (getTariffMarket().getDefaultTariff(genericType) == null) {
         log.error(
             bundle.getName()
                 + ": No default tariff for generic power type "
                 + genericType
                 + " either!");
       } else {
         log.info(
             bundle.getName()
                 + ": Subscribing "
                 + bundle.getPopulation()
                 + " customers to default "
                 + genericType
                 + " tariff");
         subscribe(
             getTariffMarket().getDefaultTariff(genericType),
             bundle,
             bundle.getPopulation(),
             false);
       }
     }
   }
 }
 /**
  * HACK, accessed from inner class, overriden from derived class
  *
  * <p>sum all originators' usage arrays
  *
  * @param originator2usage
  * @param dummySubscription
  * @param bundle
  * @return
  */
 public double[] adjustForecastPerTariff(
     HashMap<CapacityOriginator, double[]> originator2usage,
     TariffSubscription dummySubscription,
     CapacityBundle bundle) {
   // sum all originators' usage arrays
   double[] result = new double[originator2usage.values().iterator().next().length];
   for (double[] usage : originator2usage.values()) {
     for (int i = 0; i < result.length; ++i) {
       result[i] += usage[i] / bundle.getPopulation();
     }
   }
   return result;
 }
 private void usePower(Timeslot timeslot) {
   for (CapacityBundle bundle : capacityBundles) {
     List<TariffSubscription> subscriptions =
         getTariffSubscriptionRepo().findActiveSubscriptionsForCustomer(bundle.getCustomerInfo());
     double totalCapacity = 0.0;
     double totalUsageCharge = 0.0;
     for (TariffSubscription subscription : subscriptions) {
       double usageSign = bundle.getPowerType().isConsumption() ? +1 : -1;
       double currCapacity = usageSign * useCapacity(subscription, bundle);
       if (Config.getInstance().isUsageChargesLogging()) {
         double charge =
             subscription
                 .getTariff()
                 .getUsageCharge(currCapacity, subscription.getTotalUsage(), false);
         totalUsageCharge += charge;
       }
       subscription.usePower(currCapacity);
       totalCapacity += currCapacity;
     }
     log.info(
         bundle.getName()
             + ": Total "
             + bundle.getPowerType()
             + " capacity for timeslot "
             + timeslot.getSerialNumber()
             + " = "
             + totalCapacity);
     logUsageCharges(
         bundle.getName()
             + ": Total "
             + bundle.getPowerType()
             + " usage charge for timeslot "
             + timeslot.getSerialNumber()
             + " = "
             + totalUsageCharge);
   }
 }
 @Override
 public CustomerInfo getCustomerInfo() {
   return bundle.getCustomerInfo();
 }
 TariffEvaluationWrapper(CapacityBundle bundle) {
   this.bundle = bundle;
   subStructure = bundle.getSubscriberStructure();
 }