/// <summary> /// Create maximum velocity field for a dfs2 file /// <para> /// FFrom a dfs2 file containing items (H-P-Q), (P-Q-Speed) or (u-v-Speed), /// find maximum velocity for each cell and store in [outputFilename] /// </para> /// </summary> public static void MaxVelocityField(string sourceFilename, string outfilename) { // Open source file IDfs2File source = DfsFileFactory.Dfs2FileOpen(sourceFilename); // Create output file Dfs2Builder builder = Dfs2Builder.Create("Max Velocity", @"MIKE SDK", 0); // Set up the header builder.SetDataType(1); builder.SetGeographicalProjection(source.FileInfo.Projection); builder.SetTemporalAxis(source.FileInfo.TimeAxis); builder.SetSpatialAxis(source.SpatialAxis); builder.DeleteValueFloat = -1e-30f; // Add custom block foreach (IDfsCustomBlock customBlock in source.FileInfo.CustomBlocks) { builder.AddCustomBlock(customBlock); } // Set up dynamic items builder.AddDynamicItem("Maximum Speed", eumQuantity.Create(eumItem.eumIFlowVelocity, eumUnit.eumUmeterPerSec), DfsSimpleType.Float, DataValueType.Instantaneous); builder.AddDynamicItem("u-velocity", eumQuantity.Create(eumItem.eumIFlowVelocity, eumUnit.eumUmeterPerSec), DfsSimpleType.Float, DataValueType.Instantaneous); builder.AddDynamicItem("v-velocity", eumQuantity.Create(eumItem.eumIFlowVelocity, eumUnit.eumUmeterPerSec), DfsSimpleType.Float, DataValueType.Instantaneous); //builder.AddDynamicItem("H Water Depth m", eumQuantity.Create(eumItem.eumIWaterLevel, eumUnit.eumUmeter), DfsSimpleType.Float, DataValueType.Instantaneous); // Create file builder.CreateFile(outfilename); // Add static items containing bathymetri data, use data from source IDfsStaticItem sourceStaticItem; while (null != (sourceStaticItem = source.ReadStaticItemNext())) { builder.AddStaticItem(sourceStaticItem.Name, sourceStaticItem.Quantity, sourceStaticItem.Data); } // Get the file Dfs2File file = builder.GetFile(); // Arrays storing max-speed values int numberOfCells = file.SpatialAxis.SizeOfData; float[] maxSpeed = new float[numberOfCells]; float[] uAtMaxSpeed = new float[numberOfCells]; float[] vAtMaxSpeed = new float[numberOfCells]; // Initialize with delete values for (int i = 0; i < numberOfCells; i++) { maxSpeed[i] = source.FileInfo.DeleteValueFloat; uAtMaxSpeed[i] = source.FileInfo.DeleteValueFloat; vAtMaxSpeed[i] = source.FileInfo.DeleteValueFloat; } // Create empty ItemData's, for easing reading of source data IDfsItemData2D <float>[] datas = new IDfsItemData2D <float> [source.ItemInfo.Count]; for (int i = 0; i < source.ItemInfo.Count; i++) { datas[i] = source.CreateEmptyItemData <float>(i + 1); } // Find HPQ items in file - uses StartsWith, since the string varies slightly with the version of the engine. int dIndex = source.ItemInfo.FindIndex(item => item.Name.StartsWith("H Water Depth", StringComparison.OrdinalIgnoreCase)); int pIndex = source.ItemInfo.FindIndex(item => item.Name.StartsWith("P Flux", StringComparison.OrdinalIgnoreCase)); int qIndex = source.ItemInfo.FindIndex(item => item.Name.StartsWith("Q Flux", StringComparison.OrdinalIgnoreCase)); int sIndex = source.ItemInfo.FindIndex(item => item.Name.StartsWith("Current Speed", StringComparison.OrdinalIgnoreCase)); int uIndex = source.ItemInfo.FindIndex(item => item.Name.StartsWith("U velocity", StringComparison.OrdinalIgnoreCase)); int vIndex = source.ItemInfo.FindIndex(item => item.Name.StartsWith("V velocity", StringComparison.OrdinalIgnoreCase)); // Either p and q must be there, or u and v, and either d or s must be there. bool haspq = (pIndex >= 0 && qIndex >= 0); bool hasuv = (uIndex >= 0 && vIndex >= 0); if (!hasuv && !haspq || dIndex < 0 && sIndex < 0) { throw new Exception("Could not find items. File must have H-P-Q items, P-Q-Speed or U-V-Speed items"); } IDfsItemData2D <float> dItem = dIndex >= 0 ? datas[dIndex] : null; IDfsItemData2D <float> pItem = pIndex >= 0 ? datas[pIndex] : null; IDfsItemData2D <float> qItem = qIndex >= 0 ? datas[qIndex] : null; IDfsItemData2D <float> sItem = sIndex >= 0 ? datas[sIndex] : null; IDfsItemData2D <float> uItem = uIndex >= 0 ? datas[uIndex] : null; IDfsItemData2D <float> vItem = vIndex >= 0 ? datas[vIndex] : null; // Spatial 2D axis IDfsAxisEqD2 axis = (IDfsAxisEqD2)source.SpatialAxis; double dx = axis.Dx; double dy = axis.Dy; // Loop over all time steps for (int i = 0; i < source.FileInfo.TimeAxis.NumberOfTimeSteps; i++) { // Read data for all items from source file. That will also update the depth, p and q. for (int j = 0; j < source.ItemInfo.Count; j++) { source.ReadItemTimeStep(datas[j], i); } // For each cell, find maximum speed and store u, v and depth at that point in time. for (int j = 0; j < numberOfCells; j++) { // Skip delete values if (dItem?.Data[j] == source.FileInfo.DeleteValueFloat || sItem?.Data[j] == source.FileInfo.DeleteValueFloat) { continue; } double p = pItem.Data[j]; double q = qItem.Data[j]; double speed, u, v; if (sItem != null) { // Use speed from result file speed = sItem.Data[j]; if (hasuv) { // Use u and v from result file u = uItem.Data[j]; v = vItem.Data[j]; } else // (haspq) { // Calculate u and v from speed and direction of p and q double pqLength = System.Math.Sqrt(p * p + q * q); u = hasuv ? uItem.Data[j] : speed * p / pqLength; v = hasuv ? vItem.Data[j] : speed * q / pqLength; } } else // (dItem != null) { // Current speed is not directly available in source file, calculate from u and v if (hasuv) { u = uItem.Data[j]; v = vItem.Data[j]; } else { // u and v is not available, calculate fromdh, p and q. double d = dItem.Data[j]; u = pItem.Data[j] / d; v = qItem.Data[j] / d; } speed = System.Math.Sqrt(u * u + v * v); } if (speed > maxSpeed[j]) { maxSpeed[j] = (float)speed; uAtMaxSpeed[j] = (float)u; vAtMaxSpeed[j] = (float)v; } } } file.WriteItemTimeStepNext(0, maxSpeed); file.WriteItemTimeStepNext(0, uAtMaxSpeed); file.WriteItemTimeStepNext(0, vAtMaxSpeed); //file.WriteItemTimeStepNext(0, maxDepth); source.Close(); file.Close(); }
/// <summary> /// Example of how to create a Dfs2 file from scratch. This method /// creates a copy of the OresundHD.dfs2 test file. /// <para> /// Data for static and dynamic item is taken from a source dfs file, /// which here is the OresundHD.dfs2 test file. The data could come /// from any other source. /// </para> /// </summary> /// <param name="sourceFilename">Path and name of the OresundHD.dfs2 test file</param> /// <param name="filename">Path and name of the new file to create</param> public static void CreateDfs2File(string sourceFilename, string filename) { IDfs2File source = DfsFileFactory.Dfs2FileOpen(sourceFilename); DfsFactory factory = new DfsFactory(); Dfs2Builder builder = Dfs2Builder.Create("", @"C:\Program Files\DHI\2010\bin\nmodel.exe", 0); // Set up the header builder.SetDataType(1); builder.SetGeographicalProjection(factory.CreateProjectionGeoOrigin("UTM-33", 12.438741600559766, 55.225707842436385, 326.99999999999955)); builder.SetTemporalAxis(factory.CreateTemporalEqCalendarAxis(eumUnit.eumUsec, new DateTime(1993, 12, 02, 0, 0, 0), 0, 86400)); builder.SetSpatialAxis(factory.CreateAxisEqD2(eumUnit.eumUmeter, 71, 0, 900, 91, 0, 900)); builder.DeleteValueFloat = -1e-30f; // Add custom block // M21_Misc : {orientation (should match projection), drying depth, -900=has projection, land value, 0, 0, 0} builder.AddCustomBlock(factory.CreateCustomBlock("M21_Misc", new float[] { 327f, 0.2f, -900f, 10f, 0f, 0f, 0f })); // Set up dynamic items builder.AddDynamicItem("H Water Depth m", eumQuantity.Create(eumItem.eumIWaterLevel, eumUnit.eumUmeter), DfsSimpleType.Float, DataValueType.Instantaneous); builder.AddDynamicItem("P Flux m^3/s/m", eumQuantity.Create(eumItem.eumIFlowFlux, eumUnit.eumUm3PerSecPerM), DfsSimpleType.Float, DataValueType.Instantaneous); builder.AddDynamicItem("Q Flux m^3/s/m", eumQuantity.Create(eumItem.eumIFlowFlux, eumUnit.eumUm3PerSecPerM), DfsSimpleType.Float, DataValueType.Instantaneous); // Create file builder.CreateFile(filename); // Add static items containing bathymetri data, use data from source IDfsStaticItem sourceStaticItem = source.ReadStaticItemNext(); builder.AddStaticItem("Static item", eumQuantity.UnDefined, sourceStaticItem.Data); // Get the file Dfs2File file = builder.GetFile(); // Loop over all time steps for (int i = 0; i < source.FileInfo.TimeAxis.NumberOfTimeSteps; i++) { // Loop over all items for (int j = 0; j < source.ItemInfo.Count; j++) { // Add data for all item-timesteps, copying data from source file. // Read data from source file IDfsItemData2D <float> sourceData = (IDfsItemData2D <float>)source.ReadItemTimeStepNext(); // Create empty item data, and copy over data from source // The IDfsItemData2D can handle 2D indexing, on the form data2D[k,l]. // An ordinary array, float[], can also be used, though indexing from 2D to 1D must be // handled by user code i.e. using data1D[k + l*xCount] compared to data2D[k,l] IDfsItemData2D <float> itemData2D = (IDfsItemData2D <float>)file.CreateEmptyItemData(j + 1); for (int k = 0; k < 71; k++) { for (int l = 0; l < 91; l++) { itemData2D[k, l] = sourceData[k, l]; } } // the itemData2D.Data is a float[], so any float[] of the correct size is valid here. file.WriteItemTimeStep(j + 1, i, sourceData.Time, itemData2D.Data); } } source.Close(); file.Close(); }