diff --git a/libs/UGridNET/test/src/UGridNETTests.cs b/libs/UGridNET/test/src/UGridNETTests.cs index 6a29adc..894845d 100644 --- a/libs/UGridNET/test/src/UGridNETTests.cs +++ b/libs/UGridNET/test/src/UGridNETTests.cs @@ -1,126 +1,329 @@ using System; +using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Reflection; using System.Runtime.InteropServices; using NUnit.Framework; -using UGridNET.Tests.ExtensionMethods; +using UGridNET.Tests.TopologyExtensions; namespace UGridNET.Tests { - namespace ExtensionMethods - { - public static class SwigExtensions + internal static class IntPtrExtensions + { + public static T[] CopyToArray(this IntPtr pointer, int length) { + if (pointer == IntPtr.Zero) + { + throw new InvalidOperationException($"IntPtr {nameof(pointer)} is null. Cannot copy to array."); + } + + if (length <= 0) + { + throw new ArgumentException("Array length must be greater than zero.", nameof(length)); + } + var array = new T[length]; - public static SWIGTYPE_p_int NewInt(int length) + if (typeof(T) == typeof(double)) { + Marshal.Copy(pointer, array as double[], 0, array.Length); + } + else if (typeof(T) == typeof(int)) + { + Marshal.Copy(pointer, array as int[], 0, array.Length); + } + else + { + throw new NotSupportedException("Currently only double and int are supported."); + } + + return array; + } - var array = new SwigIntArray((uint)length); - return array.cast(); + public static void CopyFromArray(this IntPtr pointer, T[] array) + { + if (array == null || array.Length == 0) + { + throw new ArgumentException("Array must not be null or empty.", nameof(array)); } - public static SWIGTYPE_p_double NewDouble(int length) + if (pointer == IntPtr.Zero) { + throw new InvalidOperationException( + $"IntPtr {nameof(pointer)} is null. Cannot write array {nameof(array)} to pointer."); + } - var array = new SwigDoubleArray((uint)length); - return array.cast(); + if (typeof(T) == typeof(int)) + { + Marshal.Copy(array as int[], 0, pointer, array.Length); } + else if (typeof(T) == typeof(double)) + { + Marshal.Copy(array as double[], 0, pointer, array.Length); + } + else + { + throw new NotSupportedException("Currently only int and double are supported"); + } + } + } + + namespace TopologyExtensions + { + internal static class Mesh1DExtensions + { - public static int[] ToArray(this SWIGTYPE_p_int ptr, int length) + public static void Allocate(this Mesh1D mesh1D) { - if (ptr == null) - { - throw new ArgumentNullException(nameof(ptr)); - } + int int_bytes = Marshal.SizeOf(); + int double_bytes = Marshal.SizeOf(); + mesh1D.node_x = Marshal.AllocHGlobal(mesh1D.num_nodes * double_bytes); + mesh1D.node_y = Marshal.AllocHGlobal(mesh1D.num_nodes * double_bytes); + mesh1D.edge_x = Marshal.AllocHGlobal(mesh1D.num_edges * double_bytes); + mesh1D.edge_y = Marshal.AllocHGlobal(mesh1D.num_edges * double_bytes); + mesh1D.edge_nodes = Marshal.AllocHGlobal(mesh1D.num_edges * 2 * int_bytes); + mesh1D.edge_edge_id = Marshal.AllocHGlobal(mesh1D.num_nodes * int_bytes); // size? + mesh1D.node_edge_id = Marshal.AllocHGlobal(mesh1D.num_nodes * int_bytes); // size? + mesh1D.node_edge_offset = Marshal.AllocHGlobal(mesh1D.num_nodes * double_bytes); + } - if (length <= 0) - { - throw new ArgumentException("Length must be greater than zero.", nameof(length)); - } + // This should be done in Dispose. How can i do this without inheritance? + // Can probably extend Mesh1D in the SWIG config and have it dump the extension directly in the generated class? + public static void Free(this Mesh1D mesh1D) + { + if (mesh1D.node_x != IntPtr.Zero) Marshal.FreeHGlobal(mesh1D.node_x); + if (mesh1D.node_y != IntPtr.Zero) Marshal.FreeHGlobal(mesh1D.node_y); + if (mesh1D.edge_x != IntPtr.Zero) Marshal.FreeHGlobal(mesh1D.edge_x); + if (mesh1D.edge_y != IntPtr.Zero) Marshal.FreeHGlobal(mesh1D.edge_x); + if (mesh1D.edge_nodes != IntPtr.Zero) Marshal.FreeHGlobal(mesh1D.edge_nodes); + if (mesh1D.edge_edge_id != IntPtr.Zero) Marshal.FreeHGlobal(mesh1D.edge_edge_id); + if (mesh1D.node_edge_id != IntPtr.Zero) Marshal.FreeHGlobal(mesh1D.node_edge_id); + if (mesh1D.node_edge_offset != IntPtr.Zero) Marshal.FreeHGlobal(mesh1D.node_edge_offset); + } + } - var swigArray = SwigIntArray.frompointer(ptr); + internal static class Mesh2DExtensions + { + public static void Allocate(this Mesh2D mesh2D) + { + int int_bytes = Marshal.SizeOf(); + int double_bytes = Marshal.SizeOf(); + mesh2D.node_x = Marshal.AllocHGlobal(mesh2D.num_nodes * double_bytes); + mesh2D.node_y = Marshal.AllocHGlobal(mesh2D.num_nodes * double_bytes); + mesh2D.node_z = Marshal.AllocHGlobal(mesh2D.num_nodes * double_bytes); + mesh2D.edge_x = Marshal.AllocHGlobal(mesh2D.num_edges * double_bytes); + mesh2D.edge_y = Marshal.AllocHGlobal(mesh2D.num_edges * double_bytes); + mesh2D.edge_z = Marshal.AllocHGlobal(mesh2D.num_edges * double_bytes); + mesh2D.face_x = Marshal.AllocHGlobal(mesh2D.num_faces * double_bytes); + mesh2D.face_y = Marshal.AllocHGlobal(mesh2D.num_faces * double_bytes); + mesh2D.face_z = Marshal.AllocHGlobal(mesh2D.num_faces * double_bytes); + mesh2D.edge_nodes = Marshal.AllocHGlobal(mesh2D.num_edges * 2 * int_bytes); + mesh2D.edge_faces = Marshal.AllocHGlobal(mesh2D.num_edges * 2 * int_bytes); + mesh2D.face_nodes = Marshal.AllocHGlobal(mesh2D.num_faces * mesh2D.num_face_nodes_max * int_bytes); + mesh2D.face_edges = Marshal.AllocHGlobal(mesh2D.num_faces * mesh2D.num_face_nodes_max * int_bytes); + mesh2D.face_faces = Marshal.AllocHGlobal(mesh2D.num_faces * mesh2D.num_face_nodes_max * int_bytes); + } - var array = new int[length]; + // This should be done in Dispose. How can i do this without inheritance? + // Can probably extend Mesh2D in the SWIG config and have it dump the extension directly in the generated class? + public static void Free(this Mesh2D mesh2D) + { + if (mesh2D.node_x != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.node_x); + if (mesh2D.node_y != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.node_y); + if (mesh2D.node_z != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.node_z); + if (mesh2D.edge_x != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.edge_x); + if (mesh2D.edge_y != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.edge_y); + if (mesh2D.edge_z != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.edge_z); + if (mesh2D.face_x != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.face_x); + if (mesh2D.face_y != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.face_y); + if (mesh2D.face_z != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.face_z); + if (mesh2D.edge_nodes != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.edge_nodes); + if (mesh2D.edge_faces != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.edge_faces); + if (mesh2D.face_nodes != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.face_nodes); + if (mesh2D.face_edges != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.face_edges); + if (mesh2D.face_faces != IntPtr.Zero) Marshal.FreeHGlobal(mesh2D.face_faces); + } + } - for (uint i = 0; i < length; i++) - { - array[i] = swigArray.getitem(i); - } + internal static class ContactsExtensions + { + public static void Allocate(this Contacts contacts) + { + throw new NotImplementedException("TODO: ContactsExtensions::Allocate"); + } - return array; + // This should be done in Dispose. How can i do this without inheritance? + // Can probably extend Contacts in the SWIG config and have it dump the extension directly in the generated class? + public static void Free(this Contacts mesh2D) + { + throw new NotImplementedException("TODO: ContactsExtensions::Free"); } + } - public static double[] ToArray(this SWIGTYPE_p_double ptr, int length) + internal static class Network1DExtensions + { + public static void Allocate(this Network1D contacts) { - if (ptr == null) - { - throw new ArgumentNullException(nameof(ptr)); - } + throw new NotImplementedException("TODO: Network1DExtensions::Allocate"); + } - if (length <= 0) - { - throw new ArgumentException("Length must be greater than zero.", nameof(length)); - } + // This should be done in Dispose. How can i do this without inheritance? + // Can probably extend Network1D in the SWIG config and have it dump the extension directly in the generated class? + public static void Free(this Network1D mesh2D) + { + throw new NotImplementedException("TODO: Network1DExtensions::Free"); + } + } + } - var swigArray = SwigDoubleArray.frompointer(ptr); + public sealed class UGridWrapper : IDisposable + { + // requires using log4net; + //private static readonly ILog log = LogManager.GetLogger(typeof(UGridWrapper)); - var array = new double[length]; - for (uint i = 0; i < length; i++) - { - array[i] = swigArray.getitem(i); - } + private int fileId = -1; + private int exitCode = -1; + + private List mesh1DList; + private List mesh2DList; + private List contactsList; + private List network1DList; - return array; - } - public static void FromArray(this SWIGTYPE_p_int ptr, int[] array, int length) + public UGridWrapper(string path) + { + Read(path); + GetMesh1D(); + GetMesh2D(); + GetContacts(); + GetNetwork1D(); + } + + private void Dispose(bool disposing) + { + if (disposing) { - if (ptr == null) - { - throw new ArgumentNullException(nameof(ptr)); - } + mesh1DList.ForEach(item => item.Free()); + mesh2DList.ForEach(item => item.Free()); + contactsList.ForEach(item => item.Free()); + network1DList.ForEach(item => item.Free()); + UGrid.ug_file_close(fileId); + } + } - if (length != array.Length) - { - throw new ArgumentException("Length of the target does not match that of the source array.", nameof(length)); - } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - var swigArray = SwigIntArray.frompointer(ptr); + private void ProcessExitCode(int exitCode) + { + string message = ""; + UGrid.ug_error_get(message); + + if (!string.IsNullOrWhiteSpace(message)) + { + //log.Error(errorMessage); + // log to standard output for now + Console.WriteLine(message); + } + } - for (uint i = 0; i < length; i++) + private void Read(string path) + { + int fileMode = -1; + exitCode = UGrid.ug_file_read_mode(ref fileMode); + ProcessExitCode(exitCode); + exitCode = UGrid.ug_file_open(path, fileMode, ref fileId); + ProcessExitCode(exitCode); + } + + private void GetMesh1D() + { + int count = -1; + exitCode = UGrid.ug_topology_get_count(fileId, TopologyType.Mesh1dTopology, ref count); + ProcessExitCode(exitCode); + if (count > 0) + { + for (int i = 0; i < count; i++) { - swigArray.setitem(i, array[i]); + Mesh1D mesh1D = new Mesh1D(); + exitCode = UGrid.ug_mesh1d_inq(fileId, i, mesh1D); + ProcessExitCode(exitCode); + mesh1D.Allocate(); + exitCode = UGrid.ug_mesh1d_get(fileId, i, mesh1D); + ProcessExitCode(exitCode); + mesh1DList.Add(mesh1D); } - } + } - public static void FromArray(this SWIGTYPE_p_double ptr, double[] array, int length) + private void GetMesh2D() + { + int count = -1; + exitCode = UGrid.ug_topology_get_count(fileId, TopologyType.Mesh2dTopology, ref count); + ProcessExitCode(exitCode); + if (count > 0) { - if (ptr == null) + for (int i = 0; i < count; i++) { - throw new ArgumentNullException(nameof(ptr)); + Mesh2D mesh2D = new Mesh2D(); + exitCode = UGrid.ug_mesh2d_inq(fileId, i, mesh2D); + ProcessExitCode(exitCode); + mesh2D.Allocate(); + exitCode = UGrid.ug_mesh2d_get(fileId, i, mesh2D); + ProcessExitCode(exitCode); + mesh2DList.Add(mesh2D); } + } + } - if (length != array.Length) + private void GetContacts() + { + int count = -1; + exitCode = UGrid.ug_topology_get_count(fileId, TopologyType.ContactsTopology, ref count); + ProcessExitCode(exitCode); + if (count > 0) + { + for (int i = 0; i < count; i++) { - throw new ArgumentException("Length of the target does not match that of the source array.", nameof(length)); + Contacts contacts = new Contacts(); + exitCode = UGrid.ug_contacts_inq(fileId, i, contacts); + ProcessExitCode(exitCode); + contacts.Allocate(); + exitCode = UGrid.ug_contacts_get(fileId, i, contacts); + ProcessExitCode(exitCode); + contactsList.Add(contacts); } - - var swigArray = SwigDoubleArray.frompointer(ptr); - - for (uint i = 0; i < length; i++) + } + } + private void GetNetwork1D() + { + int count = -1; + exitCode = UGrid.ug_topology_get_count(fileId, TopologyType.ContactsTopology, ref count); + ProcessExitCode(exitCode); + if (count > 0) + { + for (int i = 0; i < count; i++) { - swigArray.setitem(i, array[i]); + Network1D network1D = new Network1D(); + exitCode = UGrid.ug_network1d_inq(fileId, i, network1D); + ProcessExitCode(exitCode); + network1D.Allocate(); + exitCode = UGrid.ug_network1d_get(fileId, i, network1D); + ProcessExitCode(exitCode); + network1DList.Add(network1D); } - } - } } + [TestFixture] public class UGridNetTests { @@ -138,7 +341,8 @@ private static string GetTestDataPath() // Check if the path is absolute and that it exists if (!(Path.IsPathRooted(path) && Directory.Exists(path))) { - throw new InvalidOperationException($"The specified UGRID_TEST_DATA_DIR path must be absolute and should exist. UGRID_TEST_DATA_DIR={path}"); + throw new InvalidOperationException( + $"The specified UGRID_TEST_DATA_DIR path must be absolute and should exist. UGRID_TEST_DATA_DIR={path}"); } return path; @@ -182,7 +386,8 @@ public void GetDoubleDataByVariableName() // compare 20 values between 86 and 106 const int offset = 85; const int sampleSize = 20; - double[] expectedData = new double[sampleSize] { + double[] expectedData = new double[sampleSize] + { -5.0, 0.27, 0.75, @@ -246,32 +451,36 @@ public void TestMesh1DReadAndInquire() Assert.That(mesh1D.num_nodes, Is.EqualTo(25)); Assert.That(mesh1D.num_edges, Is.EqualTo(24)); - //mesh1D.node_x = (new SwigDoubleArray((uint)mesh1D.num_nodes)).cast(); - //mesh1D.edge_nodes = (new SwigIntArray((uint)mesh1D.num_edges * 2)).cast(); + int int_bytes = Marshal.SizeOf(); + int double_bytes = Marshal.SizeOf(); + mesh1D.node_x = Marshal.AllocHGlobal(mesh1D.num_nodes * double_bytes); + mesh1D.edge_nodes = Marshal.AllocHGlobal(mesh1D.num_edges * 2 * int_bytes); - mesh1D.node_x = SwigExtensions.NewDouble(mesh1D.num_nodes); - mesh1D.edge_nodes = SwigExtensions.NewInt(mesh1D.num_edges * 2); - //SwigExtensions.Alloc(ref mesh1D.node_x, mesh1D.num_nodes); - //SwigExtensions.Alloc(ref mesh1D.edge_nodes, mesh1D.num_edges * 2); // get the data result = UGrid.ug_mesh1d_get(fileId, 0, mesh1D); Assert.That(result, Is.EqualTo(0)); + var arr_1 = mesh1D.edge_nodes.CopyToArray(mesh1D.num_edges * 2); + + var src = new int[mesh1D.num_edges * 2]; + for (uint i = 0; i < mesh1D.num_edges * 2; i++) + { + src[i] = 666; + } - var arr = SwigIntArray.frompointer(mesh1D.edge_nodes); - var arr_2 = mesh1D.edge_nodes.ToArray(mesh1D.num_edges * 2); + mesh1D.edge_nodes.CopyFromArray(src); - SWIGTYPE_p_int ptr = (new SwigIntArray((uint)mesh1D.num_edges * 2)).cast(); - ptr.FromArray(arr_2, mesh1D.num_edges * 2); - var arr_3 = ptr.ToArray(mesh1D.num_edges * 2); + var arr_2 = mesh1D.edge_nodes.CopyToArray(mesh1D.num_edges * 2); for (uint i = 0; i < mesh1D.num_edges * 2; i++) { - Console.WriteLine("{0} : {1} : {2}", arr.getitem(i), arr_2[i], arr_3[i]); + Console.WriteLine("{0} : {1}", arr_1[i], arr_2[i]); } + Marshal.FreeHGlobal(mesh1D.node_x); + Marshal.FreeHGlobal(mesh1D.edge_nodes); result = UGrid.ug_file_close(fileId); Assert.That(result, Is.EqualTo(0));