From a1cefcbbe04a4372f7f35c49ede27875bde0d3ea Mon Sep 17 00:00:00 2001 From: Erik Pelgrim Date: Thu, 5 Feb 2026 15:52:13 +0100 Subject: [PATCH 01/15] Roughness D-Flow FM, initial setup --- .../DFlowFMSpatialRoughnessExchangeItem.java | 344 +++++++++++++ .../DFlowFMSpatialRoughnessFile.java | 475 ++++++++++++++++++ .../DFlowFMSpatialRoughnessFileTest.java | 23 + .../SpatialRoughness/obsFile1D_obs.ini | 19 + .../SpatialRoughness/roughness-Main.ini | 38 ++ 5 files changed, 899 insertions(+) create mode 100644 model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java create mode 100644 model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java create mode 100644 model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java create mode 100644 model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/obsFile1D_obs.ini create mode 100644 model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/roughness-Main.ini 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..01239f0bb --- /dev/null +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java @@ -0,0 +1,344 @@ +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; + +import java.util.*; + +import static org.openda.model_dflowfm.DFlowFMSpatialRoughnessFile.REVERSED_POSTFIX; + +public class DFlowFMSpatialRoughnessExchangeItem implements IExchangeItem { + + private String id; + private String type; + private double[] values; + private final int chainageStartIndex; + private final int chainageEndIndex; + private InitialBranchDefinitions initialBranchDefinitions; + + public DFlowFMSpatialRoughnessExchangeItem(String id, String type, double[] values, int chainageStartIndex, int chainageEndIndex) { + this.id = id; + this.type = type; + this.values = values; + this.chainageStartIndex = chainageStartIndex; + this.chainageEndIndex = chainageEndIndex; + } + + @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-" + type, ""); + } + + @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 void setInitialBranchDefinitions(InitialBranchDefinitions initialBranchDefinitions) { + this.initialBranchDefinitions = initialBranchDefinitions; + } + + public InitialBranchDefinitions getInitialBranchDefinitions() { + return initialBranchDefinitions; + } + + public int getChainageStartIndex() { + return chainageStartIndex; + } + + public int getChainageEndIndex() { + return chainageEndIndex; + } + + + //Used for collecting data for making exchange items en rewriting .ini file + @SuppressWarnings("WeakerAccess") + static class InitialBranchDefinitions { + + private String sectionId; + private String branchId; + private String roughnessType; + private String functionType; + private double[] levels; + private List chainages; + private List listOfValueArrays; + private Map chainageValuesMap = new TreeMap<>(); + private String flowDirection; + + public InitialBranchDefinitions() {} + + public List createExchangeItems(Map> observationPointsMap) { + List exchangeItems = new ArrayList<>(); + List observationPointList = observationPointsMap.get(branchId); + chainages = new ArrayList<>(chainageValuesMap.keySet()); + listOfValueArrays = new ArrayList<>(chainageValuesMap.values()); + + if (observationPointList != null) { + Collections.sort(observationPointList); + createExchangeItemsForObservationPoints(exchangeItems, observationPointList); + } else { + double minChainage = getMinChainage(); + String firstChainage = "x" + String.valueOf(Math.round(minChainage)); + if (levels == null) { + createExchangeItemsWithoutLevels(exchangeItems, 0, chainages.size() - 1, firstChainage); + } else { + createExchangeItemPerLevel(exchangeItems, 0, chainages.size() - 1, firstChainage); + } + } + return exchangeItems; + } + + private void createExchangeItemsForObservationPoints(List list, List observationPointList) { + List splitIndices = new ArrayList<>(); + splitIndices.add(0); + for (int k = 0; k < observationPointList.size(); k++) { + DFlowFMSpatialRoughnessFile.ObservationPoint observationPoint = observationPointList.get(k); + int chainageSplitIndex = findChainageSplit(observationPoint); + if (chainageSplitIndex <= 0) continue; + splitIndices.add(chainageSplitIndex); + } + + for (int k = 0, n = splitIndices.size(); k < n; k++) { + int splitStartIndex = splitIndices.get(k); + int splitEndIndex = k == n - 1 ? chainages.size() - 1 : splitIndices.get(k + 1) - 1; + double minChainage = chainages.get(splitStartIndex); + String firstChainage = "x" + String.valueOf(Math.round(minChainage)); + + if (levels == null) { + createExchangeItemsWithoutLevels(list, splitStartIndex, splitEndIndex, firstChainage); + } else { + createExchangeItemPerLevel(list, splitStartIndex, splitEndIndex, firstChainage); + } + } + } + + private void createExchangeItemsWithoutLevels(List list, int chainageStartIndex, int chainageEndIndex, String firstChainage) { + String id = getIdWithoutLevel(firstChainage); + int size = chainageEndIndex + 1 - chainageStartIndex; + double[] values = new double[size]; + for (int j = chainageStartIndex; j <= chainageEndIndex; j++) { + double[] listOfValueArray = listOfValueArrays.get(j); + values[j - chainageStartIndex] = listOfValueArray[0]; + } + DFlowFMSpatialRoughnessExchangeItem exchangeItem = new DFlowFMSpatialRoughnessExchangeItem(id, roughnessType, values, chainageStartIndex, chainageEndIndex); + exchangeItem.setInitialBranchDefinitions(this); + list.add(exchangeItem); + } + + private String getIdWithoutLevel(String firstChainage) { + StringBuilder noLevelIdBuilder = new StringBuilder(30); + noLevelIdBuilder.append(sectionId); + if (flowDirection.equalsIgnoreCase("true") || flowDirection.equals("1")) noLevelIdBuilder.append(REVERSED_POSTFIX); + noLevelIdBuilder.append('-'); + noLevelIdBuilder.append(roughnessType); + noLevelIdBuilder.append('-'); + noLevelIdBuilder.append(branchId); + noLevelIdBuilder.append('-'); + noLevelIdBuilder.append(firstChainage); + return noLevelIdBuilder.toString(); + } + + private void createExchangeItemPerLevel(List list, int chainageStartIndex, int chainageEndIndex, String firstChainage) { + for (int i = 0; i < levels.length; i++) { + double level = levels[i]; + String id = getIdWithLevel(firstChainage, i + 1); + double[] values = new double[chainageEndIndex + 1 - chainageStartIndex]; + for (int j = chainageStartIndex; j <= chainageEndIndex; j++) { + double[] listOfValueArray = listOfValueArrays.get(j); + values[j - chainageStartIndex] = listOfValueArray[i]; + } + DFlowFMSpatialRoughnessExchangeItem exchangeItem = new DFlowFMSpatialRoughnessExchangeItem(id, roughnessType, values, chainageStartIndex, chainageEndIndex); + exchangeItem.setInitialBranchDefinitions(this); + list.add(exchangeItem); + } + } + + private String getIdWithLevel(String firstChainage, int levelIndex) { + StringBuilder levelIdBuilder = new StringBuilder(30); + levelIdBuilder.append(sectionId); + if (flowDirection.equalsIgnoreCase("true") || flowDirection.equals("1")) levelIdBuilder.append(REVERSED_POSTFIX); + levelIdBuilder.append('-'); + levelIdBuilder.append(roughnessType); + levelIdBuilder.append('-'); + levelIdBuilder.append(branchId); + levelIdBuilder.append('-'); + levelIdBuilder.append(firstChainage); + levelIdBuilder.append('-'); + levelIdBuilder.append(functionType); + levelIdBuilder.append(levelIndex); + return levelIdBuilder.toString(); + } + + private int findChainageSplit(DFlowFMSpatialRoughnessFile.ObservationPoint observationPoint) { + double chainageObservation = observationPoint.getChainage(); + int size = chainages.size(); + for (int i = 0; i < size; i++) { + double chainage = chainages.get(i); + if (chainage == chainageObservation) return i; + if (chainage > chainageObservation) return i - 1; + } + return size - 1; + } + + private double getMinChainage() { + double minChainage = Double.MAX_VALUE; + for (Double chainage : chainages) { + if (chainage < minChainage) minChainage = chainage; + } + return minChainage; + } + + public String getBranchId() { + return branchId; + } + + public String getRoughnessType() { + return roughnessType; + } + + public String getFunctionType() { + return functionType; + } + + public double[] getLevels() { + return levels; + } + + public List getChainages() { + return chainages; + } + + public void setFrictionId(String sectionId) { + this.sectionId = sectionId; + } + + public void setBranchId(String branchId) { + this.branchId = branchId; + } + + public void setFrictionType(String frictionType) { + this.roughnessType = frictionType; + } + + public void setLevels(double[] levels) { + this.levels = levels; + } + + public void setFunctionType(String functionType) { + this.functionType = functionType; + } + + public void setFlowDirection(String flowDirection) { + this.flowDirection = flowDirection; + } + } + + //Used for collecting updated values of Definitions that will be written + @SuppressWarnings("WeakerAccess") + static class WriteDefinition { + + private String branchId; + private double chainage; + private List values = new ArrayList<>(); + + public WriteDefinition(String branchId, Double chainage) { + this.branchId = branchId; + this.chainage = chainage; + } + + public void addValue(double value) { + this.values.add(value); + } + + public String getBranchId() { + return branchId; + } + + public double getChainage() { + return chainage; + } + + public List getValues() { + return values; + } + } +} 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..c65c223e5 --- /dev/null +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -0,0 +1,475 @@ +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 ROUGHNESS_TYPE = "roughnessType"; + private static final String FUNCTION_TYPE = "functionType"; + private static final String NUM_LEVELS = "numLevels"; + private static final String LEVELS = "levels"; + private static final String CHAINAGE = "chainage"; + private static final String VALUES = "values"; + private static final String VALUE = "value"; + + 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); + + Set writtenBranchProperties = new HashSet<>(); + for (IExchangeItem item : exchangeItems.values()) { + DFlowFMSpatialRoughnessExchangeItem exchangeItem = (DFlowFMSpatialRoughnessExchangeItem) item; + DFlowFMSpatialRoughnessExchangeItem.InitialBranchDefinitions initialBranchDefinitions = exchangeItem.getInitialBranchDefinitions(); + if (initialBranchDefinitions == null) continue; + String branchId = initialBranchDefinitions.getBranchId(); + if (!writtenBranchProperties.add(branchId)) continue; + appendBranchProperties(builder, initialBranchDefinitions, branchId); + } + + lineWriter.write(builder.toString()); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + private void appendBranchProperties(StringBuilder builder, DFlowFMSpatialRoughnessExchangeItem.InitialBranchDefinitions initialBranchDefinitions, String branchId) { + builder.append(BRANCH + '\n'); + builder.append(BRANCH_ID + '=').append(branchId).append('\n'); + builder.append(ROUGHNESS_TYPE + '=').append(initialBranchDefinitions.getRoughnessType()).append('\n'); + String functionType = initialBranchDefinitions.getFunctionType(); + builder.append(FUNCTION_TYPE + '=').append(functionType).append('\n'); + if (functionType.equals("constant")) { + builder.append('\n'); + return; + } + double[] levels = initialBranchDefinitions.getLevels(); + assert levels != null && levels.length > 0; + builder.append(NUM_LEVELS + '=').append(levels.length).append('\n'); + builder.append(LEVELS + '='); + for (double level : levels) { + builder.append(" ").append(level); + } + 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 = null; + if (observationSelectionFile != null) { + 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); + } + } + //noinspection unchecked + Map> observationPoints = observationFile != null ? readObservationFile(observationPointIdsSelection) : Collections.EMPTY_MAP; + + readSpatialDefinitionFile(observationPoints); + } + + 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); + + Map initialBranchDefinitionsMap = new LinkedHashMap<>(); + + line = lineReader.readLine(); + while (line.startsWith(BRANCH)) { + readBranch(lineReader, initialBranchDefinitionsMap, 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 initialBranchDefinitionsMapExchangeItemsMap, Map> observationPoints) 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(); + + 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 < numLevels; 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.isEmpty()) { + String[] splitFrictionValues = value.trim().split(" "); + for (int i = 0; i < splitFrictionValues.length; i++) { + String splitFrictionValue = splitFrictionValues[i]; + frictionValues[i][index] = Double.parseDouble(splitFrictionValue); + } + index++; + value = lineReader.readLine(); + } + + for (int i = 0; i < numLevels; i++) { + String id = getIdWithLevel(chainages[0], 0, branchId, functionType, frictionType); + exchangeItems.put(id, new DFlowFMSpatialRoughnessExchangeItem(id, frictionType, frictionValues[i], 0, numLocations - 1)); + } + } + + private String getLevelString(String functionType, int levelIndex) { + if (functionType.equals("absDischarge")) return "q" + levelIndex; + if (functionType.equals("Constant")) return ""; + throw new RuntimeException("Unknown function type, should be absDischarge, or Constant"); + } + + 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(String firstChainage, String branchId, String frictionType) { + StringBuilder levelIdBuilder = new StringBuilder(30); + levelIdBuilder.append(branchId); + levelIdBuilder.append('-'); + levelIdBuilder.append(frictionType); + levelIdBuilder.append('-'); + levelIdBuilder.append(branchId); + levelIdBuilder.append('-'); + levelIdBuilder.append(firstChainage); + return levelIdBuilder.toString(); + } + + 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, globalFrictionType, new double[]{globalFrictionValue}, 0, 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..c2191454f --- /dev/null +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java @@ -0,0 +1,23 @@ +package org.openda.model_dflowfm; + +import junit.framework.TestCase; +import org.openda.utils.OpenDaTestSupport; + +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(2, exchangeItemIDs.length); + } +} 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..5986cf466 --- /dev/null +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/obsFile1D_obs.ini @@ -0,0 +1,19 @@ +[General] + fileVersion = 2.00 + fileType = obsPoints + +[ObservationPoint] + name = Obs1 + branchId = Channel_1D_1_A + chainage = 379.50351792411624 + +[ObservationPoint] + name = Obs2 + branchId = Channel_1D_1_B + chainage = 289.14553085136981 + +[ObservationPoint] + name = Obs3 + branchId = Channel_1D_1 + chainage = 369.2842857729857 + 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..1807f48bd --- /dev/null +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/roughness-Main.ini @@ -0,0 +1,38 @@ +[General] + fileVersion = 3.00 + fileType = roughness + +[Global] + frictionId = Main + frictionType = Chezy + frictionValue = 45.00000 + +[Branch] + branchId = Channel_1D_1_A + frictionType = Manning + functionType = absDischarge + numLevels = 3 + levels = 100.000 500.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 + + +[Branch] + branchId = Channel_1D_1_B + frictionType = Manning + functionType = Constant + numLocations = 2 + chainage = 0.000000 200.000000 + frictionValues = 0.03000 0.03200 + +[Branch] + branchId = Channel_1D_1 + frictionType = Manning + functionType = Constant + numLocations = 1 + chainage = 0.000000 + frictionValues = 0.02800 + From 3f4eff8f498539a48952eb9e05744baa639d9a10 Mon Sep 17 00:00:00 2001 From: Erik Pelgrim Date: Thu, 5 Feb 2026 16:15:21 +0100 Subject: [PATCH 02/15] Roughness D-Flow FM --- .../DFlowFMSpatialRoughnessExchangeItem.java | 6 +- .../DFlowFMSpatialRoughnessFile.java | 105 +++++++++++------- .../DFlowFMSpatialRoughnessFileTest.java | 2 +- 3 files changed, 71 insertions(+), 42 deletions(-) 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 index 01239f0bb..b924dab00 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java @@ -19,7 +19,7 @@ public class DFlowFMSpatialRoughnessExchangeItem implements IExchangeItem { private final int chainageEndIndex; private InitialBranchDefinitions initialBranchDefinitions; - public DFlowFMSpatialRoughnessExchangeItem(String id, String type, double[] values, int chainageStartIndex, int chainageEndIndex) { + public DFlowFMSpatialRoughnessExchangeItem(String id, String type, double[] values, int chainageStartIndex, int chainageEndIndex, double[] levels) { this.id = id; this.type = type; this.values = values; @@ -199,7 +199,7 @@ private void createExchangeItemsWithoutLevels(List observationPointIdsSelection = null; - if (observationSelectionFile != null) { - 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); - } - } + 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<>(); @@ -285,12 +289,9 @@ private void readSpatialDefinitionFile(Map> obser DFlowFMSpatialRoughnessExchangeItem globalItem = readContent(lineReader); exchangeItems.put(globalItem.getId(), globalItem); - Map initialBranchDefinitionsMap = new LinkedHashMap<>(); - line = lineReader.readLine(); - while (line.startsWith(BRANCH)) { - readBranch(lineReader, initialBranchDefinitionsMap, observationPoints); - + while (line != null) { + if (line.startsWith(BRANCH)) readBranch(lineReader, observationPoints); line = lineReader.readLine(); } } catch (IOException e) { @@ -313,7 +314,7 @@ private void readGeneral(BufferedReader lineReader) throws IOException { } } - private void readBranch(BufferedReader lineReader, Map initialBranchDefinitionsMapExchangeItemsMap, Map> observationPoints) throws IOException { + private void readBranch(BufferedReader lineReader, Map> observationPoints) throws IOException { String line = lineReader.readLine(); String[] keyValue = readKeyValueLine(line); String branchId = keyValue[1].trim(); @@ -326,6 +327,35 @@ private void readBranch(BufferedReader lineReader, Map Date: Fri, 22 May 2026 11:41:23 +0200 Subject: [PATCH 03/15] Include levels in id --- .../DFlowFMSpatialRoughnessFile.java | 2 +- .../DFlowFMSpatialRoughnessFileTest.java | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) 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 index 9a3af7ed1..611fd7d61 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -398,7 +398,7 @@ private void readBranch(BufferedReader lineReader, Map Date: Fri, 22 May 2026 12:42:55 +0200 Subject: [PATCH 04/15] Implement finish, work in progress --- .../DFlowFMSpatialRoughnessExchangeItem.java | 241 ++---------------- .../DFlowFMSpatialRoughnessFile.java | 59 +++-- .../DFlowFMSpatialRoughnessFileTest.java | 5 + 3 files changed, 53 insertions(+), 252 deletions(-) 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 index b924dab00..468cdc2f9 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java @@ -6,25 +6,26 @@ import org.openda.interfaces.IQuantityInfo; import org.openda.interfaces.ITimeInfo; -import java.util.*; - -import static org.openda.model_dflowfm.DFlowFMSpatialRoughnessFile.REVERSED_POSTFIX; - public class DFlowFMSpatialRoughnessExchangeItem implements IExchangeItem { - private String id; - private String type; + private final String id; + private final String type; + private final String branchId; + private final String functionType; private double[] values; private final int chainageStartIndex; private final int chainageEndIndex; - private InitialBranchDefinitions initialBranchDefinitions; + private final double level; - public DFlowFMSpatialRoughnessExchangeItem(String id, String type, double[] values, int chainageStartIndex, int chainageEndIndex, double[] levels) { + public DFlowFMSpatialRoughnessExchangeItem(String id, String type, String branchId, String functionType, double[] values, int chainageStartIndex, int chainageEndIndex, double level) { this.id = id; this.type = type; + this.branchId = branchId; + this.functionType = functionType; this.values = values; this.chainageStartIndex = chainageStartIndex; this.chainageEndIndex = chainageEndIndex; + this.level = level; } @Override @@ -113,14 +114,6 @@ public void setTimes(double[] times) { throw new RuntimeException(this.getClass().getName() + "setTimes(): time stamps can not be set"); } - public void setInitialBranchDefinitions(InitialBranchDefinitions initialBranchDefinitions) { - this.initialBranchDefinitions = initialBranchDefinitions; - } - - public InitialBranchDefinitions getInitialBranchDefinitions() { - return initialBranchDefinitions; - } - public int getChainageStartIndex() { return chainageStartIndex; } @@ -130,215 +123,19 @@ public int getChainageEndIndex() { } - //Used for collecting data for making exchange items en rewriting .ini file - @SuppressWarnings("WeakerAccess") - static class InitialBranchDefinitions { - - private String sectionId; - private String branchId; - private String roughnessType; - private String functionType; - private double[] levels; - private List chainages; - private List listOfValueArrays; - private Map chainageValuesMap = new TreeMap<>(); - private String flowDirection; - - public InitialBranchDefinitions() {} - - public List createExchangeItems(Map> observationPointsMap) { - List exchangeItems = new ArrayList<>(); - List observationPointList = observationPointsMap.get(branchId); - chainages = new ArrayList<>(chainageValuesMap.keySet()); - listOfValueArrays = new ArrayList<>(chainageValuesMap.values()); - - if (observationPointList != null) { - Collections.sort(observationPointList); - createExchangeItemsForObservationPoints(exchangeItems, observationPointList); - } else { - double minChainage = getMinChainage(); - String firstChainage = "x" + String.valueOf(Math.round(minChainage)); - if (levels == null) { - createExchangeItemsWithoutLevels(exchangeItems, 0, chainages.size() - 1, firstChainage); - } else { - createExchangeItemPerLevel(exchangeItems, 0, chainages.size() - 1, firstChainage); - } - } - return exchangeItems; - } - - private void createExchangeItemsForObservationPoints(List list, List observationPointList) { - List splitIndices = new ArrayList<>(); - splitIndices.add(0); - for (int k = 0; k < observationPointList.size(); k++) { - DFlowFMSpatialRoughnessFile.ObservationPoint observationPoint = observationPointList.get(k); - int chainageSplitIndex = findChainageSplit(observationPoint); - if (chainageSplitIndex <= 0) continue; - splitIndices.add(chainageSplitIndex); - } - - for (int k = 0, n = splitIndices.size(); k < n; k++) { - int splitStartIndex = splitIndices.get(k); - int splitEndIndex = k == n - 1 ? chainages.size() - 1 : splitIndices.get(k + 1) - 1; - double minChainage = chainages.get(splitStartIndex); - String firstChainage = "x" + String.valueOf(Math.round(minChainage)); - - if (levels == null) { - createExchangeItemsWithoutLevels(list, splitStartIndex, splitEndIndex, firstChainage); - } else { - createExchangeItemPerLevel(list, splitStartIndex, splitEndIndex, firstChainage); - } - } - } - - private void createExchangeItemsWithoutLevels(List list, int chainageStartIndex, int chainageEndIndex, String firstChainage) { - String id = getIdWithoutLevel(firstChainage); - int size = chainageEndIndex + 1 - chainageStartIndex; - double[] values = new double[size]; - for (int j = chainageStartIndex; j <= chainageEndIndex; j++) { - double[] listOfValueArray = listOfValueArrays.get(j); - values[j - chainageStartIndex] = listOfValueArray[0]; - } - DFlowFMSpatialRoughnessExchangeItem exchangeItem = new DFlowFMSpatialRoughnessExchangeItem(id, roughnessType, values, chainageStartIndex, chainageEndIndex, levels); - exchangeItem.setInitialBranchDefinitions(this); - list.add(exchangeItem); - } - - private String getIdWithoutLevel(String firstChainage) { - StringBuilder noLevelIdBuilder = new StringBuilder(30); - noLevelIdBuilder.append(sectionId); - if (flowDirection.equalsIgnoreCase("true") || flowDirection.equals("1")) noLevelIdBuilder.append(REVERSED_POSTFIX); - noLevelIdBuilder.append('-'); - noLevelIdBuilder.append(roughnessType); - noLevelIdBuilder.append('-'); - noLevelIdBuilder.append(branchId); - noLevelIdBuilder.append('-'); - noLevelIdBuilder.append(firstChainage); - return noLevelIdBuilder.toString(); - } - - private void createExchangeItemPerLevel(List list, int chainageStartIndex, int chainageEndIndex, String firstChainage) { - for (int i = 0; i < levels.length; i++) { - double level = levels[i]; - String id = getIdWithLevel(firstChainage, i + 1); - double[] values = new double[chainageEndIndex + 1 - chainageStartIndex]; - for (int j = chainageStartIndex; j <= chainageEndIndex; j++) { - double[] listOfValueArray = listOfValueArrays.get(j); - values[j - chainageStartIndex] = listOfValueArray[i]; - } - DFlowFMSpatialRoughnessExchangeItem exchangeItem = new DFlowFMSpatialRoughnessExchangeItem(id, roughnessType, values, chainageStartIndex, chainageEndIndex, levels); - exchangeItem.setInitialBranchDefinitions(this); - list.add(exchangeItem); - } - } - - private String getIdWithLevel(String firstChainage, int levelIndex) { - StringBuilder levelIdBuilder = new StringBuilder(30); - levelIdBuilder.append(sectionId); - if (flowDirection.equalsIgnoreCase("true") || flowDirection.equals("1")) levelIdBuilder.append(REVERSED_POSTFIX); - levelIdBuilder.append('-'); - levelIdBuilder.append(roughnessType); - levelIdBuilder.append('-'); - levelIdBuilder.append(branchId); - levelIdBuilder.append('-'); - levelIdBuilder.append(firstChainage); - levelIdBuilder.append('-'); - levelIdBuilder.append(functionType); - levelIdBuilder.append(levelIndex); - return levelIdBuilder.toString(); - } - - private int findChainageSplit(DFlowFMSpatialRoughnessFile.ObservationPoint observationPoint) { - double chainageObservation = observationPoint.getChainage(); - int size = chainages.size(); - for (int i = 0; i < size; i++) { - double chainage = chainages.get(i); - if (chainage == chainageObservation) return i; - if (chainage > chainageObservation) return i - 1; - } - return size - 1; - } - - private double getMinChainage() { - double minChainage = Double.MAX_VALUE; - for (Double chainage : chainages) { - if (chainage < minChainage) minChainage = chainage; - } - return minChainage; - } - - public String getBranchId() { - return branchId; - } - - public String getRoughnessType() { - return roughnessType; - } - - public String getFunctionType() { - return functionType; - } - - public double[] getLevels() { - return levels; - } - - public List getChainages() { - return chainages; - } - - public void setFrictionId(String sectionId) { - this.sectionId = sectionId; - } - - public void setBranchId(String branchId) { - this.branchId = branchId; - } - - public void setFrictionType(String frictionType) { - this.roughnessType = frictionType; - } - - public void setLevels(double[] levels) { - this.levels = levels; - } - - public void setFunctionType(String functionType) { - this.functionType = functionType; - } - - public void setFlowDirection(String flowDirection) { - this.flowDirection = flowDirection; - } + public String getFunctionType() { + return functionType; } - //Used for collecting updated values of Definitions that will be written - @SuppressWarnings("WeakerAccess") - static class WriteDefinition { - - private String branchId; - private double chainage; - private List values = new ArrayList<>(); - - public WriteDefinition(String branchId, Double chainage) { - this.branchId = branchId; - this.chainage = chainage; - } - - public void addValue(double value) { - this.values.add(value); - } - - public String getBranchId() { - return branchId; - } + public String getBranchId() { + return branchId; + } - public double getChainage() { - return chainage; - } + public String getRoughnessType() { + return type; + } - public List getValues() { - return values; - } + public double getLevel() { + return level; } } 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 index 611fd7d61..5bda6a7b6 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -105,14 +105,34 @@ public void finish() { appendGeneral(builder); appendContent(builder); - Set writtenBranchProperties = new HashSet<>(); + String currentBranchId = null; + List levels = new ArrayList<>(); + for (IExchangeItem item : exchangeItems.values()) { DFlowFMSpatialRoughnessExchangeItem exchangeItem = (DFlowFMSpatialRoughnessExchangeItem) item; - DFlowFMSpatialRoughnessExchangeItem.InitialBranchDefinitions initialBranchDefinitions = exchangeItem.getInitialBranchDefinitions(); - if (initialBranchDefinitions == null) continue; - String branchId = initialBranchDefinitions.getBranchId(); - if (!writtenBranchProperties.add(branchId)) continue; - appendBranchProperties(builder, initialBranchDefinitions, branchId); + String branchId = exchangeItem.getBranchId(); + if (branchId != null && !branchId.equals(currentBranchId)) { + String functionType = exchangeItem.getFunctionType(); + if (functionType == null) continue; + builder.append(BRANCH + '\n'); + builder.append(BRANCH_ID + '=').append(branchId).append('\n'); + builder.append(ROUGHNESS_TYPE + '=').append(exchangeItem.getRoughnessType()).append('\n'); + builder.append(FUNCTION_TYPE + '=').append(functionType).append('\n'); + if (functionType.equals("constant")) { + builder.append('\n'); + continue; + } + builder.append(NUM_LEVELS + '=').append(levels.size()).append('\n'); + builder.append(LEVELS + '='); + for (double level : levels) { + builder.append(" ").append(level); + } + builder.append('\n'); + builder.append('\n'); + currentBranchId = branchId; + } else { + levels.add(exchangeItem.getLevel()); + } } lineWriter.write(builder.toString()); @@ -121,27 +141,6 @@ public void finish() { } } - private void appendBranchProperties(StringBuilder builder, DFlowFMSpatialRoughnessExchangeItem.InitialBranchDefinitions initialBranchDefinitions, String branchId) { - builder.append(BRANCH + '\n'); - builder.append(BRANCH_ID + '=').append(branchId).append('\n'); - builder.append(ROUGHNESS_TYPE + '=').append(initialBranchDefinitions.getRoughnessType()).append('\n'); - String functionType = initialBranchDefinitions.getFunctionType(); - builder.append(FUNCTION_TYPE + '=').append(functionType).append('\n'); - if (functionType.equals("constant")) { - builder.append('\n'); - return; - } - double[] levels = initialBranchDefinitions.getLevels(); - assert levels != null && levels.length > 0; - builder.append(NUM_LEVELS + '=').append(levels.length).append('\n'); - builder.append(LEVELS + '='); - for (double level : levels) { - builder.append(" ").append(level); - } - builder.append('\n'); - builder.append('\n'); - } - private void appendContent(StringBuilder builder) { builder.append(GLOBAL + '\n'); builder.append(FRICTION_ID + '=').append(frictionId).append('\n'); @@ -352,7 +351,7 @@ private void readBranch(BufferedReader lineReader, Map Date: Fri, 22 May 2026 13:53:24 +0200 Subject: [PATCH 05/15] Implement finish, first version without observation points --- .../DFlowFMSpatialRoughnessExchangeItem.java | 10 ++- .../DFlowFMSpatialRoughnessFile.java | 88 +++++++++++++------ 2 files changed, 67 insertions(+), 31 deletions(-) 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 index 468cdc2f9..1f2b44da3 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java @@ -16,16 +16,18 @@ public class DFlowFMSpatialRoughnessExchangeItem implements IExchangeItem { private final int chainageStartIndex; private final int chainageEndIndex; private final double level; + private final double[] chainages; - public DFlowFMSpatialRoughnessExchangeItem(String id, String type, String branchId, String functionType, double[] values, int chainageStartIndex, int chainageEndIndex, double level) { + public DFlowFMSpatialRoughnessExchangeItem(String id, String branchId, String frictionType, String functionType, double[] values, int chainageStartIndex, int chainageEndIndex, double level, double[] chainages) { this.id = id; - this.type = type; + this.type = frictionType; this.branchId = branchId; this.functionType = functionType; this.values = values; this.chainageStartIndex = chainageStartIndex; this.chainageEndIndex = chainageEndIndex; this.level = level; + this.chainages = chainages; } @Override @@ -138,4 +140,8 @@ public String getRoughnessType() { public double getLevel() { return level; } + + public double[] getChainages() { + return chainages; + } } 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 index 5bda6a7b6..2f7c0ceb8 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -25,10 +25,11 @@ public class DFlowFMSpatialRoughnessFile implements IDataObject { private static final String ROUGHNESS_TYPE = "roughnessType"; 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 VALUES = "values"; - private static final String VALUE = "value"; + 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"; @@ -38,7 +39,7 @@ public class DFlowFMSpatialRoughnessFile implements IDataObject { public static final String REVERSED_POSTFIX = " (Reversed)"; @SuppressWarnings("WeakerAccess") - LinkedHashMap exchangeItems = new LinkedHashMap<>(); + LinkedHashMap exchangeItems = new LinkedHashMap<>(); private String fileVersion; private String fileType; private String globalFrictionType; @@ -105,35 +106,30 @@ public void finish() { appendGeneral(builder); appendContent(builder); - String currentBranchId = null; List levels = new ArrayList<>(); + List frictionValues = new ArrayList<>(); - for (IExchangeItem item : exchangeItems.values()) { - DFlowFMSpatialRoughnessExchangeItem exchangeItem = (DFlowFMSpatialRoughnessExchangeItem) item; + List loopExchangeItems = new ArrayList<>(exchangeItems.values()); + DFlowFMSpatialRoughnessExchangeItem previousExchangeItem = loopExchangeItems.get(1); + String currentBranchId = previousExchangeItem.getBranchId(); + + for (int j = 1; j < loopExchangeItems.size(); j++) { + DFlowFMSpatialRoughnessExchangeItem exchangeItem = loopExchangeItems.get(j); String branchId = exchangeItem.getBranchId(); - if (branchId != null && !branchId.equals(currentBranchId)) { - String functionType = exchangeItem.getFunctionType(); - if (functionType == null) continue; - builder.append(BRANCH + '\n'); - builder.append(BRANCH_ID + '=').append(branchId).append('\n'); - builder.append(ROUGHNESS_TYPE + '=').append(exchangeItem.getRoughnessType()).append('\n'); - builder.append(FUNCTION_TYPE + '=').append(functionType).append('\n'); - if (functionType.equals("constant")) { - builder.append('\n'); - continue; - } - builder.append(NUM_LEVELS + '=').append(levels.size()).append('\n'); - builder.append(LEVELS + '='); - for (double level : levels) { - builder.append(" ").append(level); - } - builder.append('\n'); - builder.append('\n'); + if (!branchId.equals(currentBranchId)) { + appendText(previousExchangeItem, builder, currentBranchId, levels, frictionValues); currentBranchId = branchId; - } else { - levels.add(exchangeItem.getLevel()); + levels = new ArrayList<>(); + frictionValues = new ArrayList<>(); + previousExchangeItem = exchangeItem; + } + levels.add(exchangeItem.getLevel()); + double[] valuesAsDoubles = exchangeItem.getValuesAsDoubles(); + for (int i = 0; i < valuesAsDoubles.length; i++) { + frictionValues.add(valuesAsDoubles[i]); } } + appendText(previousExchangeItem, builder, currentBranchId, levels, frictionValues); lineWriter.write(builder.toString()); } catch (IOException e) { @@ -141,6 +137,40 @@ public void finish() { } } + private void appendText(DFlowFMSpatialRoughnessExchangeItem exchangeItem, StringBuilder builder, String branchId, List levels, List frictionValues) { + String functionType = exchangeItem.getFunctionType(); + if (functionType == null) { + return; + } + builder.append(BRANCH + '\n'); + builder.append(BRANCH_ID + '=').append(branchId).append('\n'); + builder.append(ROUGHNESS_TYPE + '=').append(exchangeItem.getRoughnessType()).append('\n'); + builder.append(FUNCTION_TYPE + '=').append(functionType).append('\n'); + if (!functionType.equalsIgnoreCase(CONSTANT)) { + builder.append(NUM_LEVELS + '=').append(levels.size()).append('\n'); + builder.append(LEVELS + '='); + for (double level : levels) { + 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 + '='); + for (int i = 0; i < levels.size(); i++) { + for (int j = 0; j < chainages.length; j++) { + builder.append(" ").append(frictionValues.get(i * chainages.length + j)); + } + builder.append('\n'); + } + builder.append('\n'); + } + private void appendContent(StringBuilder builder) { builder.append(GLOBAL + '\n'); builder.append(FRICTION_ID + '=').append(frictionId).append('\n'); @@ -351,7 +381,7 @@ private void readBranch(BufferedReader lineReader, Map Date: Tue, 26 May 2026 15:50:16 +0200 Subject: [PATCH 06/15] Implement finish, improve test --- .../DFlowFMSpatialRoughnessExchangeItem.java | 10 ++--- .../DFlowFMSpatialRoughnessFile.java | 3 +- .../DFlowFMSpatialRoughnessFileTest.java | 6 ++- .../expected_roughness-Main.ini | 37 +++++++++++++++++++ 4 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/expected_roughness-Main.ini 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 index 1f2b44da3..96cd74763 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java @@ -9,7 +9,7 @@ public class DFlowFMSpatialRoughnessExchangeItem implements IExchangeItem { private final String id; - private final String type; + private final String frictionType; private final String branchId; private final String functionType; private double[] values; @@ -20,7 +20,7 @@ public class DFlowFMSpatialRoughnessExchangeItem implements IExchangeItem { public DFlowFMSpatialRoughnessExchangeItem(String id, String branchId, String frictionType, String functionType, double[] values, int chainageStartIndex, int chainageEndIndex, double level, double[] chainages) { this.id = id; - this.type = frictionType; + this.frictionType = frictionType; this.branchId = branchId; this.functionType = functionType; this.values = values; @@ -52,7 +52,7 @@ public ITimeInfo getTimeInfo() { @Override public IQuantityInfo getQuantityInfo() { - return new QuantityInfo("Roughness-" + type, ""); + return new QuantityInfo("Roughness-" + frictionType, ""); } @Override @@ -133,8 +133,8 @@ public String getBranchId() { return branchId; } - public String getRoughnessType() { - return type; + public String getFrictionType() { + return frictionType; } public double getLevel() { 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 index 2f7c0ceb8..51250c790 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -22,7 +22,6 @@ public class DFlowFMSpatialRoughnessFile implements IDataObject { private static final String FRICTION_VALUE = "frictionValue"; private static final String BRANCH_ID = "branchId"; - private static final String ROUGHNESS_TYPE = "roughnessType"; private static final String FUNCTION_TYPE = "functionType"; private static final String NUM_LEVELS = "numLevels"; private static final String NUM_LOCATIONS = "numLocations"; @@ -144,7 +143,7 @@ private void appendText(DFlowFMSpatialRoughnessExchangeItem exchangeItem, String } builder.append(BRANCH + '\n'); builder.append(BRANCH_ID + '=').append(branchId).append('\n'); - builder.append(ROUGHNESS_TYPE + '=').append(exchangeItem.getRoughnessType()).append('\n'); + builder.append(FRICTION_TYPE + '=').append(exchangeItem.getFrictionType()).append('\n'); builder.append(FUNCTION_TYPE + '=').append(functionType).append('\n'); if (!functionType.equalsIgnoreCase(CONSTANT)) { builder.append(NUM_LEVELS + '=').append(levels.size()).append('\n'); 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 index b025b94ae..fb70e450f 100644 --- a/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java @@ -3,6 +3,7 @@ import junit.framework.TestCase; import org.openda.interfaces.IExchangeItem; import org.openda.utils.OpenDaTestSupport; +import org.openda.utils.io.AsciiFileUtils; import java.io.File; @@ -35,8 +36,9 @@ public void testReadAndWrite() { dFlowFMSpatialRoughnessFile.finish(); - File file = new File(testRunDataDir, "roughness-Main.ini"); - assertTrue(file.exists()); + 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 void checkEI(DFlowFMSpatialRoughnessFile dFlowFMSpatialRoughnessFile, String exchangeItemID, double[] expectedValues) { 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..e9d9b6518 --- /dev/null +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/testData/SpatialRoughness/expected_roughness-Main.ini @@ -0,0 +1,37 @@ +[General] +fileVersion=3.00 +fileType=roughness + +[Global] +frictionId=Main +frictionType=Chezy +frictionValue=45.0 + +[Branch] +branchId=Channel_1D_1_A +frictionType=Manning +functionType=absDischarge +numLevels=3 +levels= 100.0 500.0 1000.0 +numLocations=3 +chainage= 0.0 500.0 1000.0 +frictionValues= 0.03 0.029 0.029 + 0.03 0.025 0.026 + 0.025 0.025 0.023 + +[Branch] +branchId=Channel_1D_1_B +frictionType=Manning +functionType=Constant +numLocations=2 +chainage= 0.0 200.0 +frictionValues= 0.03 0.032 + +[Branch] +branchId=Channel_1D_1 +frictionType=Manning +functionType=Constant +numLocations=1 +chainage= 0.0 +frictionValues= 0.028 + From 2c269e4b6e4297a72ca2e910a5987fee3257fb80 Mon Sep 17 00:00:00 2001 From: erikpelgrim Date: Tue, 26 May 2026 16:41:44 +0200 Subject: [PATCH 07/15] Add splitting based on observation points --- .../DFlowFMSpatialRoughnessFile.java | 91 +++++++++++++------ .../DFlowFMSpatialRoughnessFileTest.java | 13 ++- .../SpatialRoughness/obsFile1D_obs.ini | 8 +- 3 files changed, 76 insertions(+), 36 deletions(-) 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 index 51250c790..daec8e695 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -342,7 +342,7 @@ private void readGeneral(BufferedReader lineReader) throws IOException { } } - private void readBranch(BufferedReader lineReader, Map> observationPoints) throws IOException { + private void readBranch(BufferedReader lineReader, Map> observationPointsMap) throws IOException { String line = lineReader.readLine(); String[] keyValue = readKeyValueLine(line); String branchId = keyValue[1].trim(); @@ -356,31 +356,7 @@ private void readBranch(BufferedReader lineReader, Map> observationPointsMap, String branchId, String frictionType, String functionType) 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); + } + + List observationPointsList = observationPointsMap == null ? null : observationPointsMap.get(branchId); + + if (observationPointsList == null) { + String id = getIdWithoutLevel(branchChainages[0], branchId, frictionType); + exchangeItems.put(id, new DFlowFMSpatialRoughnessExchangeItem(id, branchId, frictionType, functionType, frictionValues, 0, numLocations - 1, Double.NaN, branchChainages)); + return; + } + ArrayList chainageSplitIndices = new ArrayList<>(); + for (int i = 0; i < observationPointsList.size(); i++) { + ObservationPoint observationPoint = observationPointsList.get(i); + int chainageSplit = findChainageSplit(branchChainages, observationPoint.getChainage()); + chainageSplitIndices.add(chainageSplit); + } + 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, Double.NaN, branchChainages)); + 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 - 1, Double.NaN, branchChainages)); + } + + 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 + 1; + if (chainage > observationChainage) return i; + } + return size - 1; + } + private String getLevelString(String functionType, int levelIndex) { if (functionType.equals("absDischarge")) return "q" + levelIndex; if (functionType.equals("Constant")) return ""; 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 index fb70e450f..61da80710 100644 --- a/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java @@ -20,15 +20,22 @@ public void testReadAndWrite() { DFlowFMSpatialRoughnessFile dFlowFMSpatialRoughnessFile = new DFlowFMSpatialRoughnessFile(); dFlowFMSpatialRoughnessFile.initialize(testRunDataDir, new String[]{"roughness-Main.ini", "observationFile=obsFile1D_obs.ini"}); String[] exchangeItemIDs = dFlowFMSpatialRoughnessFile.getExchangeItemIDs(); - assertEquals(6, exchangeItemIDs.length); + assertEquals(7, exchangeItemIDs.length); - String[] expectedIds = {"Main-model_wide-Chezy", "Main-Manning-Channel_1D_1_A-x0-q0", "Main-Manning-Channel_1D_1_A-x0-q1", "Main-Manning-Channel_1D_1_A-x0-q2", "Main-Manning-Channel_1D_1_B-x0", "Main-Manning-Channel_1D_1-x0"}; + String[] expectedIds = {"Main-model_wide-Chezy", + "Main-Manning-Channel_1D_1_A-x0-q0", + "Main-Manning-Channel_1D_1_A-x0-q1", + "Main-Manning-Channel_1D_1_A-x0-q2", + "Main-Manning-Channel_1D_1_B-x0", + "Main-Manning-Channel_1D_1_B-x200", + "Main-Manning-Channel_1D_1-x0"}; for (int i = 0; i < 6; i++) { assertEquals(expectedIds[i], exchangeItemIDs[i]); } checkEI(dFlowFMSpatialRoughnessFile, "Main-model_wide-Chezy", new double[]{45.0}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_B-x0", new double[]{0.03, 0.032}); + checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_B-x0", new double[]{0.03}); + checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_B-x200", new double[]{0.032}); checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1-x0", new double[]{0.028}); checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q0", new double[]{0.03, 0.029, 0.029}); checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q1", new double[]{0.03, 0.025, 0.026}); 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 index 5986cf466..4f660deca 100644 --- 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 @@ -10,10 +10,4 @@ [ObservationPoint] name = Obs2 branchId = Channel_1D_1_B - chainage = 289.14553085136981 - -[ObservationPoint] - name = Obs3 - branchId = Channel_1D_1 - chainage = 369.2842857729857 - + chainage = 189.14553085136981 From f74adabc3eeee4ceef270e6f087c440541d6deb8 Mon Sep 17 00:00:00 2001 From: erikpelgrim Date: Tue, 26 May 2026 16:58:03 +0200 Subject: [PATCH 08/15] Add splitting based on observation points --- .../DFlowFMSpatialRoughnessFile.java | 18 ++++++++++++++---- .../DFlowFMSpatialRoughnessFileTest.java | 19 ++++++++++++------- .../expected_roughness-Main.ini | 4 ++-- 3 files changed, 28 insertions(+), 13 deletions(-) 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 index daec8e695..6a226824f 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -122,7 +122,8 @@ public void finish() { frictionValues = new ArrayList<>(); previousExchangeItem = exchangeItem; } - levels.add(exchangeItem.getLevel()); + double level = exchangeItem.getLevel(); + if (!Double.isNaN(level)) levels.add(level); double[] valuesAsDoubles = exchangeItem.getValuesAsDoubles(); for (int i = 0; i < valuesAsDoubles.length; i++) { frictionValues.add(valuesAsDoubles[i]); @@ -161,9 +162,18 @@ private void appendText(DFlowFMSpatialRoughnessExchangeItem exchangeItem, String } builder.append('\n'); builder.append(FRICTION_VALUES + '='); - for (int i = 0; i < levels.size(); i++) { - for (int j = 0; j < chainages.length; j++) { - builder.append(" ").append(frictionValues.get(i * chainages.length + j)); + if (levels.isEmpty()) { + for (int i = 0; i < chainages.length; i++) { + builder.append(" ").append(frictionValues.get(i)); + } + builder.append('\n'); + builder.append('\n'); + return; + } + for (int levelIndex = 0; levelIndex < levels.size(); levelIndex++) { + for (int chainageIndex = 0; chainageIndex < chainages.length; chainageIndex++) { + int index = levelIndex * chainages.length + chainageIndex; + builder.append(" ").append(frictionValues.get(index)); } builder.append('\n'); } 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 index 61da80710..43f741450 100644 --- a/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java @@ -33,10 +33,14 @@ public void testReadAndWrite() { for (int i = 0; i < 6; i++) { assertEquals(expectedIds[i], exchangeItemIDs[i]); } - checkEI(dFlowFMSpatialRoughnessFile, "Main-model_wide-Chezy", new double[]{45.0}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_B-x0", new double[]{0.03}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_B-x200", new double[]{0.032}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1-x0", new double[]{0.028}); + IExchangeItem chezy = checkEI(dFlowFMSpatialRoughnessFile, "Main-model_wide-Chezy", new double[]{45.0}); + chezy.setValuesAsDoubles(new double[]{50.0}); + IExchangeItem b0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_B-x0", new double[]{0.03}); + b0.setValuesAsDoubles(new double[]{0.033}); + IExchangeItem b200 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_B-x200", new double[]{0.032}); + b200.setValuesAsDoubles(new double[]{0.035}); + IExchangeItem x0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1-x0", new double[]{0.028}); + x0.setValuesAsDoubles(new double[]{0.031}); checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q0", new double[]{0.03, 0.029, 0.029}); checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q1", new double[]{0.03, 0.025, 0.026}); checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q2", new double[]{0.025, 0.025, 0.023}); @@ -48,12 +52,13 @@ public void testReadAndWrite() { assertEquals(AsciiFileUtils.readText(new File(testRunDataDir, "expected_roughness-Main.ini")), AsciiFileUtils.readText(resultFile)); } - private void checkEI(DFlowFMSpatialRoughnessFile dFlowFMSpatialRoughnessFile, String exchangeItemID, double[] expectedValues) { - IExchangeItem modelWideEI = dFlowFMSpatialRoughnessFile.getDataObjectExchangeItem(exchangeItemID); - double[] modelWideValues = modelWideEI.getValuesAsDoubles(); + 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; } } 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 index e9d9b6518..7cbd4b3b1 100644 --- 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 @@ -25,7 +25,7 @@ frictionType=Manning functionType=Constant numLocations=2 chainage= 0.0 200.0 -frictionValues= 0.03 0.032 +frictionValues= 0.033 0.035 [Branch] branchId=Channel_1D_1 @@ -33,5 +33,5 @@ frictionType=Manning functionType=Constant numLocations=1 chainage= 0.0 -frictionValues= 0.028 +frictionValues= 0.031 From 0ba4fe18f7966562278068f1c592919acad96034 Mon Sep 17 00:00:00 2001 From: erikpelgrim Date: Wed, 27 May 2026 15:18:12 +0200 Subject: [PATCH 09/15] Add splitting based on observation points, split before observation point --- .../org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java | 4 ++-- .../model_dflowfm/testData/SpatialRoughness/obsFile1D_obs.ini | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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 index 6a226824f..a47610cfb 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -474,8 +474,8 @@ private int findChainageSplit(double[] branchChainages, double observationChaina int size = branchChainages.length; for (int i = 0; i < size; i++) { double chainage = branchChainages[i]; - if (chainage == observationChainage) return i + 1; - if (chainage > observationChainage) return i; + if (chainage == observationChainage) return i; + if (chainage > observationChainage) return i - 1; } return size - 1; } 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 index 4f660deca..6ddf0e8c4 100644 --- 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 @@ -5,9 +5,9 @@ [ObservationPoint] name = Obs1 branchId = Channel_1D_1_A - chainage = 379.50351792411624 + chainage = 679.50351792411624 [ObservationPoint] name = Obs2 branchId = Channel_1D_1_B - chainage = 189.14553085136981 + chainage = 289.14553085136981 From fcb159375ebaf685a454c74be961ee0a23a6f837 Mon Sep 17 00:00:00 2001 From: erikpelgrim Date: Wed, 27 May 2026 16:53:38 +0200 Subject: [PATCH 10/15] Add splitting based on observation points --- .../DFlowFMSpatialRoughnessExchangeItem.java | 20 ++-- .../DFlowFMSpatialRoughnessFile.java | 112 ++++++++++++------ .../DFlowFMSpatialRoughnessFileTest.java | 16 ++- 3 files changed, 98 insertions(+), 50 deletions(-) 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 index 96cd74763..61a9c65f5 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessExchangeItem.java @@ -12,13 +12,14 @@ public class DFlowFMSpatialRoughnessExchangeItem implements IExchangeItem { private final String frictionType; private final String branchId; private final String functionType; - private double[] values; + private final double[] chainages; + private final int levelIndex; private final int chainageStartIndex; private final int chainageEndIndex; - private final double level; - private final double[] chainages; + private final double[] levels; + private double[] values; - public DFlowFMSpatialRoughnessExchangeItem(String id, String branchId, String frictionType, String functionType, double[] values, int chainageStartIndex, int chainageEndIndex, double level, double[] chainages) { + 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; @@ -26,8 +27,9 @@ public DFlowFMSpatialRoughnessExchangeItem(String id, String branchId, String fr this.values = values; this.chainageStartIndex = chainageStartIndex; this.chainageEndIndex = chainageEndIndex; - this.level = level; + this.levels = levels; this.chainages = chainages; + this.levelIndex = levelIndex; } @Override @@ -137,11 +139,15 @@ public String getFrictionType() { return frictionType; } - public double getLevel() { - return level; + 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 index a47610cfb..e518e84c3 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -105,31 +105,34 @@ public void finish() { appendGeneral(builder); appendContent(builder); - List levels = new ArrayList<>(); - List frictionValues = new ArrayList<>(); - List loopExchangeItems = new ArrayList<>(exchangeItems.values()); - DFlowFMSpatialRoughnessExchangeItem previousExchangeItem = loopExchangeItems.get(1); + DFlowFMSpatialRoughnessExchangeItem previousExchangeItem = loopExchangeItems.get(0); String currentBranchId = previousExchangeItem.getBranchId(); + double[][] frictionValues = new double[1][1]; - for (int j = 1; j < loopExchangeItems.size(); j++) { + 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)) { - appendText(previousExchangeItem, builder, currentBranchId, levels, frictionValues); + if (j != 0) appendText(previousExchangeItem, builder, currentBranchId, frictionValues); currentBranchId = branchId; - levels = new ArrayList<>(); - frictionValues = new ArrayList<>(); + frictionValues = new double[exchangeItemLevels == null ? 1 : exchangeItemLevels.length][exchangeItem.getChainages().length]; previousExchangeItem = exchangeItem; } - double level = exchangeItem.getLevel(); - if (!Double.isNaN(level)) levels.add(level); + int levelIndex = exchangeItem.getLevelIndex(); double[] valuesAsDoubles = exchangeItem.getValuesAsDoubles(); - for (int i = 0; i < valuesAsDoubles.length; i++) { - frictionValues.add(valuesAsDoubles[i]); + 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, levels, frictionValues); + appendText(previousExchangeItem, builder, currentBranchId, frictionValues); lineWriter.write(builder.toString()); } catch (IOException e) { @@ -137,7 +140,7 @@ public void finish() { } } - private void appendText(DFlowFMSpatialRoughnessExchangeItem exchangeItem, StringBuilder builder, String branchId, List levels, List frictionValues) { + private void appendText(DFlowFMSpatialRoughnessExchangeItem exchangeItem, StringBuilder builder, String branchId, double[][] frictionValues) { String functionType = exchangeItem.getFunctionType(); if (functionType == null) { return; @@ -146,10 +149,11 @@ private void appendText(DFlowFMSpatialRoughnessExchangeItem exchangeItem, String 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(levels.size()).append('\n'); + builder.append(NUM_LEVELS + '=').append(exchangeItemLevels.length).append('\n'); builder.append(LEVELS + '='); - for (double level : levels) { + for (double level : exchangeItemLevels) { builder.append(" ").append(level); } builder.append('\n'); @@ -162,18 +166,17 @@ private void appendText(DFlowFMSpatialRoughnessExchangeItem exchangeItem, String } builder.append('\n'); builder.append(FRICTION_VALUES + '='); - if (levels.isEmpty()) { + if (exchangeItemLevels == null) { for (int i = 0; i < chainages.length; i++) { - builder.append(" ").append(frictionValues.get(i)); + builder.append(" ").append(frictionValues[0][i]); } builder.append('\n'); builder.append('\n'); return; } - for (int levelIndex = 0; levelIndex < levels.size(); levelIndex++) { + for (int levelIndex = 0; levelIndex < exchangeItemLevels.length; levelIndex++) { for (int chainageIndex = 0; chainageIndex < chainages.length; chainageIndex++) { - int index = levelIndex * chainages.length + chainageIndex; - builder.append(" ").append(frictionValues.get(index)); + builder.append(" ").append(frictionValues[levelIndex][chainageIndex]); } builder.append('\n'); } @@ -365,8 +368,10 @@ private void readBranch(BufferedReader lineReader, Map observationPointsList = observationPointsMap == null ? null : observationPointsMap.get(branchId); + if (functionType.equalsIgnoreCase("constant")) { - createConstantExchangeItems(lineReader, observationPointsMap, branchId, frictionType, functionType); + createConstantExchangeItems(lineReader, branchId, frictionType, functionType, observationPointsList); return; } @@ -411,13 +416,37 @@ private void readBranch(BufferedReader lineReader, Map 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, Map> observationPointsMap, String branchId, String frictionType, String functionType) throws IOException { + private void createConstantExchangeItems(BufferedReader lineReader, String branchId, String frictionType, String functionType, List observationPointsList) throws IOException { String[] keyValue; String line; line = lineReader.readLine(); @@ -443,31 +472,38 @@ private void createConstantExchangeItems(BufferedReader lineReader, Map observationPointsList = observationPointsMap == null ? null : observationPointsMap.get(branchId); - if (observationPointsList == null) { String id = getIdWithoutLevel(branchChainages[0], branchId, frictionType); - exchangeItems.put(id, new DFlowFMSpatialRoughnessExchangeItem(id, branchId, frictionType, functionType, frictionValues, 0, numLocations - 1, Double.NaN, branchChainages)); + exchangeItems.put(id, new DFlowFMSpatialRoughnessExchangeItem(id, branchId, frictionType, functionType, frictionValues, 0, numLocations, null, branchChainages, 0)); return; } - ArrayList chainageSplitIndices = new ArrayList<>(); - for (int i = 0; i < observationPointsList.size(); i++) { - ObservationPoint observationPoint = observationPointsList.get(i); - int chainageSplit = findChainageSplit(branchChainages, observationPoint.getChainage()); - chainageSplitIndices.add(chainageSplit); - } + 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, Double.NaN, branchChainages)); + 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 - 1, Double.NaN, branchChainages)); + 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()); + chainageSplitIndices.add(chainageSplit); + } + return chainageSplitIndices; } private int findChainageSplit(double[] branchChainages, double observationChainage) { @@ -531,7 +567,7 @@ private DFlowFMSpatialRoughnessExchangeItem readContent(BufferedReader lineReade line = lineReader.readLine(); } - return new DFlowFMSpatialRoughnessExchangeItem(mainExchangeItemId, frictionId, globalFrictionType, null, new double[]{globalFrictionValue}, -1, -1, Double.NaN, null); + return new DFlowFMSpatialRoughnessExchangeItem(mainExchangeItemId, frictionId, globalFrictionType, null, new double[]{globalFrictionValue}, -1, -1, null, null, 0); } private String[] readKeyValueLine(String line) throws IOException { 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 index 43f741450..cf21729b0 100644 --- a/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java @@ -20,17 +20,20 @@ public void testReadAndWrite() { DFlowFMSpatialRoughnessFile dFlowFMSpatialRoughnessFile = new DFlowFMSpatialRoughnessFile(); dFlowFMSpatialRoughnessFile.initialize(testRunDataDir, new String[]{"roughness-Main.ini", "observationFile=obsFile1D_obs.ini"}); String[] exchangeItemIDs = dFlowFMSpatialRoughnessFile.getExchangeItemIDs(); - assertEquals(7, exchangeItemIDs.length); + assertEquals(10, exchangeItemIDs.length); String[] expectedIds = {"Main-model_wide-Chezy", "Main-Manning-Channel_1D_1_A-x0-q0", "Main-Manning-Channel_1D_1_A-x0-q1", "Main-Manning-Channel_1D_1_A-x0-q2", + "Main-Manning-Channel_1D_1_A-x500-q0", + "Main-Manning-Channel_1D_1_A-x500-q1", + "Main-Manning-Channel_1D_1_A-x500-q2", "Main-Manning-Channel_1D_1_B-x0", "Main-Manning-Channel_1D_1_B-x200", "Main-Manning-Channel_1D_1-x0"}; - for (int i = 0; i < 6; i++) { + 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}); @@ -41,9 +44,12 @@ public void testReadAndWrite() { b200.setValuesAsDoubles(new double[]{0.035}); IExchangeItem x0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1-x0", new double[]{0.028}); x0.setValuesAsDoubles(new double[]{0.031}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q0", new double[]{0.03, 0.029, 0.029}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q1", new double[]{0.03, 0.025, 0.026}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q2", new double[]{0.025, 0.025, 0.023}); + checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q0", new double[]{0.03}); + checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q1", new double[]{0.03}); + checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q2", new double[]{0.025}); + checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x500-q0", new double[]{0.029, 0.029}); + checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x500-q1", new double[]{0.025, 0.026}); + checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x500-q2", new double[]{0.025, 0.023}); dFlowFMSpatialRoughnessFile.finish(); From 0dcb93da582544c8c24e9259f9f646ec87fb5616 Mon Sep 17 00:00:00 2001 From: erikpelgrim Date: Thu, 28 May 2026 17:08:27 +0200 Subject: [PATCH 11/15] Add splitting based on observation points, extend unit test --- .../DFlowFMSpatialRoughnessFileTest.java | 18 ++++++++++++------ .../expected_roughness-Main.ini | 6 +++--- 2 files changed, 15 insertions(+), 9 deletions(-) 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 index cf21729b0..12a066457 100644 --- a/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java +++ b/model_dflowfm_blackbox/java/test/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFileTest.java @@ -44,12 +44,18 @@ public void testReadAndWrite() { b200.setValuesAsDoubles(new double[]{0.035}); IExchangeItem x0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1-x0", new double[]{0.028}); x0.setValuesAsDoubles(new double[]{0.031}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q0", new double[]{0.03}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q1", new double[]{0.03}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q2", new double[]{0.025}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x500-q0", new double[]{0.029, 0.029}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x500-q1", new double[]{0.025, 0.026}); - checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x500-q2", new double[]{0.025, 0.023}); + IExchangeItem x0q0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q0", new double[]{0.03}); + x0q0.setValuesAsDoubles(new double[]{0.0314}); + IExchangeItem x0q1 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q1", new double[]{0.03}); + x0q1.setValuesAsDoubles(new double[]{0.0345}); + IExchangeItem x0q2 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x0-q2", new double[]{0.025}); + x0q2.setValuesAsDoubles(new double[]{0.0579}); + IExchangeItem x500q0 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x500-q0", new double[]{0.029, 0.029}); + x500q0.setValuesAsDoubles(new double[]{0.02978, 0.02963}); + IExchangeItem x500q1 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x500-q1", new double[]{0.025, 0.026}); + x500q1.setValuesAsDoubles(new double[]{0.02576, 0.02634}); + IExchangeItem x500q2 = checkEI(dFlowFMSpatialRoughnessFile, "Main-Manning-Channel_1D_1_A-x500-q2", new double[]{0.025, 0.023}); + x500q2.setValuesAsDoubles(new double[]{0.02513, 0.02346}); dFlowFMSpatialRoughnessFile.finish(); 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 index 7cbd4b3b1..974cab281 100644 --- 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 @@ -15,9 +15,9 @@ numLevels=3 levels= 100.0 500.0 1000.0 numLocations=3 chainage= 0.0 500.0 1000.0 -frictionValues= 0.03 0.029 0.029 - 0.03 0.025 0.026 - 0.025 0.025 0.023 +frictionValues= 0.0314 0.02978 0.02963 + 0.0345 0.02576 0.02634 + 0.0579 0.02513 0.02346 [Branch] branchId=Channel_1D_1_B From cd0ad0f89a0f2e2abbd8402c4765dce3d736d7a2 Mon Sep 17 00:00:00 2001 From: erikpelgrim Date: Thu, 28 May 2026 17:45:16 +0200 Subject: [PATCH 12/15] Add splitting based on observation points, switch levels and locations in value order --- .../openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java | 5 +++-- .../model_dflowfm/DFlowFMSpatialRoughnessFileTest.java | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) 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 index e518e84c3..273b4d883 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -401,7 +401,7 @@ private void readBranch(BufferedReader lineReader, Map Date: Fri, 29 May 2026 11:29:43 +0200 Subject: [PATCH 13/15] Add splitting based on observation points, switch levels and locations in value order, improve test --- .../DFlowFMSpatialRoughnessFile.java | 4 ++-- .../DFlowFMSpatialRoughnessFileTest.java | 24 ++++++++++++++----- .../expected_roughness-Main.ini | 6 +++-- .../SpatialRoughness/roughness-Main.ini | 6 +++-- 4 files changed, 28 insertions(+), 12 deletions(-) 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 index 273b4d883..8b083096a 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -401,7 +401,7 @@ private void readBranch(BufferedReader lineReader, Map Date: Fri, 29 May 2026 14:03:03 +0200 Subject: [PATCH 14/15] Add water level dependent unit test case --- .../DFlowFMSpatialRoughnessFile.java | 4 +- .../DFlowFMSpatialRoughnessFileTest.java | 66 +++++++++++-------- .../expected_roughness-Main.ini | 44 +++++++++---- .../SpatialRoughness/obsFile1D_obs.ini | 4 +- .../SpatialRoughness/roughness-Main.ini | 54 +++++++++------ 5 files changed, 105 insertions(+), 67 deletions(-) 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 index 8b083096a..ae46dc394 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -406,7 +406,7 @@ private void readBranch(BufferedReader lineReader, Map Date: Fri, 29 May 2026 14:36:40 +0200 Subject: [PATCH 15/15] Add water level dependent unit test case, skip unnecessary and sort observation points --- .../org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java | 3 +++ 1 file changed, 3 insertions(+) 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 index ae46dc394..214c7ca55 100644 --- a/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java +++ b/model_dflowfm_blackbox/java/src/org/openda/model_dflowfm/DFlowFMSpatialRoughnessFile.java @@ -370,6 +370,8 @@ private void readBranch(BufferedReader lineReader, Map observationPointsList = observationPointsMap == null ? null : observationPointsMap.get(branchId); + if (observationPointsList != null) Collections.sort(observationPointsList); + if (functionType.equalsIgnoreCase("constant")) { createConstantExchangeItems(lineReader, branchId, frictionType, functionType, observationPointsList); return; @@ -501,6 +503,7 @@ private ArrayList getChainageSplitIndices(List observ 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;