// formulate and generate the solution, if necessary private void solve() { if (solved) return; // min obj.x s.t. a.x=b, lb <= x <= ub // x is energy use per block for size hours, b is slack var per block. // Block is a shift, or portion of shift with constant price. // For multi-hour blocks, energy use is evenly distributed across hours // after solution. Date start = new Date(); int shifts = needs.length; // Create blocks that break on both shift boundaries and tariff price // boundaries. ShiftBlock[] blocks = makeBlocks(shifts); int columns = blocks.length; int blockIndex = -1; double[] obj = new double[columns + shifts]; double[][] a = new double[shifts][columns + shifts]; double[] b = new double[shifts]; double[] lb = new double[columns + shifts]; double[] ub = new double[columns + shifts]; int column = 0; double cumulativeMin = 0.0; // this is the primary constraint // construct the problem for (int i = 0; i < shifts; i++) { // one iteration per shift // double kwh = // needs[i].getEnergyNeeded() + needs[i].getMaxSurplus(); // for (int j = 0; j < needs[i].getDuration(); j++) { while ((blockIndex < blocks.length - 1) && (blocks[blockIndex + 1].getShiftEnergy() == needs[i])) { blockIndex += 1; // one iteration per block within a shift // fill in objective function obj[column] = blocks[blockIndex].getCost(); lb[column] = 0.0; ub[column] = (needs[i].getEnergyNeeded() + needs[i].getMaxSurplus()) * (double) blocks[blockIndex].getDuration() / needs[i].getDuration(); column += 1; // construct cumulative usage constraints // a[i][column] = -1.0; // time = time.plus(TimeService.HOUR); } // fill a row up to column for (int j = 0; j < column; j++) { a[i][j] = -1.0; } // b vector - one entry per constraint double need = needs[i].getEnergyNeeded(); if (needs[i].getMaxSurplus() < 0.0) need += needs[i].getMaxSurplus(); cumulativeMin += need; b[i] = -cumulativeMin; // fill in slack values, one per constraint obj[columns + i] = 0.0; a[i][columns + i] = 1.0; lb[columns + i] = 0.0; // upper bound is max possible energy for shift ub[columns + i] = (needs[i].getEnergyNeeded() + needs[i].getMaxSurplus()); } // run the optimization LPOptimizationRequest or = new LPOptimizationRequest(); log.debug("Obj: " + Arrays.toString(obj)); or.setC(obj); log.debug("a:"); for (int i = 0; i < a.length; i++) log.debug(Arrays.toString(a[i])); or.setA(a); log.debug("b: " + Arrays.toString(b)); or.setB(b); or.setLb(lb); log.debug("ub: " + Arrays.toString(ub)); or.setUb(ub); or.setTolerance(1.0e-2); LPPrimalDualMethod opt = new LPPrimalDualMethod(); opt.setLPOptimizationRequest(or); try { int returnCode = opt.optimize(); if (returnCode != OptimizationResponse.SUCCESS) { log.error(getName() + "bad optimization return code " + returnCode); } double[] sol = opt.getOptimizationResponse().getSolution(); Date end = new Date(); log.info("Solution time: " + (end.getTime() - start.getTime())); log.debug("Solution = " + Arrays.toString(sol)); recordSolution(sol, blocks); } catch (Exception e) { log.error(e.toString()); } // we call it solved whether or not the solution was successful solved = true; }