/// <summary> /// Extract a single layer from a 3D dfsu file, and write it to a 2D dfsu file. /// <para> /// If a layer value does not exist for a certain 2D element, delete value is written /// to the 2D resut file. This is relevant for Sigma-Z type of files. /// </para> /// </summary> /// <param name="filenameDfsu3">Name of 3D dfsu source file</param> /// <param name="filenameDfsu2">Name of 2D dfsu result file</param> /// <param name="layerNumber">Layer to extract. /// <para> /// Positive values count from bottom up i.e. 1 is bottom layer, 2 is second layer from bottom etc. /// </para> /// <para> /// Negative values count from top down, i.e. -1 is toplayer, -2 is second layer from top etc. /// </para> /// </param> public static void ExtractDfsu2DLayerFrom3D(string filenameDfsu3, string filenameDfsu2, int layerNumber) { IDfsuFile dfsu3File = DfsFileFactory.DfsuFileOpen(filenameDfsu3); // Check that dfsu3 file is a 3D dfsu file. switch (dfsu3File.DfsuFileType) { case DfsuFileType.Dfsu2D: case DfsuFileType.DfsuVerticalColumn: case DfsuFileType.DfsuVerticalProfileSigma: case DfsuFileType.DfsuVerticalProfileSigmaZ: throw new InvalidOperationException("Input file is not a 3D dfsu file"); } // Calculate offset from toplayer element. Offset is between 0 (top layer) and // dfsu3File.NumberOfLayers-1 (bottom layer) int topLayerOffset; if (layerNumber > 0 && layerNumber <= dfsu3File.NumberOfLayers) { topLayerOffset = dfsu3File.NumberOfLayers - layerNumber; } else if (layerNumber < 0 && -layerNumber <= dfsu3File.NumberOfLayers) { topLayerOffset = -layerNumber - 1; } else { throw new ArgumentException("Layer number is out of range"); } double[] xv = dfsu3File.X; double[] yv = dfsu3File.Y; float[] zv = dfsu3File.Z; int[] cv = dfsu3File.Code; // -------------------------------------------------- // Create 2D mesh from 3D mesh // List of new 2D nodes int node2DCount = 0; List <double> xv2 = new List <double>(); List <double> yv2 = new List <double>(); List <float> zv2 = new List <float>(); List <int> cv2 = new List <int>(); // Renumbering array, from 3D node numbers to 2D node numbers // i.e. if a 3D element refers to node number k, the 2D element node number is renumber[k] int[] renumber = new int[dfsu3File.NumberOfNodes]; // Coordinates of last created node double xr2 = -1e-10; double yr2 = -1e-10; // Create 2D nodes, by skipping nodes with equal x,y coordinates for (int i = 0; i < dfsu3File.NumberOfNodes; i++) { // If 3D x,y coordinates are equal to the last created 2D node, // map this node to the last created 2D node, otherwise // create new 2D node and map to that one if (xv[i] != xr2 || yv[i] != yr2) { // Create new node node2DCount++; xr2 = xv[i]; yr2 = yv[i]; float zr2 = zv[i]; int cr2 = cv[i]; xv2.Add(xr2); yv2.Add(yr2); zv2.Add(zr2); cv2.Add(cr2); } // Map this 3D node to the last created 2D node. renumber[i] = node2DCount; } // Find indices of top layer elements IList <int> topLayer = dfsu3File.FindTopLayerElements(); // Create element table for 2D dfsu file int[][] elmttable2 = new int[topLayer.Count][]; for (int i = 0; i < topLayer.Count; i++) { // 3D element nodes int[] elmt3 = dfsu3File.ElementTable[topLayer[i]]; // 2D element nodes, only half as big, so copy over the first half int[] elmt2 = new int[elmt3.Length / 2]; for (int j = 0; j < elmt2.Length; j++) { elmt2[j] = renumber[elmt3[j]]; } elmttable2[i] = elmt2; } // -------------------------------------------------- // Create 2D dfsu file DfsuBuilder builder = DfsuBuilder.Create(DfsuFileType.Dfsu2D); // Setup header and geometry builder.SetNodes(xv2.ToArray(), yv2.ToArray(), zv2.ToArray(), cv2.ToArray()); builder.SetElements(elmttable2); builder.SetProjection(dfsu3File.Projection); builder.SetTimeInfo(dfsu3File.StartDateTime, dfsu3File.TimeStepInSeconds); if (dfsu3File.ZUnit == eumUnit.eumUUnitUndefined) { builder.SetZUnit(eumUnit.eumUmeter); } else { builder.SetZUnit(dfsu3File.ZUnit); } // Add dynamic items, copying from source, though not the first one, if it // contains the z-variation on the nodes for (int i = 0; i < dfsu3File.ItemInfo.Count; i++) { IDfsSimpleDynamicItemInfo itemInfo = dfsu3File.ItemInfo[i]; if (itemInfo.ElementCount == dfsu3File.NumberOfElements) { builder.AddDynamicItem(itemInfo.Name, itemInfo.Quantity); } } // Create file DfsuFile dfsu2File = builder.CreateFile(filenameDfsu2); // -------------------------------------------------- // Process data // Check if the layer number exists for 2D element, i.e. if that element // in 2D has that number of columnes in the 3D (relevant for sigma-z files) // If elementExists[i] is false, write delete value to file bool[] elementExists = new bool[topLayer.Count]; int numLayersInColumn = topLayer[0] + 1; elementExists[0] = (numLayersInColumn - topLayerOffset) > 0; for (int i = 1; i < topLayer.Count; i++) { numLayersInColumn = (topLayer[i] - topLayer[i - 1]); elementExists[i] = (numLayersInColumn - topLayerOffset) > 0; } // For performance, use predefined itemdata objects when reading data from dfsu 3D file IDfsItemData <float>[] dfsu3ItemDatas = new IDfsItemData <float> [dfsu3File.ItemInfo.Count]; for (int j = 0; j < dfsu3File.ItemInfo.Count; j++) { dfsu3ItemDatas[j] = (IDfsItemData <float>)dfsu3File.ItemInfo[j].CreateEmptyItemData(); } // Float data to write to dfsu 2D file float[] data2 = new float[dfsu2File.NumberOfElements]; float deleteValueFloat = dfsu2File.DeleteValueFloat; for (int i = 0; i < dfsu3File.NumberOfTimeSteps; i++) { for (int j = 0; j < dfsu3File.ItemInfo.Count; j++) { // Read data from 3D dfsu IDfsItemData <float> data3Item = dfsu3ItemDatas[j]; bool ok = dfsu3File.ReadItemTimeStep(data3Item, i); // 3D data float[] data3 = data3Item.Data; // Skip any items not having size = NumberOfElments (the z-variation on the nodes) if (data3.Length != dfsu3File.NumberOfElements) { continue; } // Loop over all 2D elements for (int k = 0; k < topLayer.Count; k++) { // Extract layer data from 3D column into 2D element value if (elementExists[k]) { data2[k] = data3[topLayer[k] - topLayerOffset]; } else { data2[k] = deleteValueFloat; } } dfsu2File.WriteItemTimeStepNext(data3Item.Time, data2); } } dfsu3File.Close(); dfsu2File.Close(); }
/// <summary> /// Create dfsu and mesh file from dfs2 file. /// <para> /// Note 1: Boundary code is set to land value at /// all boundaries of mesh and dfsu file. /// These must be updated to something "better" /// if to use as input in another simulation. /// </para> /// <para> /// Note 2: P and Q values are not rotated with the /// grid, but should be so, if used in the /// projected coordinate system. It must take /// the 327 degrees rotation into account. /// </para> /// </summary> /// <param name="dfs2Filename">Name of input dfs2 file, e.g. the OresundHD.dfs2</param> /// <param name="meshFilename">Name of output mesh file</param> /// <param name="dfsuFilename">Name of output dfsu file</param> public static void CreateDfsuFromDfs2(string dfs2Filename, string meshFilename, string dfsuFilename) { // Open file Dfs2File dfs2 = DfsFileFactory.Dfs2FileOpen(dfs2Filename); // Read bathymetry from first static item IDfsStaticItem bathymetryItem = dfs2.ReadStaticItemNext(); float[] bathymetry = (float[])bathymetryItem.Data; // Extract spatial axis IDfsAxisEqD2 spatialAxis = (IDfsAxisEqD2)dfs2.SpatialAxis; // Some convenience variables double dx = spatialAxis.Dx; double dy = spatialAxis.Dy; double x0 = spatialAxis.X0; double y0 = spatialAxis.Y0; int xCount = spatialAxis.XCount; int yCount = spatialAxis.YCount; // First custom block (index 0) contains the M21_MISC values, // where the 4th (index 3) is the land value float landValue = (float)dfs2.FileInfo.CustomBlocks[0][3]; //----------------------------------------- // Find out which elements in the dfs2 grid that is not a land value // and include all those elements and their surrounding nodes in mesh // Arrays indicating if element and node in grid is used or not in mesh bool[,] elmts = new bool[xCount, yCount]; int[,] nodes = new int[xCount + 1, yCount + 1]; // Loop over all elements in 2D grid for (int l = 0; l < yCount; l++) { for (int k = 0; k < xCount; k++) { // If bathymetry is not land value, use element. if (bathymetry[k + l * xCount] != landValue) { // element [l,k] is used, and also the 4 nodes around it elmts[k, l] = true; nodes[k, l] = 1; nodes[k + 1, l] = 1; nodes[k, l + 1] = 1; nodes[k + 1, l + 1] = 1; } } } //----------------------------------------- // Create new mest nodes // Cartography object can convert grid (x,y) to projection (east,north) IDfsProjection proj = dfs2.FileInfo.Projection; DHI.Projections.Cartography cart = new DHI.Projections.Cartography(proj.WKTString, proj.Longitude, proj.Latitude, proj.Orientation); // New mesh nodes List <double> X = new List <double>(); List <double> Y = new List <double>(); List <float> Zf = new List <float>(); // float values for dfsu file List <double> Zd = new List <double>(); // double values for mesh file List <int> Code = new List <int>(); // Loop over all nodes int nodesCount = 0; for (int l = 0; l < yCount + 1; l++) { for (int k = 0; k < xCount + 1; k++) { // Check if node is included in mesh if (nodes[k, l] > 0) { // Convert from mesh (x,y) to projection (east,north) double east, north; cart.Xy2Proj((k - 0.5) * dx + x0, (l - 0.5) * dy + y0, out east, out north); // Average Z on node from neighbouring grid cell values, cell value is used // unless they are outside grid or has land values double z = 0; int zCount = 0; if (k > 0 && l > 0 && bathymetry[k - 1 + (l - 1) * xCount] != landValue) { zCount++; z += bathymetry[k - 1 + (l - 1) * xCount]; } if (k < xCount && l > 0 && bathymetry[k + (l - 1) * xCount] != landValue) { zCount++; z += bathymetry[k + (l - 1) * xCount]; } if (k > 0 && l < yCount && bathymetry[k - 1 + (l) * xCount] != landValue) { zCount++; z += bathymetry[k - 1 + (l) * xCount]; } if (k < xCount && l < yCount && bathymetry[k + (l) * xCount] != landValue) { zCount++; z += bathymetry[k + (l) * xCount]; } if (zCount > 0) { z /= zCount; } else { z = landValue; } // Store new node number and add node nodesCount++; nodes[k, l] = nodesCount; // this is the node number to use in the element table X.Add(east); Y.Add(north); Zf.Add((float)z); Zd.Add(z); Code.Add(zCount == 4 ? 0 : 1); // Land boundary if zCount < 4 } } } // New mesh elements List <int[]> elmttable2 = new List <int[]>(); for (int l = 0; l < yCount; l++) { for (int k = 0; k < xCount; k++) { // Check if element is included in mesh if (elmts[k, l]) { // For this element, add the four surrounding nodes, // counter-clockwise order int[] newNodes = new int[4]; newNodes[0] = nodes[k, l]; newNodes[1] = nodes[k + 1, l]; newNodes[2] = nodes[k + 1, l + 1]; newNodes[3] = nodes[k, l + 1]; elmttable2.Add(newNodes); } } } //----------------------------------------- // Create mesh { // Create 2D dfsu file MeshBuilder builder = new MeshBuilder(); // Setup header and geometry builder.SetNodes(X.ToArray(), Y.ToArray(), Zd.ToArray(), Code.ToArray()); builder.SetElements(elmttable2.ToArray()); builder.SetProjection(dfs2.FileInfo.Projection); // Create new file MeshFile mesh = builder.CreateMesh(); mesh.Write(meshFilename); } //----------------------------------------- // Create dfsu file { // dfs2 time axis IDfsEqCalendarAxis timeAxis = (IDfsEqCalendarAxis)dfs2.FileInfo.TimeAxis; // Create 2D dfsu file DfsuBuilder builder = DfsuBuilder.Create(DfsuFileType.Dfsu2D); // Setup header and geometry builder.SetNodes(X.ToArray(), Y.ToArray(), Zf.ToArray(), Code.ToArray()); builder.SetElements(elmttable2.ToArray()); builder.SetProjection(dfs2.FileInfo.Projection); builder.SetTimeInfo(timeAxis.StartDateTime, timeAxis.TimeStepInSeconds()); builder.SetZUnit(eumUnit.eumUmeter); // Add dynamic items, copying from dfs2 file for (int i = 0; i < dfs2.ItemInfo.Count; i++) { IDfsSimpleDynamicItemInfo itemInfo = dfs2.ItemInfo[i]; builder.AddDynamicItem(itemInfo.Name, itemInfo.Quantity); } // Create new file DfsuFile dfsu = builder.CreateFile(dfsuFilename); // Add dfs2 data to dfsu file float[] dfsuData = new float[dfsu.NumberOfElements]; for (int i = 0; i < dfs2.FileInfo.TimeAxis.NumberOfTimeSteps; i++) { for (int j = 0; j < dfs2.ItemInfo.Count; j++) { // Read dfs2 grid data IDfsItemData2D <float> itemData = (IDfsItemData2D <float>)dfs2.ReadItemTimeStep(j + 1, i); // Extract 2D grid data to dfsu data array int lk = 0; for (int l = 0; l < yCount; l++) { for (int k = 0; k < xCount; k++) { if (elmts[k, l]) { dfsuData[lk++] = itemData[k, l]; } } } // write data dfsu.WriteItemTimeStepNext(itemData.Time, dfsuData); } } dfsu.Close(); } dfs2.Close(); }
/// <summary> /// Example on how to extract dfs0 data from a 2D dfsu file for certain elements. All items /// from dfsu file are extracted. /// </summary> /// <param name="dfsuFileNamePath">Name, including path, of 2D dfsu file</param> /// <param name="elmtsIndices">Indices of elements to extract data from</param> /// <param name="useStream">Use stream when writing dfs0 files - then more than 400 files can be created simultaneously</param> public static void ExtractDfs0FromDfsu(string dfsuFileNamePath, IList <int> elmtsIndices, bool useStream) { // If not using stream approach, at most 400 elements at a time can be processed. // There is a limit on how many files you can have open at the same time using // the standard approach. It will fail in a nasty way, if the maximum number of // file handles are exceeded. This is not an issue when using .NET streams. if (!useStream && elmtsIndices.Count > 400) { throw new ArgumentException("At most 400 elements at a time"); } // Open source dfsu file IDfsuFile source; Stream stream = null; if (useStream) { stream = new FileStream(dfsuFileNamePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); source = DfsuFile.Open(stream); } else { source = DfsuFile.Open(dfsuFileNamePath); } // Figure out "basic" dfs0 file name string dfsuFilename = Path.GetFileNameWithoutExtension(dfsuFileNamePath); string path = Path.GetDirectoryName(dfsuFileNamePath); string dfs0BaseFilename = Path.Combine(path, "test_" + dfsuFilename + "-"); // Factory for creating dfs objects DfsFactory factory = new DfsFactory(); // Create a dfs0 file for each element in elmtsIndices DfsFile[] dfs0Files = new DfsFile[elmtsIndices.Count]; Stream[] dfs0Streams = new Stream [elmtsIndices.Count]; double timeSpan = source.TimeStepInSeconds * source.NumberOfTimeSteps; for (int k = 0; k < elmtsIndices.Count; k++) { // Index of element to create dfs0 for int elmtsIndex = elmtsIndices[k]; // Calculate element center coordinates, to be stored in dfs0 items. // Stored as float in dfs0, hence possible loss of precision... float x = 0, y = 0, z = 0; int[] nodeNumbers = source.ElementTable[elmtsIndex]; for (int i = 0; i < nodeNumbers.Length; i++) { int nodeIndex = nodeNumbers[i] - 1; // from number to index x += (float)source.X[nodeIndex]; y += (float)source.Y[nodeIndex]; z += source.Z[nodeIndex]; } x /= nodeNumbers.Length; y /= nodeNumbers.Length; z /= nodeNumbers.Length; // Start building dfs0 file header DfsBuilder builder = DfsBuilder.Create("fileTitle", "appTitle", 1); builder.SetDataType(1); // standard dfs0 value builder.SetGeographicalProjection(source.Projection); builder.SetTemporalAxis(factory.CreateTemporalEqCalendarAxis(eumUnit.eumUsec, source.StartDateTime, 0, source.TimeStepInSeconds)); // Add all dynamic items from dfsu file to dfs0 file for (int j = 0; j < source.ItemInfo.Count; j++) { IDfsSimpleDynamicItemInfo sourceItem = source.ItemInfo[j]; DfsDynamicItemBuilder itemBuilder = builder.CreateDynamicItemBuilder(); itemBuilder.Set(sourceItem.Name, sourceItem.Quantity, sourceItem.DataType); itemBuilder.SetAxis(factory.CreateAxisEqD0()); itemBuilder.SetValueType(sourceItem.ValueType); itemBuilder.SetReferenceCoordinates(x, y, z); // optional builder.AddDynamicItem(itemBuilder.GetDynamicItemInfo()); } // Create and get file, store them in dfs0s array string dfs0Filename = dfs0BaseFilename + (elmtsIndex).ToString("000000") + ".dfs0"; if (useStream) { // Create file using C# streams - necessary to provie number of time steps and timespan of data builder.SetNumberOfTimeSteps(source.NumberOfTimeSteps); builder.SetTimeInfo(0, timeSpan); Stream dfs0FileStream = new FileStream(dfs0Filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); builder.CreateStream(dfs0FileStream); dfs0Streams[k] = dfs0FileStream; } else { // Create file in the ordinary way. Will include statistics (of delete values etc). builder.CreateFile(dfs0Filename); } dfs0Files[k] = builder.GetFile(); } // For performance, use predefined itemdata objects when reading data from dfsu IDfsItemData <float>[] dfsuItemDatas = new IDfsItemData <float> [source.ItemInfo.Count]; for (int j = 0; j < source.ItemInfo.Count; j++) { dfsuItemDatas[j] = (IDfsItemData <float>)source.ItemInfo[j].CreateEmptyItemData(); } // Read data from dfsu and store in dfs0 float[] dfs0Data = new float[1]; for (int i = 0; i < source.NumberOfTimeSteps; i++) { for (int j = 0; j < source.ItemInfo.Count; j++) { // Read data from dfsu IDfsItemData <float> dfsuItemData = dfsuItemDatas[j]; bool ok = source.ReadItemTimeStep(dfsuItemData, i); float[] floats = dfsuItemData.Data; // write data to dfs0's for (int k = 0; k < elmtsIndices.Count; k++) { int elmtsIndex = elmtsIndices[k]; dfs0Data[0] = floats[elmtsIndex]; dfs0Files[k].WriteItemTimeStepNext(0, dfs0Data); } } } // Close dfsu files source.Close(); if (stream != null) { stream.Close(); } // Close all dfs0 files for (int k = 0; k < elmtsIndices.Count; k++) { dfs0Files[k].Close(); if (dfs0Streams[k] != null) { dfs0Streams[k].Close(); } } }