a8d3f6cf by Nathan Lighthart

Add reading of temporal parameters

- Add temporal parameter reader
- Add model parameter for the temporal parameter file
- Update model execution to read from the temporal reader before executing subsurface
- Add example temporal file for arti_catch
1 parent e888c146
......@@ -44,6 +44,7 @@ import io.SoilsParameterReader;
import io.SpatialParameterReader;
import io.StationClimateReader;
import io.StationReader;
import io.TemporalParameterReader;
import io.TmeanStationClimateReader;
import irrigation.ProcessIrrigationAdapter;
import java.io.IOException;
......@@ -846,6 +847,11 @@ public class AgESModel {
outputExecutors.add(reachOutputExecutor);
}
TemporalParameterReader temporalReader = null;
if (parameters.temporalAdditionalFilePath != null) {
temporalReader = new TemporalParameterReader(parameters.temporalAdditionalFilePath, temporalContext);
}
for (time = parameters.startTime; !time.isAfter(parameters.endTime); time = time.plusDays(1L)) {
if (time.getDayOfMonth() == 1) {
long now = System.currentTimeMillis();
......@@ -855,6 +861,10 @@ public class AgESModel {
temporalContext.put("time", time);
readerCO2.setValue(temporalContext);
if (temporalReader != null) {
temporalReader.read();
}
updateClimateReaders();
updateManagementSchedules();
......
......@@ -88,6 +88,9 @@ public class AgESParameters {
@Description("Reach additional parameters file path")
public final Path reachAdditionalFilePath;
@Description("Temporal additional parameters file path")
public final Path temporalAdditionalFilePath;
@Description("ID set for HRU")
public final String idSet_hru;
......@@ -682,6 +685,7 @@ public class AgESParameters {
hruAdditionalFilePath = builder.hruAdditionalFilePath;
reachAdditionalFilePath = builder.reachAdditionalFilePath;
temporalAdditionalFilePath = builder.temporalAdditionalFilePath;
idSet_hru = builder.idSet_hru;
idSet_reach = builder.idSet_reach;
......@@ -908,6 +912,7 @@ public class AgESParameters {
private Path hruAdditionalFilePath;
private Path reachAdditionalFilePath;
private Path temporalAdditionalFilePath;
private String idSet_hru;
private String idSet_reach;
......@@ -1282,6 +1287,11 @@ public class AgESParameters {
return this;
}
public Builder temporalAdditionalFilePath(Path temporalAdditionalFilePath) {
this.temporalAdditionalFilePath = temporalAdditionalFilePath;
return this;
}
public Builder idSet_hru(String idSet_hru) {
this.idSet_hru = idSet_hru;
return this;
......
......@@ -164,7 +164,14 @@ public class ParameterTableReader {
private class ParameterInformation {
private final String name;
private String units;
// represents type field as specified by the file
// if multiple columns this is the component type for array
// otherwise this the actual type
private Class<?> type;
// represents the actual data type returned by a read
// if multiple columns this is an array of variables of "type"
// otherwise this is identical to "type"
private Class<?> fullType;
private final List<Integer> columnIndices;
......@@ -186,7 +193,7 @@ public class ParameterTableReader {
}
public Class<?> getType() {
return type;
return fullType;
}
private void merge(ParameterInformation info) {
......@@ -206,10 +213,12 @@ public class ParameterTableReader {
}
if (columnIndices.size() == 1) {
fullType = type;
return convert(row[columnIndices.get(0)]);
} else {
final int length = columnIndices.size();
Object array = Array.newInstance(type, length);
fullType = array.getClass();
for (int i = 0; i < length; ++i) {
Object value = convert(row[columnIndices.get(i)]);
Array.set(array, i, value);
......
package io;
import gov.usda.jcf.core.Context;
import gov.usda.jcf.util.conversion.TypeConverters;
import java.io.IOException;
import java.nio.file.Path;
import java.time.LocalDate;
import java.util.Map;
public class TemporalParameterReader {
private Path temporalParameterFilePath;
private Context temporalContext;
private ParameterTableReader reader;
private int rowCount;
public TemporalParameterReader(Path temporalParameterFilePath, Context temporalContext) {
this.temporalParameterFilePath = temporalParameterFilePath;
this.temporalContext = temporalContext;
reader = null;
}
public void read() throws IOException {
try {
read0();
} catch (Exception ex) {
throw new IOException("Error reading temporal parameter file: " + temporalParameterFilePath, ex);
}
}
private void read0() throws Exception {
if (reader == null) {
readFirstDay();
} else {
readNextDay();
}
}
private void readFirstDay() throws Exception {
reader = new ParameterTableReader(temporalParameterFilePath);
if (!"date".equals(reader.getTable().getColumnName(1))) {
throw new RuntimeException("Invalid additional file: date needs to be the first column");
}
LocalDate currentModelTime = getCurrentModelTime();
LocalDate dataFileTime = null;
rowCount = 1;
Map<String, Object> data;
do {
data = reader.readNextLine();
// retrieve actual time form data file
LocalDate actualTime = getActualTime(data);
if (dataFileTime == null) {
// no previous time so it is the first
// verify that actual time is before or equal to current model time
if (actualTime.isAfter(currentModelTime)) {
throw new RuntimeException("Start date in file is after the model start date: "
+ actualTime + " model time: " + currentModelTime);
}
dataFileTime = actualTime;
} else {
// previous time is known so verify that day is not skipped
LocalDate expectedTime = dataFileTime.plusDays(1L);
if (actualTime.equals(expectedTime)) {
// time in the data file is the next day as expected
dataFileTime = expectedTime;
} else {
// time is not what is expected so malformed file
throw new RuntimeException(getUnexpectedTimeString(expectedTime, actualTime));
}
}
++rowCount;
} while (!currentModelTime.equals(dataFileTime));
// current model time found
// so add to context
readIntoContext(data);
}
private void readNextDay() throws Exception {
LocalDate currentModelTime = getCurrentModelTime();
Map<String, Object> data = reader.readNextLine();
LocalDate actualTime = getActualTime(data);
if (currentModelTime.equals(actualTime)) {
// time is as expected so add data to context
readIntoContext(data);
} else {
// time is not what is expected so malformed file
throw new RuntimeException(getUnexpectedTimeString(currentModelTime, actualTime));
}
++rowCount;
}
private LocalDate getCurrentModelTime() {
return temporalContext.get("time", LocalDate.class);
}
private LocalDate getActualTime(Map<String, Object> data) {
Object timeObject = data.get("date");
if (timeObject == null) {
throw new RuntimeException("Missing time on data row " + rowCount);
}
return TypeConverters.INSTANCE.convert(timeObject, LocalDate.class);
}
private void readIntoContext(Map<String, Object> data) {
// Add the parameter values and metadata to the context
for (Map.Entry<String, Object> entry : data.entrySet()) {
String parameterName = entry.getKey();
// ID should be skipped as it is not a parameter
if ("date".equals(parameterName)) {
continue;
}
// Add the parameter to the context
temporalContext.put(parameterName, entry.getValue(), reader.getType(parameterName));
// Add the units to the context if it exists
String units = reader.getUnits(parameterName);
if (units != null) {
temporalContext.putProperty(parameterName, "units", units);
}
}
}
private String getUnexpectedTimeString(LocalDate expectedTime, LocalDate actualTime) {
return "Unexpected time occured on data row "
+ rowCount + ": " + actualTime + " Expected: " + expectedTime;
}
}
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!