mschaefer@9295: /** Copyright (C) 2017 by Bundesanstalt für Gewässerkunde mschaefer@9295: * Software engineering by mschaefer@9295: * Björnsen Beratende Ingenieure GmbH mschaefer@9295: * Dr. Schumacher Ingenieurbüro für Wasser und Umwelt mschaefer@9295: * mschaefer@9295: * This file is Free Software under the GNU AGPL (>=v3) mschaefer@9295: * and comes with ABSOLUTELY NO WARRANTY! Check out the mschaefer@9295: * documentation coming with Dive4Elements River for details. mschaefer@9295: */ mschaefer@9295: package org.dive4elements.river.artifacts.uinfo.salix; mschaefer@9295: mschaefer@9295: import java.util.ArrayList; mschaefer@9295: import java.util.HashMap; mschaefer@9295: import java.util.List; mschaefer@9295: import java.util.Map; mschaefer@9316: import java.util.Map.Entry; mschaefer@9309: import java.util.NavigableMap; mschaefer@9295: mschaefer@9295: import org.dive4elements.river.artifacts.WINFOArtifact; mschaefer@9295: import org.dive4elements.river.artifacts.access.ComputationRangeAccess; mschaefer@9295: import org.dive4elements.river.artifacts.common.GeneralResultType; mschaefer@9295: import org.dive4elements.river.artifacts.common.ResultRow; mschaefer@9295: import org.dive4elements.river.artifacts.model.Calculation; mschaefer@9295: import org.dive4elements.river.artifacts.model.WstValueTable; mschaefer@9295: import org.dive4elements.river.artifacts.model.WstValueTable.QPosition; mschaefer@9295: import org.dive4elements.river.artifacts.model.WstValueTableFactory; mschaefer@9295: import org.dive4elements.river.artifacts.sinfo.common.GaugeDischargeValuesFinder; mschaefer@9295: import org.dive4elements.river.artifacts.sinfo.common.RiverInfoProvider; mschaefer@9295: import org.dive4elements.river.artifacts.sinfo.common.SInfoResultType; mschaefer@9295: import org.dive4elements.river.artifacts.sinfo.tkhstate.WinfoArtifactWrapper; mschaefer@9295: import org.dive4elements.river.artifacts.uinfo.UINFOArtifact; mschaefer@9295: import org.dive4elements.river.artifacts.uinfo.commons.UInfoResultType; mschaefer@9309: import org.dive4elements.river.artifacts.uinfo.salix.SalixLineAccess.ScenarioType; mschaefer@9295: import org.dive4elements.river.model.Gauge; mschaefer@9295: import org.dive4elements.river.model.MainValue; mschaefer@9295: import org.dive4elements.river.model.MainValueType.MainValueTypeKey; mschaefer@9295: mschaefer@9295: /** mschaefer@9295: * Calculation of the result rows of the u-info salix line calc mode mschaefer@9295: * mschaefer@9295: * @author Matthias Schäfer mschaefer@9295: */ gernotbelger@9321: final class SalixLineCalculator { mschaefer@9295: mschaefer@9295: private final List rows = new ArrayList<>(); mschaefer@9295: mschaefer@9295: private final RiverInfoProvider riverInfoProvider; mschaefer@9295: mschaefer@9295: private final Map gaugeMwPos; mschaefer@9295: private final Map gaugeMnwPos; mschaefer@9295: private final Map gaugeMhwPos; mschaefer@9295: mschaefer@9295: private Calculation problems; mschaefer@9295: mschaefer@9295: private WstValueTable wst; mschaefer@9295: gernotbelger@9321: public SalixLineCalculator(final RiverInfoProvider riverInfoProvider) { mschaefer@9295: this.riverInfoProvider = riverInfoProvider; mschaefer@9295: this.gaugeMwPos = new HashMap<>(); mschaefer@9295: this.gaugeMnwPos = new HashMap<>(); mschaefer@9295: this.gaugeMhwPos = new HashMap<>(); mschaefer@9295: } mschaefer@9295: mschaefer@9295: /** mschaefer@9295: * Calculate the salix line result rows mschaefer@9295: */ mschaefer@9309: public void execute(final Calculation problems, final UINFOArtifact uinfo, final NavigableMap> rangeScenarios, mschaefer@9316: final ScenarioType scenarioType, final String[] scenarioLabels, final SalixLineCalculationResults results) { mschaefer@9295: mschaefer@9295: this.problems = problems; mschaefer@9295: this.wst = WstValueTableFactory.getTable(this.riverInfoProvider.getRiver()); mschaefer@9295: mschaefer@9295: fetchGaugeMainValuePositions(); mschaefer@9295: mschaefer@9295: final WINFOArtifact winfo = new WinfoArtifactWrapper(uinfo); mschaefer@9295: winfo.addStringData("ld_mode", "distance"); mschaefer@9295: winfo.addStringData("ld_step", "100"); mschaefer@9295: for (final double station : new ComputationRangeAccess(winfo).getKms()) { gernotbelger@9321: this.rows.add(createRow(station, rangeScenarios)); mschaefer@9295: } mschaefer@9309: if (scenarioType == ScenarioType.REGIONAL) mschaefer@9316: results.addResult(new SalixLineCalculationRegionalResult("Salix", scenarioLabels, this.rows), problems); mschaefer@9309: else if (scenarioType == ScenarioType.SUPRAREGIONAL) mschaefer@9316: results.addResult(new SalixLineCalculationSupraRegionalResult("Salix", this.rows), problems); mschaefer@9309: else if (scenarioType == ScenarioType.HISTORICAL) mschaefer@9316: results.addResult(new SalixLineCalculationHistoricalResult("Salix", this.rows), problems); mschaefer@9309: else mschaefer@9316: results.addResult(new SalixLineCalculationNoScenarioResult("Salix", this.rows), problems); mschaefer@9295: } mschaefer@9295: mschaefer@9295: /** mschaefer@9295: * Fetch MW, MNW and MHW of all gauges and determine the wst QPosition for each one mschaefer@9295: */ mschaefer@9295: private void fetchGaugeMainValuePositions() { mschaefer@9295: this.gaugeMwPos.clear(); mschaefer@9295: this.gaugeMnwPos.clear(); mschaefer@9295: this.gaugeMhwPos.clear(); mschaefer@9295: for (final Gauge gauge : this.riverInfoProvider.getGauges()) { mschaefer@9295: this.gaugeMwPos.put(gauge, null); mschaefer@9295: this.gaugeMnwPos.put(gauge, null); mschaefer@9295: this.gaugeMhwPos.put(gauge, null); mschaefer@9295: final GaugeDischargeValuesFinder finder = GaugeDischargeValuesFinder.loadValues(gauge, this.problems); mschaefer@9295: if (finder == null) mschaefer@9295: continue; mschaefer@9295: final double gaugeKm = gauge.getStation().doubleValue(); mschaefer@9295: for (final MainValue mv : MainValue.getValuesOfGaugeAndType(gauge, MainValueTypeKey.W)) { mschaefer@9295: if (mv.getMainValue().getName().equalsIgnoreCase("mw")) mschaefer@9295: this.gaugeMwPos.put(gauge, this.wst.getQPosition(gaugeKm, finder.getDischarge(mv.getValue().doubleValue()))); mschaefer@9295: else if (mv.getMainValue().getName().equalsIgnoreCase("mnw")) mschaefer@9295: this.gaugeMnwPos.put(gauge, this.wst.getQPosition(gaugeKm, finder.getDischarge(mv.getValue().doubleValue()))); mschaefer@9295: else if (mv.getMainValue().getName().equalsIgnoreCase("mhw")) mschaefer@9295: this.gaugeMhwPos.put(gauge, this.wst.getQPosition(gaugeKm, finder.getDischarge(mv.getValue().doubleValue()))); mschaefer@9295: } mschaefer@9295: } mschaefer@9295: } mschaefer@9295: mschaefer@9295: /** mschaefer@9295: * Create a result row for a station and its gauge, and add w-q-values as selected gernotbelger@9321: * gernotbelger@9321: * @param rangeScenarios2 mschaefer@9295: */ gernotbelger@9321: private ResultRow createRow(final double station, final NavigableMap> rangeScenarios) { mschaefer@9295: mschaefer@9295: final ResultRow row = ResultRow.create(); mschaefer@9316: // Find station's gauge mschaefer@9295: final Gauge gauge = this.riverInfoProvider.getGauge(station, true); mschaefer@9295: row.putValue(GeneralResultType.station, station); mschaefer@9316: // Interpolate mnw, mw, and mhw mschaefer@9295: final double mnw = interpolateW(station, this.gaugeMnwPos.get(gauge)); mschaefer@9295: final double mw = interpolateW(station, this.gaugeMwPos.get(gauge)); mschaefer@9295: final double mhw = interpolateW(station, this.gaugeMhwPos.get(gauge)); mschaefer@9295: row.putValue(SInfoResultType.waterlevel, mnw); mschaefer@9295: row.putValue(SInfoResultType.waterlevel1, mw); mschaefer@9295: row.putValue(SInfoResultType.waterlevel2, mhw); mschaefer@9316: // Calc salix-line and mw-mnw mschaefer@9295: row.putValue(UInfoResultType.salixline, calcSalix(mhw, mw)); mschaefer@9295: row.putValue(UInfoResultType.salix_delta_mw, calcMwmnw(mw, mnw)); mschaefer@9316: // Calc scenario values (always all scenario types set, Result variant extracts the fields needed) mschaefer@9316: final List scenarios = new ArrayList<>(); gernotbelger@9321: final double[] deltaws = getDeltaWs(station, rangeScenarios); mschaefer@9316: for (int i = 0; i <= deltaws.length - 1; i++) { mschaefer@9316: if (Math.abs(deltaws[i]) < 0.0001) { mschaefer@9316: row.putValue(UInfoResultType.salix_line_scenario, Double.NaN); mschaefer@9316: row.putValue(UInfoResultType.salix_line_scenario_dwspl, 0); // TODO NaN when changed from int to double gernotbelger@9321: /* always need to add a member, so the exporter will produce empty columns */ gernotbelger@9321: scenarios.add(null); gernotbelger@9321: } else { mschaefer@9316: final double salix = calcSalix(mhw, mw + deltaws[i]); mschaefer@9316: row.putValue(UInfoResultType.salix_line_scenario, salix); mschaefer@9316: row.putValue(UInfoResultType.salix_line_scenario_dwspl, (int) (deltaws[i] * 100)); mschaefer@9316: scenarios.add(new SalixScenario((int) (deltaws[i] * 100), salix)); mschaefer@9316: } mschaefer@9316: } mschaefer@9316: row.putValue(UInfoResultType.customMultiRowColSalixRegionalValue_Dwspl, scenarios); mschaefer@9295: return row; mschaefer@9295: } mschaefer@9295: mschaefer@9295: /** mschaefer@9295: * Interpolates the W for a station with a fixed (virtual) wst column position mschaefer@9295: */ mschaefer@9295: private double interpolateW(final double station, final QPosition qPosition) { mschaefer@9295: if (qPosition != null) mschaefer@9295: return this.wst.interpolateW(station, qPosition, this.problems); gernotbelger@9321: gernotbelger@9321: return Double.NaN; mschaefer@9295: } mschaefer@9295: mschaefer@9295: /** mschaefer@9295: * Calculates the salix value mschaefer@9295: */ mschaefer@9295: private double calcSalix(final double mhw, final double mw) { mschaefer@9295: return mhw - 2.31 - mw; mschaefer@9295: } mschaefer@9295: mschaefer@9295: /** mschaefer@9295: * Calculates the inverse MW-MNW difference mschaefer@9295: */ mschaefer@9295: private double calcMwmnw(final double mw, final double mnw) { mschaefer@9295: return mnw - mw; mschaefer@9295: } mschaefer@9316: mschaefer@9316: /** mschaefer@9316: * Gets the station-specific list of delta-ws of the active scenario, at least with one 0 item in any case gernotbelger@9321: * gernotbelger@9321: * @param rangeScenarios mschaefer@9316: */ gernotbelger@9321: private double[] getDeltaWs(final double station, final NavigableMap> rangeScenarios) { gernotbelger@9321: final Entry> stationScenarios = rangeScenarios.floorEntry(station); mschaefer@9316: if (stationScenarios == null) mschaefer@9316: return new double[] { 0.0 }; gernotbelger@9321: gernotbelger@9321: final double[] deltaws = new double[stationScenarios.getValue().size()]; gernotbelger@9321: for (int i = 0; i <= stationScenarios.getValue().size() - 1; i++) gernotbelger@9321: deltaws[i] = stationScenarios.getValue().get(i); gernotbelger@9321: return deltaws; mschaefer@9316: } gernotbelger@9321: }