Skip to content

DHI.Services.MIKE1D — Internal Guide

Provider for MIKE 1D result files (.res1d, .res11, .crf, .prf) that exposes data as DHI.Services.TimeSeries objects. 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.


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 as SortedSet<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 for Global not implemented)
  • <quantity>: PredefinedQuantity name from MIKE 1D (Mike1D.Generic), spaces ignored, e.g. WaterLevel, Discharge, Volume
  • <id>: Node/Catchment Id or Reach Name
  • chainage (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)

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 by TimeSeriesQueryReachSumGridPoints), which element-wise sums all grid point vectors.

Metadata on returned series

When a TimeSeries<string,double> is created (TimeSeriesQuery.*.CreateTimeSeries):

  • QuantityQuantity.Id
  • UnitQuantity.EumQuantity.UnitAbbreviation
  • DataType ← from Quantity.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: SetValuesNotImplementedException("Overwriting values in a result file is not allowed.")
  • Invalid ID / type: ArgumentException("Invalid time series id: …") or Exception("Unrecognized type: …")
  • Reach chainage:
    • Missing: Exception("Please add reach chainage …")
    • Not found: Exception("Could not find index for chainage: …")
  • 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: …")

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.
  • GetValuesForAllChainages streams 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.