DHI.Services.MIKE1D — Internal Guide¶
Provider for MIKE 1D result files (
.res1d,.res11,.crf,.prf) that exposes data asDHI.Services.TimeSeriesobjects. Covers: repository types, grouping by file, ID grammar (Node/Reach/Catchment/Global + chainage), reading APIs, batch helpers, errors, performance, and Web API wiring.
What this provider gives you¶
| Scope | Class | Base type | Read | Write | Grouped | Notes |
|---|---|---|---|---|---|---|
| One result file | ResultFileTimeSeriesRepository |
BaseDiscreteTimeSeriesRepository<string,double> |
✓ | ✗ | — | Loads one result file and exposes all Node/Reach/Catchment series inside. |
| Many result files under a root | ResultFileGroupedTimeSeriesRepository |
BaseGroupedDiscreteTimeSeriesRepository<string,double> |
✓ | ✗ | ✓ | Treats each result file as a group; lazily caches a single-file repo per discovered file. |
Supported extensions: .res1d, .res11, .crf, .prf
Write operations: not allowed (simulation outputs). SetValues throws in the single-file repo; grouped repo forwards and will throw too.
Quick start (grouped — recommended)¶
using DHI.Services.Provider.MIKE1D;
using DHI.Services.TimeSeries;
var root = @"C:\Projects\Runs\2025-01";
var repo = new ResultFileGroupedTimeSeriesRepository(root);
// enumerate everything
foreach (var ts in repo.GetAll())
{
Console.WriteLine($"{ts.Group} :: {ts.Name}");
}
// pick an ID and read values
var id = "ScenarioA/Run01/Network.res1d/Node;WaterLevel;A01";
var values = repo.GetValues(id).Value;
Console.WriteLine($"{values.Count} points, {values.DateTimes[0]} … {values.DateTimes[^1]}");
Concepts¶
Groups (how files map to groups)¶
- In the grouped repo, each result file is a group.
- The group string is the relative path from the root, including filename + extension, e.g.
ScenarioA/Run01/Network.res1d. - Files are discovered recursively under the root; supported extensions only.
Common time axis¶
- All series in a result file share the same axis:
ResultData.TimesList. GetDateTimes(id)returns that axis asSortedSet<DateTime>.
Time series IDs (grammar)¶
A MIKE1D ID is <group>/<localName>, where <localName> uses ;:
<group>/<type>;<quantity>;<id>[;chainage]
<group>: relative path + filename (with extension), e.g.ScenarioA/Run01/Network.res1d<type>:Node|Reach|Catchment|Global(values forGlobalnot implemented)<quantity>:PredefinedQuantityname from MIKE 1D (Mike1D.Generic), spaces ignored, e.g.WaterLevel,Discharge,Volume<id>: Node/Catchment Id or Reach Namechainage(Reach only):- numeric (e.g.
0,123.45) → exact chainage - keywords:
start/from,end/to - special:
sum→ sum the quantity across all grid points (see below)
- numeric (e.g.
Examples¶
ScenarioA/Run01/Network.res1d/Node;WaterLevel;A01
ScenarioA/Run01/Network.res1d/Reach;Discharge;River1;0
ScenarioA/Run01/Network.res1d/Reach;Discharge;River1;end
ScenarioA/Run01/Network.res1d/Reach;Volume;River1;sum
ScenarioA/Run01/Network.res1d/Catchment;Runoff;C01
Parser: TimeSeriesQuery.Parse(id)
- Validates shape and type; throws on invalid format or missing reach chainage.
- Quantities are parsed via
PredefinedQuantity(spaces removed); unknown quantities throw.
Reading APIs¶
Grouped repo (folder of files)¶
var repo = new ResultFileGroupedTimeSeriesRepository(root);
// series enumeration
IEnumerable<TimeSeries<string,double>> all = repo.GetAll();
IEnumerable<TimeSeries<string,double>> inFile = repo.GetByGroup("ScenarioA/Run01/Network.res1d");
// values
var maybe = repo.GetValues("…/Reach;Discharge;River1;from"); // Maybe<ITimeSeriesData<double>>
// convenience
IEnumerable<string> fullIds = repo.GetFullNames("ScenarioA/Run01/Network.res1d"); // all IDs in that file
bool exists = repo.ContainsGroup("ScenarioA/Run01/Network.res1d");
Single-file repo (one file)¶
var fileRepo = new ResultFileTimeSeriesRepository(@"C:\...\Network.res1d");
// enumerate without data
foreach (var ts in fileRepo.GetAll())
Console.WriteLine(ts.Name);
// with data attached (Maybe<TimeSeries<...>>)
foreach (var ts in fileRepo.GetAllWithValues())
{
var series = ts.Value;
}
// one series with values
var node = fileRepo.GetWithValues("Node;WaterLevel;A01").Value;
// all chainages for a reach
foreach (var data in fileRepo.GetValuesForAllChainages("Reach;Discharge;River1"))
{
var oneChainage = data.Value;
}
What’s supported per type¶
| Type | ID shape | Behavior |
|---|---|---|
| Node | …/Node;Quantity;NodeId |
Returns the item’s full time series at that node. |
| Reach | …/Reach;Quantity;ReachName;{chainage} |
chainage: numeric, start/from, end/to, or sum (sum across all grid points). |
| Catchment | …/Catchment;Quantity;CatchmentId |
Returns the item’s series for the catchment. |
| Global | …/Global;Quantity;id? |
Values not implemented; parsing exists but GetValues throws NotImplementedException. |
Internals (reach): chainage → grid point index via reach.GridPointIndexForChainage(...). If not found, throws.
Batch math helpers (single-file repo)¶
-
Sum time series data (same sampling length required):
var s1 = fileRepo.GetWithValues("Node;Discharge;N1"); var s2 = fileRepo.GetWithValues("Node;Discharge;N2"); var sum12 = ResultFileTimeSeriesRepository.Sum(new[] { s1, s2 }); // Maybe<ITimeSeriesData<double>> -
Sum grid points in a reach:
- Use the special ID
Reach;Quantity;ReachName;sum(handled byTimeSeriesQueryReachSumGridPoints), which element-wise sums all grid point vectors.
- Use the special ID
Metadata on returned series¶
When a TimeSeries<string,double> is created (TimeSeriesQuery.*.CreateTimeSeries):
Quantity←Quantity.IdUnit←Quantity.EumQuantity.UnitAbbreviationDataType← fromQuantity.TimeValueType(Enumeration.FromValue<TimeSeriesDataType>(...))Group← result file group (relative path + filename)
Values are returned as double? (converted from float) to preserve gaps as null.
Error handling¶
- Write attempts:
SetValues→NotImplementedException("Overwriting values in a result file is not allowed.") - Invalid ID / type:
ArgumentException("Invalid time series id: …")orException("Unrecognized type: …") - Reach chainage:
- Missing:
Exception("Please add reach chainage …") - Not found:
Exception("Could not find index for chainage: …")
- Missing:
- Quantity mismatch:
Exception("No data for chosen quantity …") - File loading:
- Unsupported extension:
ArgumentException("Unsupported file type: …") - Diagnostics errors after load:
Exception("File could not be loaded: …")
- Unsupported extension:
Performance notes¶
- Grouped repo lazily creates and caches one single-file repo per group on first touch.
- Single-file repo loads the MIKE 1D result once (
ResultData.Load) and slices vectors per query. GetValuesForAllChainagesstreams through grid points; if you only need specific locations, query those explicitly.
Connections (Web API)¶
Expose as a grouped connection:
{
"$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[DHI.Services.IConnection, DHI.Services]], mscorlib",
"mike1d-results": {
"$type": "DHI.Services.TimeSeries.WebApi.GroupedDiscreteTimeSeriesServiceConnection, DHI.Services.TimeSeries.WebApi",
"ConnectionString": "C:\\Projects\\Runs\\2025-01",
"RepositoryType": "DHI.Services.Provider.MIKE1D.ResultFileGroupedTimeSeriesRepository, DHI.Services.Provider.MIKE1D",
"Name": "MIKE 1D result files",
"Id": "mike1d-results"
}
}
HTTP usage (URL-encode the ID after /mike1d-results/):
GET /api/timeseries/mike1d-results/ScenarioA%2FRun01%2FNetwork.res1d%2FNode%3BWaterLevel%3BA01/values
GET /api/timeseries/mike1d-results/ScenarioA%2FRun01%2FNetwork.res1d%2FReach%3BDischarge%3BRiver1%3Bend/values
Troubleshooting¶
- “File could not be loaded …”: diagnostics reported errors; check the result file integrity.
- “No data for chosen quantity …”: quantity not present for that element.
- “Invalid time series id …”: check the grammar and semicolons; ensure group + local name.
- Summation errors: summed series must have identical sample counts.