ingo@2215: package de.intevation.flys.artifacts.model;
ingo@2215: 
ingo@2228: import java.util.ArrayList;
ingo@4241: import java.util.Arrays;
ingo@2228: import java.util.Date;
ingo@2228: import java.util.List;
ingo@2228: 
ingo@2215: import org.apache.log4j.Logger;
ingo@2215: 
ingo@2228: import de.intevation.flys.model.DischargeTable;
ingo@2219: import de.intevation.flys.model.Gauge;
ingo@2228: import de.intevation.flys.model.TimeInterval;
ingo@2219: 
ingo@2215: 
ingo@2215: /**
ingo@2215:  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
ingo@2215:  */
ingo@2215: public class Calculation6 extends Calculation {
ingo@2215: 
ingo@2215:     private static final Logger logger = Logger.getLogger(Calculation6.class);
ingo@2215: 
ingo@4232:     private int mode;
ingo@4232:     private long[] timerange;
ingo@2219:     private double[] values;
ingo@2215: 
ingo@2219:     public static final int MODE_W = 0;
ingo@2219:     public static final int MODE_Q = 1;
ingo@2219: 
ingo@4136:     public static final double SCALE = 1d;
ingo@2230: 
ingo@2228:     public Calculation6(int mode, long[] timerange, double[] values) {
ingo@4232:         this.mode = mode;
ingo@2219:         this.timerange = timerange;
ingo@4232:         this.values = values;
ingo@2215:     }
ingo@2215: 
ingo@2219:     public CalculationResult calculate(Gauge gauge) {
ingo@2219:         if (!checkParameters() || gauge == null) {
ingo@2219:             logger.warn("Parameters not valid for calculation.");
ingo@2219: 
ingo@2219:             return null;
ingo@2219:         }
ingo@2219: 
ingo@2219:         if (logger.isDebugEnabled()) {
ingo@2219:             debug();
ingo@2219:         }
ingo@2219: 
ingo@4232:         DischargeTable refTable = fetchReferenceTable(gauge);
ingo@2228:         List<DischargeTable> dts = fetchDischargeTables(gauge);
ingo@2219: 
ingo@2304:         int numTables = dts.size();
ingo@2304: 
ingo@2304:         logger.debug("Take " + numTables + " into account.");
ingo@2304: 
ingo@2304:         if (numTables == 0) {
ingo@2304:             addProblem("cannot.find.hist.q.tables");
ingo@2304:         }
ingo@2228: 
ingo@4241:         WQTimerange[] wqt = prepareTimerangeData(refTable, dts);
ingo@4241:         WQKms[] wqs = prepareWQData(dts);
ingo@2228: 
ingo@2250:         logger.debug("Number of calculation results: " + wqt.length);
ingo@2250: 
ingo@4241:         return new CalculationResult(new HistoricalDischargeData(wqt, wqs),
ingo@4241:             this);
ingo@2215:     }
ingo@2219: 
ingo@2219:     protected boolean checkParameters() {
ingo@2219:         if (!(mode == MODE_W || mode == MODE_Q)) {
ingo@2219:             logger.warn("Invalid mode '" + mode + "' for calculation.");
ingo@2219:             return false;
ingo@2219:         }
ingo@2219: 
ingo@2219:         if (timerange == null || timerange.length < 2) {
ingo@2219:             logger.warn("Invalid timerange for calculation.");
ingo@2219:             return false;
ingo@2219:         }
ingo@2219: 
ingo@2219:         if (values == null || values.length == 0) {
ingo@2219:             logger.warn("No values for W or Q specified.");
ingo@2219:             return false;
ingo@2219:         }
ingo@2219: 
ingo@2219:         return true;
ingo@2219:     }
ingo@2219: 
ingo@2311:     protected DischargeTable fetchReferenceTable(Gauge gauge) {
ingo@2311:         return gauge.fetchMasterDischargeTable();
ingo@2311:     }
ingo@2311: 
ingo@2228:     protected List<DischargeTable> fetchDischargeTables(Gauge gauge) {
ingo@2228:         List<DischargeTable> relevant = new ArrayList<DischargeTable>();
ingo@4232:         List<DischargeTable> all = gauge.getDischargeTables();
ingo@2228: 
ingo@4232:         for (DischargeTable dt : all) {
ingo@2228:             if (isDischargeTableRelevant(dt)) {
ingo@2228:                 relevant.add(dt);
ingo@2228:             }
ingo@2228:         }
ingo@2228: 
ingo@2228:         return relevant;
ingo@2228:     }
ingo@2228: 
ingo@2228:     protected boolean isDischargeTableRelevant(DischargeTable dt) {
ingo@2228:         TimeInterval ti = dt.getTimeInterval();
ingo@2228: 
ingo@2598:         if (dt.getKind() == Gauge.MASTER_DISCHARGE_TABLE || ti == null) {
ingo@2315:             return false;
ingo@2315:         }
ingo@2315: 
ingo@4232:         Date start = ti.getStartTime();
ingo@2228:         long startTime = start.getTime();
ingo@2228: 
ingo@2228:         if (startTime >= timerange[0] && startTime <= timerange[1]) {
ingo@2228:             return true;
ingo@2228:         }
ingo@2228: 
ingo@4232:         Date stop = ti.getStopTime();
ingo@2228:         long stopTime = stop != null ? stop.getTime() : -1l;
ingo@2228: 
ingo@2228:         if (stopTime >= timerange[0] && stopTime <= timerange[1]) {
ingo@2228:             return true;
ingo@2228:         }
ingo@2228: 
ingo@2228:         logger.debug("DischargeTable not in range: " + start + " -> " + stop);
ingo@2228: 
ingo@2228:         return false;
ingo@2228:     }
ingo@2228: 
ingo@4241:     protected WQTimerange[] prepareTimerangeData(DischargeTable refTable,
ingo@4232:         List<DischargeTable> dts) {
ingo@2311:         if (refTable == null) {
ingo@2311:             addProblem("cannot.find.hist.q.reftable");
ingo@2311:             return prepareSimpleData(dts);
ingo@2311:         }
ingo@2311:         else {
ingo@2311:             return prepareData(refTable, dts);
ingo@2311:         }
ingo@2311:     }
ingo@2311: 
ingo@4241:     protected WQKms[] prepareWQData(List<DischargeTable> dts) {
ingo@4241:         WQKms[] wqs = new WQKms[dts.size()];
ingo@4241: 
ingo@4241:         int idx = 0;
ingo@4241: 
ingo@4241:         for (DischargeTable dt : dts) {
ingo@4241:             double[][] values = null;
ingo@4241: 
ingo@4241:             if (dt.getKind() == DischargeTables.MASTER) {
ingo@4241:                 values = DischargeTables.loadDischargeTableValues(dt,
ingo@4241:                     DischargeTables.MASTER_SCALE);
ingo@4241: 
ingo@4241:             }
ingo@4241:             else {
ingo@4241:                 values = DischargeTables.loadDischargeTableValues(dt,
ingo@4241:                     DischargeTables.HISTORICAL_SCALE);
ingo@4241: 
ingo@4241:             }
ingo@4241: 
ingo@4241:             wqs[idx++] = prepareWQ(dt, values);
ingo@4241:         }
ingo@4241: 
ingo@4241:         return wqs;
ingo@4241:     }
ingo@4241: 
ingo@4241:     protected WQKms prepareWQ(DischargeTable dt, double[][] values) {
ingo@4241:         double km = dt.getGauge().getStation().doubleValue();
ingo@4241: 
ingo@4241:         double[] kms = new double[values[0].length];
ingo@4241:         Arrays.fill(kms, km);
ingo@4241: 
ingo@4241:         return new HistoricalWQKms(kms, values[1], values[0],
ingo@4241:             String.valueOf(km), dt.getTimeInterval());
ingo@4241:     }
ingo@4241: 
ingo@2311:     protected WQTimerange[] prepareSimpleData(List<DischargeTable> dts) {
ingo@4232:         List<WQTimerange> wqts = new ArrayList<WQTimerange>(values.length);
ingo@2228: 
ingo@4232:         for (double value : values) {
ingo@2228:             logger.debug("Prepare data for value: " + value);
ingo@2228: 
ingo@4232:             String name = mode == MODE_W ? "W=" + value : "Q=" + value;
ingo@4232:             WQTimerange wqt = null;
ingo@2228: 
ingo@4232:             for (DischargeTable dt : dts) {
ingo@4232:                 Date[] ti = prepareTimeInterval(dt);
ingo@2315:                 Timerange t = new Timerange(ti[0], ti[1]);
ingo@4232:                 double w;
ingo@4232:                 double q;
ingo@2228: 
ingo@2228:                 if (mode == MODE_W) {
ingo@2228:                     w = value;
ingo@4138:                     q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE);
ingo@2244: 
ingo@2244:                     if (Double.isNaN(q)) {
ingo@2244:                         logger.warn("Cannot find Q for W: " + w);
ingo@2315:                         addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]);
ingo@2244:                         continue;
ingo@2244:                     }
ingo@2228:                 }
ingo@2228:                 else {
ingo@2228:                     q = value;
ingo@4232:                     w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE);
ingo@2228:                 }
ingo@2228: 
ingo@2315:                 logger.debug("Q=" + q + " | W=" + w);
ingo@2230: 
ingo@2244:                 if (wqt == null) {
ingo@2244:                     wqt = new WQTimerange(name);
ingo@2244:                 }
ingo@2244: 
ingo@2228:                 wqt.add(w, q, t);
ingo@2228:             }
ingo@2228: 
ingo@2244:             if (wqt != null) {
ingo@2244:                 wqts.add(wqt);
ingo@2244:             }
ingo@2228:         }
ingo@2228: 
sascha@3452:         return wqts.toArray(new WQTimerange[wqts.size()]);
ingo@2228:     }
ingo@2228: 
ingo@4232:     protected HistoricalWQTimerange[] prepareData(DischargeTable refTable,
ingo@4232:         List<DischargeTable> dts) {
ingo@4232:         List<HistoricalWQTimerange> wqts = new ArrayList<HistoricalWQTimerange>(
ingo@4232:             values.length);
ingo@2228: 
ingo@4232:         for (double value : values) {
ingo@2311:             logger.debug("Prepare data plus diff for value: " + value);
ingo@2311: 
ingo@2311:             String name = mode == MODE_W ? "W=" + value : "Q=" + value;
ingo@4232:             HistoricalWQTimerange wqt = null;
ingo@2311: 
ingo@2311:             double ref;
ingo@2315:             double diff;
ingo@2311: 
ingo@2311:             if (refTable != null && mode == MODE_W) {
ingo@4232:                 ref = findValueForW(refTable, value,
ingo@4232:                     DischargeTables.MASTER_SCALE);
ingo@2311:             }
ingo@2311:             else if (refTable != null) {
ingo@4232:                 ref = findValueForQ(refTable, value,
ingo@4232:                     DischargeTables.MASTER_SCALE);
ingo@2311:             }
ingo@2311:             else {
ingo@2311:                 ref = Double.NaN;
ingo@2311:             }
ingo@2311: 
ingo@4232:             for (DischargeTable dt : dts) {
ingo@2315:                 Date[] ti = prepareTimeInterval(dt);
ingo@2311: 
ingo@4232:                 Timerange t = new Timerange(ti[0], ti[1]);
ingo@4232:                 double w;
ingo@4232:                 double q;
ingo@2311: 
ingo@2311:                 if (mode == MODE_W) {
ingo@4232:                     w = value;
ingo@4232:                     q = findValueForW(dt, w, DischargeTables.HISTORICAL_SCALE);
ingo@2311: 
ingo@2311:                     if (Double.isNaN(q)) {
ingo@2311:                         logger.warn("Cannot find Q for W: " + w);
ingo@2315:                         addProblem("cannot.find.hist.q.for.w", w, ti[0], ti[1]);
ingo@2311:                         continue;
ingo@2311:                     }
ingo@2315: 
ingo@4232:                     diff = ref - q;
ingo@2311:                 }
ingo@2311:                 else {
ingo@4232:                     q = value;
ingo@4232:                     w = findValueForQ(dt, q, DischargeTables.HISTORICAL_SCALE);
ingo@4232: 
ingo@4232:                     if (Double.isNaN(w)) {
ingo@4232:                         logger.warn("Cannot find W for Q: " + q);
ingo@4232:                         addProblem("cannot.find.hist.w.for.q", q, ti[0], ti[1]);
ingo@4232:                         continue;
ingo@4232:                     }
ingo@4232:                     diff = ref - w;
ingo@2311:                 }
ingo@2311: 
ingo@2315:                 logger.debug("Q=" + q + " | W=" + w + " | Ref = " + ref);
ingo@2311: 
ingo@2311:                 if (wqt == null) {
ingo@2311:                     wqt = new HistoricalWQTimerange(name);
ingo@2311:                 }
ingo@2311: 
ingo@2315:                 wqt.add(w, q, diff, t);
ingo@2311:             }
ingo@2311: 
ingo@2311:             if (wqt != null) {
ingo@2311:                 wqts.add(wqt);
ingo@2311:             }
ingo@2311:         }
ingo@2311: 
ingo@4232:         return (HistoricalWQTimerange[]) wqts
ingo@4232:             .toArray(new HistoricalWQTimerange[wqts.size()]);
ingo@2311:     }
ingo@2311: 
ingo@2315:     protected Date[] prepareTimeInterval(DischargeTable dt) {
ingo@2315:         TimeInterval ti = dt.getTimeInterval();
ingo@2315: 
ingo@2315:         Date start = ti.getStartTime();
ingo@4232:         Date end = ti.getStopTime();
ingo@2315: 
ingo@2315:         if (end == null) {
ingo@2315:             logger.warn("TimeInterval has no stop time set!");
ingo@2315: 
ingo@2315:             end = new Date();
ingo@2315:         }
ingo@2315: 
ingo@2315:         return new Date[] { start, end };
ingo@2315:     }
ingo@2315: 
ingo@4138:     protected double findValueForW(DischargeTable dt, double w, double scale) {
ingo@4138:         double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale);
ingo@4232:         double[] qs = DischargeTables.getQsForW(vs, w);
sascha@2418:         return qs.length == 0 ? Double.NaN : qs[0];
ingo@2228:     }
ingo@2228: 
ingo@4232:     protected double findValueForQ(DischargeTable dt, double q, double scale) {
ingo@4232:         double[][] vs = DischargeTables.loadDischargeTableValues(dt, scale);
ingo@4232:         double[] ws = DischargeTables.getWsForQ(vs, q);
ingo@2228: 
ingo@4232:         return ws.length == 0 ? Double.NaN : ws[0];
ingo@2228:     }
ingo@2228: 
ingo@2219:     /**
ingo@2219:      * Writes the parameters used for this calculation to logger.
ingo@2219:      */
ingo@2219:     public void debug() {
ingo@2219:         StringBuilder sb = new StringBuilder();
ingo@4232:         for (double value : values) {
ingo@2219:             sb.append(String.valueOf(value) + " ");
ingo@2219:         }
ingo@2219: 
ingo@2219:         logger.debug("========== Calculation6 ==========");
ingo@2219:         logger.debug("   Mode:         " + mode);
ingo@2219:         logger.debug("   Timerange:    " + timerange[0] + " - " + timerange[1]);
ingo@2219:         logger.debug("   Input values: " + sb.toString());
ingo@2219:         logger.debug("==================================");
ingo@2219:     }
ingo@2215: }
ingo@2215: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :