diff --git a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java new file mode 100644 index 000000000..61a9c65f5 --- /dev/null +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java @@ -0,0 +1,153 @@ +package org.openda.model_dflowfm; + +import org.openda.exchange.QuantityInfo; +import org.openda.interfaces.IExchangeItem; +import org.openda.interfaces.IGeometryInfo; +import org.openda.interfaces.IQuantityInfo; +import org.openda.interfaces.ITimeInfo; + +public class DFlowFMSpatialRoughnessExchangeItem implements IExchangeItem { + + private final String id; + private final String frictionType; + private final String branchId; + private final String functionType; + private final double[] chainages; + private final int levelIndex; + private final int chainageStartIndex; + private final int chainageEndIndex; + private final double[] levels; + private double[] values; + + public DFlowFMSpatialRoughnessExchangeItem(String id, String branchId, String frictionType, String functionType, double[] values, int chainageStartIndex, int chainageEndIndex, double[] levels, double[] chainages, int levelIndex) { + this.id = id; + this.frictionType = frictionType; + this.branchId = branchId; + this.functionType = functionType; + this.values = values; + this.chainageStartIndex = chainageStartIndex; + this.chainageEndIndex = chainageEndIndex; + this.levels = levels; + this.chainages = chainages; + this.levelIndex = levelIndex; + } + + @Override + public String getId() { + return id; + } + + @Override + public String getDescription() { + return null; + } + + @Override + public void copyValuesFromItem(IExchangeItem sourceItem) { + throw new RuntimeException("Method not implemented"); + } + + @Override + public ITimeInfo getTimeInfo() { + return null; + } + + @Override + public IQuantityInfo getQuantityInfo() { + return new QuantityInfo("Roughness-" + frictionType, ""); + } + + @Override + public IGeometryInfo getGeometryInfo() { + return null; + } + + @Override + public ValueType getValuesType() { + return ValueType.doublesType; + } + + @Override + public Role getRole() { + return null; + } + + @Override + public Object getValues() { + return null; + } + + @Override + public double[] getValuesAsDoubles() { + return values; + } + + @Override + public void axpyOnValues(double alpha, double[] axpyValues) { + if (this.values != null) { + for (int i = 0; i < values.length; i++) { + values[i] += alpha * axpyValues[i]; + } + } + } + + @Override + public void multiplyValues(double[] multiplicationFactors) { + if (this.values != null) { + for (int i = 0; i < values.length; i++) { + values[i] *= multiplicationFactors[i]; + } + } + } + + @Override + public void setValues(Object values) { + + } + + @Override + public void setValuesAsDoubles(double[] values) { + this.values = values; + } + + public double[] getTimes() { + return null; + } + + public void setTimes(double[] times) { + throw new RuntimeException(this.getClass().getName() + "setTimes(): time stamps can not be set"); + } + + public int getChainageStartIndex() { + return chainageStartIndex; + } + + public int getChainageEndIndex() { + return chainageEndIndex; + } + + + public String getFunctionType() { + return functionType; + } + + public String getBranchId() { + return branchId; + } + + public String getFrictionType() { + return frictionType; + } + + public double[] getLevels() { + return levels; + } + + public double[] getChainages() { + return chainages; + } + + public int getLevelIndex() { + return levelIndex; + } +} diff --git a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java new file mode 100644 index 000000000..214c7ca55 --- /dev/null +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -0,0 +1,621 @@ +package org.openda.model_dflowfm; + +import org.openda.interfaces.IDataObject; +import org.openda.interfaces.IExchangeItem; +import org.openda.utils.generalJavaUtils.StringUtilities; + +import java.io.*; +import java.util.*; + +public class DFlowFMSpatialRoughnessFile implements IDataObject { + + private static final String GENERAL = "[General]"; + private static final String GLOBAL = "[Global]"; + private static final String BRANCH = "[Branch]"; + private static final String OBSERVATION_POINT = "[ObservationPoint]"; + + private static final String FILE_VERSION = "fileVersion"; + private static final String FILE_TYPE = "fileType"; + + private static final String FRICTION_ID = "frictionId"; + private static final String FRICTION_TYPE = "frictionType"; + private static final String FRICTION_VALUE = "frictionValue"; + + private static final String BRANCH_ID = "branchId"; + private static final String FUNCTION_TYPE = "functionType"; + private static final String NUM_LEVELS = "numLevels"; + private static final String NUM_LOCATIONS = "numLocations"; + private static final String LEVELS = "levels"; + private static final String CHAINAGE = "chainage"; + private static final String CONSTANT = "Constant"; + private static final String FRICTION_VALUES = "frictionValues"; + + private static final String ID = "id"; + private static final String NAME = "name"; + + public static final String OBSERVATION_FILE = "observationFile"; + public static final String OBSERVATION_SELECTION_FILE = "observationSelectionFile"; + public static final String REVERSED_POSTFIX = " (Reversed)"; + + @SuppressWarnings("WeakerAccess") + LinkedHashMap exchangeItems = new LinkedHashMap<>(); + private String fileVersion; + private String fileType; + private String globalFrictionType; + private double globalFrictionValue; + private String frictionId; + private File spatialRoughnessFile; + private File observationFile; + private File observationSelectionFile; + + enum RoughnessType { + Chezy(1), Manning(4), Nikuradse(5), Strickler(6), WhiteColebrook(7), BosBijkerk(9); + + private final int value; + + RoughnessType(int i) { + value = i; + } + + int getValue() { + return value; + } + + static RoughnessType getInstance(int value) { + switch (value) { + case (1): + return Chezy; + case (4): + return Manning; + case (5): + return Nikuradse; + case (6): + return Strickler; + case (7): + return WhiteColebrook; + case (9): + return BosBijkerk; + default: + throw new RuntimeException("Unknown globalType"); + } + } + } + + @Override + public String[] getExchangeItemIDs() { + return exchangeItems.keySet().toArray(new String[exchangeItems.keySet().size()]); + } + + @Override + public String[] getExchangeItemIDs(IExchangeItem.Role role) { + return getExchangeItemIDs(); + } + + @Override + public IExchangeItem getDataObjectExchangeItem(String exchangeItemID) { + return exchangeItems.get(exchangeItemID); + } + + @Override + public void finish() { + try (FileOutputStream fileOutputStream = new FileOutputStream(spatialRoughnessFile); + OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream); + BufferedWriter lineWriter = new BufferedWriter(outputStreamWriter)) { + StringBuilder builder = new StringBuilder(1000); + appendGeneral(builder); + appendContent(builder); + + List loopExchangeItems = new ArrayList<>(exchangeItems.values()); + DFlowFMSpatialRoughnessExchangeItem previousExchangeItem = loopExchangeItems.get(0); + String currentBranchId = previousExchangeItem.getBranchId(); + double[][] frictionValues = new double[1][1]; + + for (int j = 0; j < loopExchangeItems.size(); j++) { + DFlowFMSpatialRoughnessExchangeItem exchangeItem = loopExchangeItems.get(j); + String branchId = exchangeItem.getBranchId(); + double[] exchangeItemLevels = exchangeItem.getLevels(); + if (!branchId.equals(currentBranchId)) { + if (j != 0) appendText(previousExchangeItem, builder, currentBranchId, frictionValues); + currentBranchId = branchId; + frictionValues = new double[exchangeItemLevels == null ? 1 : exchangeItemLevels.length][exchangeItem.getChainages().length]; + previousExchangeItem = exchangeItem; + } + int levelIndex = exchangeItem.getLevelIndex(); + double[] valuesAsDoubles = exchangeItem.getValuesAsDoubles(); + int chainageStartIndex = exchangeItem.getChainageStartIndex(); + int chainageEndIndex = exchangeItem.getChainageEndIndex(); + if (chainageStartIndex == -1) { + frictionValues[levelIndex][0] = valuesAsDoubles[levelIndex]; + continue; + } + for (int i = chainageStartIndex; i < chainageEndIndex; i++) { + frictionValues[levelIndex][i] = valuesAsDoubles[i - chainageStartIndex]; + } + } + appendText(previousExchangeItem, builder, currentBranchId, frictionValues); + + lineWriter.write(builder.toString()); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + private void appendText(DFlowFMSpatialRoughnessExchangeItem exchangeItem, StringBuilder builder, String branchId, double[][] frictionValues) { + String functionType = exchangeItem.getFunctionType(); + if (functionType == null) { + return; + } + builder.append(BRANCH + '\n'); + builder.append(BRANCH_ID + '=').append(branchId).append('\n'); + builder.append(FRICTION_TYPE + '=').append(exchangeItem.getFrictionType()).append('\n'); + builder.append(FUNCTION_TYPE + '=').append(functionType).append('\n'); + double[] exchangeItemLevels = exchangeItem.getLevels(); + if (!functionType.equalsIgnoreCase(CONSTANT)) { + builder.append(NUM_LEVELS + '=').append(exchangeItemLevels.length).append('\n'); + builder.append(LEVELS + '='); + for (double level : exchangeItemLevels) { + builder.append(" ").append(level); + } + builder.append('\n'); + } + double[] chainages = exchangeItem.getChainages(); + builder.append(NUM_LOCATIONS + '=').append(chainages.length).append('\n'); + builder.append(CHAINAGE + '='); + for (double chainage : chainages) { + builder.append(" ").append(chainage); + } + builder.append('\n'); + builder.append(FRICTION_VALUES + '='); + if (exchangeItemLevels == null) { + for (int i = 0; i < chainages.length; i++) { + builder.append(" ").append(frictionValues[0][i]); + } + builder.append('\n'); + builder.append('\n'); + return; + } + for (int levelIndex = 0; levelIndex < exchangeItemLevels.length; levelIndex++) { + for (int chainageIndex = 0; chainageIndex < chainages.length; chainageIndex++) { + builder.append(" ").append(frictionValues[levelIndex][chainageIndex]); + } + builder.append('\n'); + } + builder.append('\n'); + } + + private void appendContent(StringBuilder builder) { + builder.append(GLOBAL + '\n'); + builder.append(FRICTION_ID + '=').append(frictionId).append('\n'); + builder.append(FRICTION_TYPE + '=').append(globalFrictionType).append('\n'); + builder.append(FRICTION_VALUE + '=').append(globalFrictionValue).append('\n'); + builder.append('\n'); + } + + private void appendGeneral(StringBuilder builder) { + builder.append(GENERAL + '\n'); + builder.append(FILE_VERSION + '=').append(fileVersion).append('\n'); + builder.append(FILE_TYPE + '=').append(fileType).append('\n'); + builder.append('\n'); + } + + @Override + public void initialize(File workingDir, String[] arguments) { + spatialRoughnessFile = new File(workingDir, arguments[0]); + + for (int i = 1; i < arguments.length; i++) { + String argument = arguments[i]; + String[] keyValue = StringUtilities.getKeyValuePair(argument); + String key = keyValue[0]; + String value = keyValue[1]; + switch (key) { + case OBSERVATION_FILE: + observationFile = new File(workingDir, value); + continue; + case OBSERVATION_SELECTION_FILE: + observationSelectionFile = new File(workingDir, value); + continue; + default: + throw new RuntimeException("Unknown key " + key + ". Please specify only targetFile as key=value pair"); + } + } + + Set observationPointIdsSelection = getObservationSelection(); + //noinspection unchecked + Map> observationPoints = observationFile != null ? readObservationFile(observationPointIdsSelection) : Collections.EMPTY_MAP; + + readSpatialDefinitionFile(observationPoints); + } + + private Set getObservationSelection() { + Set observationPointIdsSelection = null; + if (observationSelectionFile == null) return observationPointIdsSelection; + if (!observationSelectionFile.exists()) throw new RuntimeException("Observation file " + observationSelectionFile.getAbsolutePath() + " does not exist"); + observationPointIdsSelection = new HashSet<>(); + try (FileInputStream fileInputStream = new FileInputStream(observationSelectionFile); + InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); + BufferedReader lineReader = new BufferedReader(inputStreamReader)) { + String line = lineReader.readLine(); + while (line != null) { + if (!line.startsWith("#")) { + observationPointIdsSelection.add(line.trim()); + } + line = lineReader.readLine(); + + } + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + return observationPointIdsSelection; + } + + private Map> readObservationFile(Set observationPointIdsSelection) { + if (!observationFile.exists()) throw new RuntimeException("Observation file " + observationFile.getAbsolutePath() + " does not exist"); + Map> observationPointsMap = new LinkedHashMap<>(); + try (FileInputStream fileInputStream = new FileInputStream(observationFile); + InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); + BufferedReader lineReader = new BufferedReader(inputStreamReader)) { + String line = lineReader.readLine(); + if (!line.contains(GENERAL)) throw new RuntimeException("File should start with " + GENERAL); + + line = skipGeneral(lineReader); + + if (!line.contains(OBSERVATION_POINT)) throw new RuntimeException("File should have " + OBSERVATION_POINT + " after " + GENERAL); + + while (line.startsWith(OBSERVATION_POINT)) { + + line = lineReader.readLine(); + String[] keyValue = readKeyValueLine(line); + String name = keyValue[1]; + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + String branchId = keyValue[1]; + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + double chainage = Double.parseDouble(keyValue[1]); + + lineReader.readLine(); + + if (observationPointIdsSelection == null || observationPointIdsSelection.contains(name)) addObservationPoint(observationPointsMap, name, branchId, chainage, name); + + line = lineReader.readLine(); + if (line == null) return observationPointsMap; + while (line.trim().isEmpty()) { + line = lineReader.readLine(); + if (line == null) return observationPointsMap; + } + } + + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + return observationPointsMap; + } + + private void addObservationPoint(Map> observationPointsMap, String id, String branchId, double chainage, String name) { + ObservationPoint observationPoint = new ObservationPoint(id, branchId, chainage, name); + List observationPointList = observationPointsMap.get(branchId); + if (observationPointList == null) { + observationPointList = new ArrayList<>(); + observationPointList.add(observationPoint); + observationPointsMap.put(branchId, observationPointList); + } else { + observationPointList.add(observationPoint); + } + } + + private String skipGeneral(BufferedReader lineReader) throws IOException { + lineReader.readLine(); + lineReader.readLine(); + lineReader.readLine(); + String line = lineReader.readLine(); + while (line.isEmpty()) { + line = lineReader.readLine(); + } + + return line; + } + + private void readSpatialDefinitionFile(Map> observationPoints) { + try (FileInputStream fileInputStream = new FileInputStream(spatialRoughnessFile); + InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); + BufferedReader lineReader = new BufferedReader(inputStreamReader)) { + String line = lineReader.readLine(); + if (!line.contains(GENERAL)) throw new RuntimeException("File should start with " + GENERAL); + readGeneral(lineReader); + + line = lineReader.readLine(); + if (!line.startsWith(GLOBAL)) throw new RuntimeException("File should have " + GLOBAL + " after " + GENERAL); + DFlowFMSpatialRoughnessExchangeItem globalItem = readContent(lineReader); + exchangeItems.put(globalItem.getId(), globalItem); + + line = lineReader.readLine(); + while (line != null) { + if (line.startsWith(BRANCH)) readBranch(lineReader, observationPoints); + line = lineReader.readLine(); + } + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + private void readGeneral(BufferedReader lineReader) throws IOException { + String line = lineReader.readLine(); + + String[] keyValue = readKeyValueLine(line); + fileVersion = keyValue[1]; + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + fileType = keyValue[1]; + + while (!line.isEmpty()) { + line = lineReader.readLine(); + } + } + + private void readBranch(BufferedReader lineReader, Map> observationPointsMap) throws IOException { + String line = lineReader.readLine(); + String[] keyValue = readKeyValueLine(line); + String branchId = keyValue[1].trim(); + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + String frictionType = keyValue[1].trim(); + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + String functionType = keyValue[1].trim(); + + List observationPointsList = observationPointsMap == null ? null : observationPointsMap.get(branchId); + + if (observationPointsList != null) Collections.sort(observationPointsList); + + if (functionType.equalsIgnoreCase("constant")) { + createConstantExchangeItems(lineReader, branchId, frictionType, functionType, observationPointsList); + return; + } + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + int numLevels = Integer.parseInt(keyValue[1]); + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + String[] splitLevels = keyValue[1].trim().split(" "); + assert splitLevels.length == numLevels; + double[] levels = new double[numLevels]; + for (int i = 0; i < numLevels; i++) { + levels[i] = Double.parseDouble(splitLevels[i]); + } + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + int numLocations = Integer.parseInt(keyValue[1]); + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + String[] splitChainage = keyValue[1].trim().split(" "); + assert splitChainage.length == numLocations; + double[] chainages = new double[numLocations]; + for (int i = 0; i < numLocations; i++) { + chainages[i] = Double.parseDouble(splitChainage[i]); + } + + double[][] frictionValues = new double[numLevels][numLocations]; + int index = 0; + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + String value = keyValue[1]; + while (value != null && !value.isEmpty()) { + String[] splitFrictionValues = value.trim().split(" "); + for (int i = 0; i < splitFrictionValues.length; i++) { + String splitFrictionValue = splitFrictionValues[i]; + frictionValues[index][i] = Double.parseDouble(splitFrictionValue); + } + index++; + value = lineReader.readLine(); + } + + + if (observationPointsList == null) { + for (int i = 0; i < numLevels; i++) { + String id = getIdWithLevel(chainages[0], i, branchId, functionType, frictionType); + exchangeItems.put(id, new DFlowFMSpatialRoughnessExchangeItem(id, branchId, frictionType, functionType, frictionValues[i], 0, numLocations, levels, chainages, i)); + } + return; + } + createExchangeItemsBasedOnSplit(branchId, frictionType, functionType, observationPointsList, chainages, levels, frictionValues); + } + + private void createExchangeItemsBasedOnSplit(String branchId, String frictionType, String functionType, List observationPointsList, double[] branchChainages, double[] levels, double[][] frictionValues) { + ArrayList chainageSplitIndices = getChainageSplitIndices(observationPointsList, branchChainages); + int previousSplit = 0; + for (int i = 0; i < chainageSplitIndices.size(); i++) { + int chainageSplitIndex = chainageSplitIndices.get(i); + createExchangeItemsForSegment(branchId, frictionType, functionType, branchChainages, levels, frictionValues, previousSplit, chainageSplitIndex, chainageSplitIndex); + previousSplit = chainageSplitIndex; + } + createExchangeItemsForSegment(branchId, frictionType, functionType, branchChainages, levels, frictionValues, previousSplit, branchChainages.length, branchChainages.length); + } + + private void createExchangeItemsForSegment(String branchId, String frictionType, String functionType, double[] branchChainages, double[] levels, double[][] frictionValues, int startIndex, int copyToIndex, int exchangeItemEndIndex) { + for (int levelIndex = 0; levelIndex < levels.length; levelIndex++) { + String id = getIdWithLevel(branchChainages[startIndex], levelIndex, branchId, functionType, frictionType); + double[] segmentFrictionValues = Arrays.copyOfRange(frictionValues[levelIndex], startIndex, copyToIndex); + exchangeItems.put(id, new DFlowFMSpatialRoughnessExchangeItem(id, branchId, frictionType, functionType, segmentFrictionValues, startIndex, exchangeItemEndIndex, levels, branchChainages, levelIndex)); + } + } + + private void createConstantExchangeItems(BufferedReader lineReader, String branchId, String frictionType, String functionType, List observationPointsList) throws IOException { + String[] keyValue; + String line; + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + int numLocations = Integer.parseInt(keyValue[1]); + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + String[] splitChainage = keyValue[1].trim().split(" "); + assert splitChainage.length == numLocations; + double[] branchChainages = new double[numLocations]; + for (int i = 0; i < numLocations; i++) { + branchChainages[i] = Double.parseDouble(splitChainage[i]); + } + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + + double[] frictionValues = new double[numLocations]; + String[] splitFrictionValues = keyValue[1].trim().split(" "); + for (int i = 0; i < splitFrictionValues.length; i++) { + String splitFrictionValue = splitFrictionValues[i]; + frictionValues[i] = Double.parseDouble(splitFrictionValue); + } + + if (observationPointsList == null) { + String id = getIdWithoutLevel(branchChainages[0], branchId, frictionType); + exchangeItems.put(id, new DFlowFMSpatialRoughnessExchangeItem(id, branchId, frictionType, functionType, frictionValues, 0, numLocations, null, branchChainages, 0)); + return; + } + createConstantExchangeItemsBasedOnSplit(branchId, frictionType, functionType, observationPointsList, branchChainages, frictionValues); + } + + private void createConstantExchangeItemsBasedOnSplit(String branchId, String frictionType, String functionType, List observationPointsList, double[] branchChainages, double[] frictionValues) { + ArrayList chainageSplitIndices = getChainageSplitIndices(observationPointsList, branchChainages); + int previousSplit = 0; + for (int i = 0; i < chainageSplitIndices.size(); i++) { + int chainageSplitIndex = chainageSplitIndices.get(i); + String id = getIdWithoutLevel(branchChainages[previousSplit], branchId, frictionType); + double[] segmentFrictionValues = Arrays.copyOfRange(frictionValues, previousSplit, chainageSplitIndex); + + exchangeItems.put(id, new DFlowFMSpatialRoughnessExchangeItem(id, branchId, frictionType, functionType, segmentFrictionValues, previousSplit, chainageSplitIndex, null, branchChainages, 0)); + previousSplit = chainageSplitIndex; + } + String id = getIdWithoutLevel(branchChainages[previousSplit], branchId, frictionType); + double[] segmentFrictionValues = Arrays.copyOfRange(frictionValues, previousSplit, branchChainages.length); + exchangeItems.put(id, new DFlowFMSpatialRoughnessExchangeItem(id, branchId, frictionType, functionType, segmentFrictionValues, previousSplit, branchChainages.length, null, branchChainages, 0)); + } + + private ArrayList getChainageSplitIndices(List observationPointsList, double[] branchChainages) { + ArrayList chainageSplitIndices = new ArrayList<>(); + for (int i = 0; i < observationPointsList.size(); i++) { + ObservationPoint observationPoint = observationPointsList.get(i); + int chainageSplit = findChainageSplit(branchChainages, observationPoint.getChainage()); + if (chainageSplit == -1) continue; + chainageSplitIndices.add(chainageSplit); + } + return chainageSplitIndices; + } + + private int findChainageSplit(double[] branchChainages, double observationChainage) { + int size = branchChainages.length; + for (int i = 0; i < size; i++) { + double chainage = branchChainages[i]; + if (chainage == observationChainage) return i; + if (chainage > observationChainage) return i - 1; + } + return size - 1; + } + + private String getLevelString(String functionType, int levelIndex) { + if (functionType.equals("absDischarge")) return "q" + levelIndex; + if (functionType.equals("waterLevel")) return "h" + levelIndex; + if (functionType.equals("Constant")) return ""; + throw new RuntimeException(String.format("Unknown function type %s, should be absDischarge, or Constant", functionType)); + } + + private String getIdWithLevel(double firstChainage, int levelIndex, String branchId, String functionType, String frictionType) { + return frictionId + + '-' + + frictionType + + '-' + + branchId + + '-' + + "x" + + Math.round(firstChainage) + + '-' + + getLevelString(functionType, levelIndex); + } + + private String getIdWithoutLevel(double firstChainage, String branchId, String frictionType) { + return frictionId + + '-' + + frictionType + + '-' + + branchId + + '-' + + "x" + + Math.round(firstChainage); + } + + private DFlowFMSpatialRoughnessExchangeItem readContent(BufferedReader lineReader) throws IOException { + + String line = lineReader.readLine(); + String[] keyValue = readKeyValueLine(line); + frictionId = keyValue[1]; + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + globalFrictionType = keyValue[1]; + + line = lineReader.readLine(); + keyValue = readKeyValueLine(line); + globalFrictionValue = Double.parseDouble(keyValue[1]); + + String mainExchangeItemId = frictionId + "-model_wide-" + globalFrictionType; + + line = lineReader.readLine(); + while (!line.isEmpty()) { + line = lineReader.readLine(); + } + + return new DFlowFMSpatialRoughnessExchangeItem(mainExchangeItemId, frictionId, globalFrictionType, null, new double[]{globalFrictionValue}, -1, -1, null, null, 0); + } + + private String[] readKeyValueLine(String line) throws IOException { + String[] split = line.split("="); + assert split.length == 2; + split[0] = split[0].trim(); + split[1] = split[1].trim(); + return split; + } + + static class ObservationPoint implements Comparable { + private final String id; + private final String branchId; + private final double chainage; + private final String name; + + public ObservationPoint(String id, String branchId, double chainage, String name) { + this.id = id; + this.branchId = branchId; + this.chainage = chainage; + this.name = name; + } + + public String getId() { + return id; + } + + public String getBranchId() { + return branchId; + } + + public double getChainage() { + return chainage; + } + + public String getName() { + return name; + } + + @Override + public int compareTo(ObservationPoint other) { + int compareTo = branchId.compareTo(other.branchId); + if (compareTo != 0) return compareTo; + return Double.compare(chainage, other.chainage); + } + } +} diff --git a/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java new file mode 100644 index 000000000..0f4a58f34 --- /dev/null +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java @@ -0,0 +1,103 @@ +package org.openda.model_dflowfm; + +import junit.framework.TestCase; +import org.openda.interfaces.IExchangeItem; +import org.openda.utils.OpenDaTestSupport; +import org.openda.utils.io.AsciiFileUtils; + +import java.io.File; + +public class DFlowFMSpatialRoughnessFileTest extends TestCase { + private File testRunDataDir; + private OpenDaTestSupport testData; + + protected void setUp() { + testData = new OpenDaTestSupport(DFlowFMSpatialRoughnessFileTest.class, "model_dflowfm_blackbox"); + testRunDataDir = new File(testData.getTestRunDataDir(), "SpatialRoughness"); + } + + public void testReadAndWrite() { + DFlowFMSpatialRoughnessFile dFlowFMSpatialRoughnessFile = new DFlowFMSpatialRoughnessFile(); + dFlowFMSpatialRoughnessFile.initialize(testRunDataDir, new String[]{"roughness-Main.ini", "observationFile=obsFile1D_obs.ini"}); + String[] exchangeItemIDs = dFlowFMSpatialRoughnessFile.getExchangeItemIDs(); + assertEquals(21, exchangeItemIDs.length); + + String[] expectedIds = {"Main-model_wide-Chezy", + "Main-Manning-Constant_1_chainage-x0", + "Main-Manning-Constant_2_chainage-x0", + "Main-Manning-Constant_2_chainage-x200", + "Main-Manning-ChainageAndDischargeDependent-x0-q0", + "Main-Manning-ChainageAndDischargeDependent-x0-q1", + "Main-Manning-ChainageAndDischargeDependent-x0-q2", + "Main-Manning-ChainageAndDischargeDependent-x0-q3", + "Main-Manning-ChainageAndDischargeDependent-x0-q4", + "Main-Manning-ChainageAndDischargeDependent-x500-q0", + "Main-Manning-ChainageAndDischargeDependent-x500-q1", + "Main-Manning-ChainageAndDischargeDependent-x500-q2", + "Main-Manning-ChainageAndDischargeDependent-x500-q3", + "Main-Manning-ChainageAndDischargeDependent-x500-q4", + "Main-Manning-ChainageAndWaterlevelDependent-x0-h0", + "Main-Manning-ChainageAndWaterlevelDependent-x0-h1", + "Main-Manning-ChainageAndWaterlevelDependent-x0-h2", + "Main-Manning-ChainageAndWaterlevelDependent-x0-h3", + "Main-Manning-ChainageAndWaterlevelDependent-x0-h4", + "Main-Manning-ChainageAndWaterlevelDependent-x0-h5", + "Main-Manning-ChainageAndWaterlevelDependent-x0-h6" + }; + + for (int i = 0; i < expectedIds.length; i++) { + assertEquals(expectedIds[i], exchangeItemIDs[i]); + } + IExchangeItem chezy = checkEI(dFlowFMSpatialRoughnessFile, "Main-model_wide-Chezy", new double[]{45.0}); + chezy.setValuesAsDoubles(new double[]{50.0}); + IExchangeItem b0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Constant_2_chainage-x0", new double[]{0.03}); + b0.setValuesAsDoubles(new double[]{0.033}); + IExchangeItem b200 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Constant_2_chainage-x200", new double[]{0.032}); + b200.setValuesAsDoubles(new double[]{0.035}); + IExchangeItem x0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Constant_1_chainage-x0", new double[]{0.028}); + x0.setValuesAsDoubles(new double[]{0.031}); + IExchangeItem x0q0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x0-q0", new double[]{0.03}); + x0q0.setValuesAsDoubles(new double[]{0.0314}); + IExchangeItem x0q1 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x0-q1", new double[]{0.029}); + x0q1.setValuesAsDoubles(new double[]{0.0345}); + IExchangeItem x0q2 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x0-q2", new double[]{0.029}); + x0q2.setValuesAsDoubles(new double[]{0.0579}); + IExchangeItem x0q3 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x0-q3", new double[]{0.028}); + x0q3.setValuesAsDoubles(new double[]{0.0347}); + IExchangeItem x0q4 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x0-q4", new double[]{0.027}); + x0q4.setValuesAsDoubles(new double[]{0.0581}); + IExchangeItem x500q0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x500-q0", new double[]{0.03, 0.025}); + x500q0.setValuesAsDoubles(new double[]{0.02978, 0.02963}); + IExchangeItem x500q1 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x500-q1", new double[]{0.025, 0.025}); + x500q1.setValuesAsDoubles(new double[]{0.02576, 0.02634}); + IExchangeItem x500q2 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x500-q2", new double[]{0.026, 0.023}); + x500q2.setValuesAsDoubles(new double[]{0.02513, 0.02346}); + IExchangeItem x500q3 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x500-q3", new double[]{0.0259, 0.022}); + x500q3.setValuesAsDoubles(new double[]{0.02576, 0.02634}); + IExchangeItem x500q4 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-ChainageAndDischargeDependent-x500-q4", new double[]{0.0258, 0.021}); + x500q4.setValuesAsDoubles(new double[]{0.02513, 0.02346}); + + dFlowFMSpatialRoughnessFile.finish(); + + File resultFile = new File(testRunDataDir, "roughness-Main.ini"); + assertTrue(resultFile.exists()); + assertEquals(AsciiFileUtils.readText(new File(testRunDataDir, "expected_roughness-Main.ini")), AsciiFileUtils.readText(resultFile)); + } + + private IExchangeItem checkEI(DFlowFMSpatialRoughnessFile dFlowFMSpatialRoughnessFile, String exchangeItemID, double[] expectedValues) { + IExchangeItem exchangeItem = dFlowFMSpatialRoughnessFile.getDataObjectExchangeItem(exchangeItemID); + double[] modelWideValues = exchangeItem.getValuesAsDoubles(); + assertEquals(expectedValues.length, modelWideValues.length); + for (int i = 0; i < expectedValues.length; i++) { + assertEquals(expectedValues[i], modelWideValues[i], 1e-6); + } + return exchangeItem; + } + +/* public void testReadAndWriteOlof() { + DFlowFMSpatialRoughnessFile dFlowFMSpatialRoughnessFile = new DFlowFMSpatialRoughnessFile(); + dFlowFMSpatialRoughnessFile.initialize(testRunDataDir, new String[]{"roughness-Main-Olof.ini", "observationFile=ObservationPoints-Olof.ini"}); + String[] exchangeItemIDs = dFlowFMSpatialRoughnessFile.getExchangeItemIDs(); + assertEquals(10, exchangeItemIDs.length); + }*/ +} diff --git a/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/expected_roughness-Main.ini b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/expected_roughness-Main.ini new file mode 100644 index 000000000..82c8e9038 --- /dev/null +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/expected_roughness-Main.ini @@ -0,0 +1,55 @@ +[General] +fileVersion=3.00 +fileType=roughness + +[Global] +frictionId=Main +frictionType=Chezy +frictionValue=45.0 + +[Branch] +branchId=Constant_1_chainage +frictionType=Manning +functionType=Constant +numLocations=1 +chainage= 0.0 +frictionValues= 0.031 + +[Branch] +branchId=Constant_2_chainage +frictionType=Manning +functionType=Constant +numLocations=2 +chainage= 0.0 200.0 +frictionValues= 0.033 0.035 + +[Branch] +branchId=ChainageAndDischargeDependent +frictionType=Manning +functionType=absDischarge +numLevels=5 +levels= 100.0 300.0 500.0 700.0 1000.0 +numLocations=3 +chainage= 0.0 500.0 1000.0 +frictionValues= 0.0314 0.02978 0.02963 + 0.0345 0.02576 0.02634 + 0.0579 0.02513 0.02346 + 0.0347 0.02576 0.02634 + 0.0581 0.02513 0.02346 + +[Branch] +branchId=ChainageAndWaterlevelDependent +frictionType=Manning +functionType=waterLevel +numLevels=7 +levels= 100.0 200.0 300.0 400.0 500.0 700.0 1000.0 +numLocations=5 +chainage= 0.0 100.0 300.0 500.0 1000.0 +frictionValues= 0.1 0.2 0.3 0.4 0.5 + 1.1 1.2 1.3 1.4 1.5 + 2.1 2.2 2.3 2.4 2.5 + 3.1 3.2 3.3 3.4 3.5 + 4.1 4.2 4.3 4.4 4.5 + 5.1 5.2 5.3 5.4 5.5 + 6.1 6.2 6.3 6.4 6.5 + diff --git a/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/obsFile1D_obs.ini b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/obsFile1D_obs.ini new file mode 100644 index 000000000..4c751f197 --- /dev/null +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/obsFile1D_obs.ini @@ -0,0 +1,13 @@ +[General] + fileVersion = 2.00 + fileType = obsPoints + +[ObservationPoint] + name = Obs1 + branchId = ChainageAndDischargeDependent + chainage = 679.50351792411624 + +[ObservationPoint] + name = Obs2 + branchId = Constant_2_chainage + chainage = 289.14553085136981 diff --git a/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/roughness-Main.ini b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/roughness-Main.ini new file mode 100644 index 000000000..47571751d --- /dev/null +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/roughness-Main.ini @@ -0,0 +1,54 @@ +[General] + fileVersion = 3.00 + fileType = roughness + +[Global] + frictionId = Main + frictionType = Chezy + frictionValue = 45.00000 + +[Branch] + branchId = Constant_1_chainage + frictionType = Manning + functionType = Constant + numLocations = 1 + chainage = 0.000000 + frictionValues = 0.02800 + +[Branch] + branchId = Constant_2_chainage + frictionType = Manning + functionType = Constant + numLocations = 2 + chainage = 0.000000 200.000000 + frictionValues = 0.03000 0.03200 + +[Branch] + branchId = ChainageAndDischargeDependent + frictionType = Manning + functionType = absDischarge + numLevels = 5 + levels = 100.000 300.000 500.000 700.000 1000.000 + numLocations = 3 + chainage = 0.000000 500.000000 1000.000000 + frictionValues = 0.03000 0.03000 0.02500 +0.02900 0.02500 0.02500 +0.02900 0.02600 0.02300 +0.02800 0.02590 0.02200 +0.02700 0.02580 0.02100 + +[Branch] + branchId = ChainageAndWaterlevelDependent + frictionType = Manning + functionType = waterLevel + numLevels = 7 + levels = 100.000 200.000 300.000 400.00 500.000 700.000 1000.000 + numLocations = 5 + chainage = 0.000000 100 300 500.000000 1000.000000 + frictionValues = 0.1 0.2 0.3 0.4 0.5 +1.1 1.2 1.3 1.4 1.5 +2.1 2.2 2.3 2.4 2.5 +3.1 3.2 3.3 3.4 3.5 +4.1 4.2 4.3 4.4 4.5 +5.1 5.2 5.3 5.4 5.5 +6.1 6.2 6.3 6.4 6.5