// 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; }