/**
   * Initialization must provide accessor to Customer instance and time. We assume configuration has
   * already happened. We also start with no active trucks, and the weakest batteries on
   * availableChargers. Trucks will not be active (in bootstrap mode) until the first shift change.
   */
  @Override
  public void initialize() {
    super.initialize();
    log.info("Initialize " + name);
    // fill out CustomerInfo. We label this model as thermal storage
    // because we don't allow battery discharge
    powerType = PowerType.THERMAL_STORAGE_CONSUMPTION;
    CustomerInfo info = new CustomerInfo(name, 1);
    // conservative interruptible capacity
    double interruptible = Math.min(nChargers * maxChargeKW, nBatteries * maxChargeKW / 3.0);
    info.withPowerType(powerType)
        .withCustomerClass(CustomerClass.LARGE)
        .withControllableKW(-interruptible)
        .withStorageCapacity(nBatteries * maxChargeKW / 3.0)
        .withUpRegulationKW(-nChargers * maxChargeKW)
        .withDownRegulationKW(nChargers * maxChargeKW); // optimistic, perhaps
    addCustomerInfo(info);
    ensureSeeds();

    // use default values when not configured
    ensureShifts();

    // make sure we have enough batteries and availableChargers
    validateBatteries();
    validateChargers();

    // all batteries are charging
    // energyCharging = getStateOfCharge() * getBatteryCapacity();
    // capacityInUse = 0.0;
    // energyInUse = 0.0;

    // set up the tariff evaluator. We are wide-open to variable pricing.
    tariffEvaluator = new TariffEvaluator(this);
    tariffEvaluator.withInertia(0.7).withPreferredContractDuration(14);
    tariffEvaluator.initializeInconvenienceFactors(0.0, 0.01, 0.0, 0.0);
    tariffEvaluator.initializeRegulationFactors(
        -nChargers * maxChargeKW * 0.05, 0.0, nChargers * maxChargeKW * 0.04);
  }