diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index cff2df95..ff19a64c 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -33,21 +33,20 @@ jobs: - name: Checkout Repository uses: actions/checkout@v2 - uses: actions/setup-python@v1 - - name: Install OpenMPI, CMake, Boost library, Eigen and pkg-config + - name: Install OpenMPI, CMake, Boost library, Eigen, libxml2 and pkg-config run: | sudo apt-get -yy update - sudo apt-get install -y libopenmpi-dev cmake libboost-all-dev libeigen3-dev pkg-config + sudo apt-get install -y libopenmpi-dev cmake libboost-all-dev libeigen3-dev libxml2-dev pkg-config sudo rm -rf /var/lib/apt/lists/* - - name: Checkout precice and make required files discoverable + - name: Checkout precice and build the universal mock + # TODO: switch back to --branch develop https://github.com/precice/precice.git + # once the universal mock (extras/mock) is merged upstream run: | - git clone --branch develop https://github.com/precice/precice.git precice-core - mkdir -p precice - cp precice-core/src/precice/Participant.hpp precice/Participant.hpp - cp precice-core/src/precice/Tooling.hpp precice/Tooling.hpp - cp precice-core/src/precice/Tooling.cpp precice/Tooling.cpp + git clone --branch add-mocking-prototype https://github.com/JulianHarrison-Wirth/precice.git precice-core cd precice-core mkdir build && cd build cmake .. -DPRECICE_FEATURE_MPI_COMMUNICATION=OFF -DPRECICE_FEATURE_PETSC_MAPPING=OFF -DPRECICE_FEATURE_PYTHON_ACTIONS=OFF -DBUILD_TESTING=OFF + cmake --build . --target preciceMocked -j 2 - name: Install tox run: pip install tox - name: Run tox @@ -55,6 +54,7 @@ jobs: PKG_CONFIG_SYSTEM_INCLUDE_PATH: 1 run: | export PKG_CONFIG_PATH=$(readlink -f "precice-core/build") + export PRECICE_MOCK_LIB=$(readlink -f "precice-core/build/libpreciceMocked.so") tox build_dist: diff --git a/MANIFEST.in b/MANIFEST.in index ce772adb..bbe7b09a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -10,7 +10,6 @@ include LICENSE.txt include cyprecice/*.pyx include cyprecice/*.pxd include test/test_bindings_module.py -include test/Participant.cpp -# Include mocked interface file -include test/Participant.cpp +# Include configuration used by the tests together with the universal preCICE mock +include test/precice-config.xml diff --git a/setup.py b/setup.py index 2cdcf5ff..31b35f32 100644 --- a/setup.py +++ b/setup.py @@ -33,8 +33,11 @@ def get_extensions(): compile_args += cflags if os.environ.get(MOCKED_ENV) is not None: - print(f"Building mocked pyprecice as {MOCKED_ENV} is set") - bindings_sources.append("test/Participant.cpp") + print( + f"Building pyprecice without linking libprecice as {MOCKED_ENV} is set.\n" + "The preCICE symbols have to be provided at runtime by preloading the " + "universal preCICE mock, e.g. LD_PRELOAD=/path/to/libpreciceMocked.so" + ) else: link_args += ldflags diff --git a/test/Participant.cpp b/test/Participant.cpp deleted file mode 100644 index dd24d5c3..00000000 --- a/test/Participant.cpp +++ /dev/null @@ -1,260 +0,0 @@ -#include "precice/Participant.hpp" -#include "precice/Tooling.hpp" -#include -#include -#include -#include - -bool operator==(const ::precice::string_view &sv, const std::string &s) { - return std::string(sv.begin(), sv.end()) == s; -} - -std::string fake_version; -std::vector fake_read_write_buffer; -int fake_mesh_dimensions; -int fake_scalar_data_dimensions; -int fake_vector_data_dimensions; -std::vector fake_ids; -int n_fake_vertices; -std::string fake_mesh_name; -std::string fake_scalar_data_name; -std::string fake_vector_data_name; -int fake_data_id; -std::vector fake_bounding_box; -std::vector fake_coordinates; - -namespace precice { - -namespace impl { -class ParticipantImpl {}; -} // namespace impl - -Participant::Participant(precice::string_view participantName, - precice::string_view configurationFileName, - int solverProcessIndex, int solverProcessSize) { - fake_version = "dummy"; - fake_read_write_buffer = std::vector(); - fake_mesh_dimensions = 3; - fake_scalar_data_dimensions = 1; - fake_vector_data_dimensions = 3; - fake_data_id = 15; - fake_mesh_name = "FakeMesh"; - fake_scalar_data_name = "FakeScalarData"; - fake_vector_data_name = "FakeVectorData"; - n_fake_vertices = 3; - fake_ids.resize(n_fake_vertices); - std::iota(fake_ids.begin(), fake_ids.end(), 0); - fake_bounding_box.resize(fake_mesh_dimensions * 2); - std::iota(fake_bounding_box.begin(), fake_bounding_box.end(), 0); - fake_coordinates.resize(n_fake_vertices * fake_mesh_dimensions); - std::iota(fake_coordinates.begin(), fake_coordinates.end(), 0); -} - -Participant::Participant(precice::string_view participantName, - precice::string_view configurationFileName, - int solverProcessIndex, int solverProcessSize, - void *communicator) { - fake_version = "dummy"; - fake_read_write_buffer = std::vector(); - fake_mesh_dimensions = 3; - fake_scalar_data_dimensions = 1; - fake_vector_data_dimensions = 3; - fake_data_id = 15; - fake_mesh_name = "FakeMesh"; - fake_scalar_data_name = "FakeScalarData"; - fake_vector_data_name = "FakeVectorData"; - n_fake_vertices = 3; - fake_ids.resize(n_fake_vertices); - std::iota(fake_ids.begin(), fake_ids.end(), 0); - fake_bounding_box.resize(fake_mesh_dimensions * 2); - std::iota(fake_bounding_box.begin(), fake_bounding_box.end(), 0); -} - -Participant::~Participant() = default; - -void Participant::initialize() {} - -void Participant::advance(double computedTimestepLength) {} - -void Participant::finalize() {} - -int Participant::getMeshDimensions(precice::string_view meshName) const { - return fake_mesh_dimensions; -} - -int Participant::getDataDimensions(precice::string_view meshName, - precice::string_view dataName) const { - if (dataName.data() == fake_scalar_data_name) { - return fake_scalar_data_dimensions; - } else if (dataName.data() == fake_vector_data_name) { - return fake_vector_data_dimensions; - } else { - return -1; - } -} - -bool Participant::isCouplingOngoing() const { return 0; } - -bool Participant::isTimeWindowComplete() const { return 0; } - -double Participant::getMaxTimeStepSize() const { return -1.0; } - -bool Participant::requiresInitialData() { return 0; } - -bool Participant::requiresReadingCheckpoint() { return 0; } - -bool Participant::requiresWritingCheckpoint() { return 0; } - -bool Participant::requiresMeshConnectivityFor( - precice::string_view meshName) const { - return 0; -} - -bool Participant::requiresGradientDataFor(precice::string_view meshName, - precice::string_view dataName) const { - return 0; -} - -int Participant::setMeshVertex(precice::string_view meshName, - precice::span position) { - return 0; -} - -int Participant::getMeshVertexSize(precice::string_view meshName) const { - return n_fake_vertices; -} - -void Participant::setMeshVertices(precice::string_view meshName, - precice::span positions, - precice::span ids) { - if (ids.size() > 0) { - assert(ids.size() == fake_ids.size()); - std::copy(fake_ids.begin(), fake_ids.end(), ids.data()); - } -} - -void Participant::setMeshEdge(precice::string_view meshName, int firstVertexID, - int secondVertexID) {} - -void Participant::setMeshEdges( - precice::string_view meshName, - precice::span vertices) {} - -void Participant::setMeshTriangle(precice::string_view meshName, - int firstVertexID, int secondVertexID, - int thirdVertexID) {} - -void Participant::setMeshTriangles( - precice::string_view meshName, - precice::span vertices) {} - -void Participant::setMeshQuad(precice::string_view meshName, int firstVertexID, - int secondVertexID, int thirdVertexID, - int fourthVertexID) {} - -void Participant::setMeshQuads( - precice::string_view meshName, - precice::span vertices) {} - -void Participant::setMeshTetrahedron(precice::string_view meshName, - int firstVertexID, int secondVertexID, - int thirdVertexID, int fourthVertexID) {} - -void Participant::setMeshTetrahedra( - precice::string_view meshName, - precice::span vertices) {} - -void Participant::resetMesh(precice::string_view meshName) {} - -void Participant::writeData(precice::string_view meshName, - precice::string_view dataName, - precice::span vertices, - precice::span values) { - fake_read_write_buffer.clear(); - - for (const double value : values) { - fake_read_write_buffer.push_back(value); - } -} - -void Participant::readData(precice::string_view meshName, - precice::string_view dataName, - precice::span vertices, - double relativeReadTime, - precice::span values) const { - if (dataName.data() == fake_scalar_data_name) { - for (const int id : vertices) { - values[id] = fake_read_write_buffer[id]; - } - } else if (dataName.data() == fake_vector_data_name) { - for (const int id : vertices) { - for (int d = 0; d < fake_vector_data_dimensions; d++) { - const int linearized_id = fake_vector_data_dimensions * id + d; - values[linearized_id] = fake_read_write_buffer[linearized_id]; - } - } - } -} - -void Participant::writeAndMapData(precice::string_view meshName, - precice::string_view dataName, - precice::span coordinates, - precice::span values) { - fake_read_write_buffer.clear(); - - for (const double value : values) { - fake_read_write_buffer.push_back(value); - } -} - -void Participant::mapAndReadData(precice::string_view meshName, - precice::string_view dataName, - precice::span coordinates, - double relativeReadTime, - precice::span values) const { - std::copy(fake_read_write_buffer.begin(), fake_read_write_buffer.end(), - values.begin()); -} - -void Participant::setMeshAccessRegion( - precice::string_view meshName, - precice::span boundingBox) const { - assert(meshName == fake_mesh_name); - - for (std::size_t i = 0; i < fake_bounding_box.size(); i++) { - assert(boundingBox[i] == fake_bounding_box[i]); - } -} - -void Participant::getMeshVertexIDsAndCoordinates( - precice::string_view meshName, precice::span valueIndices, - precice::span coordinates) const { - assert(meshName == fake_mesh_name); - assert(valueIndices.size() == fake_ids.size()); - assert(coordinates.size() == fake_coordinates.size()); - - for (std::size_t i = 0; i < fake_ids.size(); i++) { - valueIndices[i] = fake_ids[i]; - } - for (std::size_t i = 0; i < fake_coordinates.size(); i++) { - coordinates[i] = fake_coordinates[i]; - } -} - -void Participant::writeGradientData( - precice::string_view meshName, precice::string_view dataName, - precice::span vertices, - precice::span gradients) { - fake_read_write_buffer.clear(); - for (const double gradient : gradients) { - fake_read_write_buffer.push_back(gradient); - } -} - -void Participant::startProfilingSection(precice::string_view sectionName) {} - -void Participant::stopLastProfilingSection() {} - -std::string getVersionInformation() { return fake_version; } - -} // namespace precice diff --git a/test/precice-config.xml b/test/precice-config.xml new file mode 100644 index 00000000..b47d4306 --- /dev/null +++ b/test/precice-config.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test_bindings_module.py b/test/test_bindings_module.py index 6ba49f35..0bd99080 100644 --- a/test/test_bindings_module.py +++ b/test/test_bindings_module.py @@ -1,252 +1,368 @@ +import os + import precice from unittest import TestCase import numpy as np from mpi4py import MPI +CONFIG_PATH = os.path.join(os.path.dirname(__file__), "precice-config.xml") + +# Names defined in test/precice-config.xml +PARTICIPANT_NAME = "test" +MESH_NAME = "FakeMesh" +MESH_DIMENSIONS = 3 +SCALAR_WRITE_DATA = "FakeScalarData" +SCALAR_READ_DATA = "FakeScalarReadData" +VECTOR_WRITE_DATA = "FakeVectorData" +VECTOR_READ_DATA = "FakeVectorReadData" +DIRECT_ACCESS_MESH = "PartnerMesh" +JIT_WRITE_DATA = "JitWriteData" +JIT_READ_DATA = "JitReadData" +BOUNDING_BOX = [0.0, 1.0, 0.0, 1.0, 0.0, 1.0] +# The universal preCICE mock synthesizes this many vertices for a received mesh +# after set_mesh_access_region() has been called (see extras/mock in the preCICE +# repository). +N_DIRECT_ACCESS_VERTICES = 10 + +TIME_WINDOW_SIZE = 1.0 +MAX_TIME_WINDOWS = 2 + class TestBindings(TestCase): """ Test suite to check correct behaviour of python bindings. + + The tests run against the universal preCICE mock (libpreciceMocked.so from + extras/mock in the preCICE repository), which is loaded via LD_PRELOAD. The + mock parses test/precice-config.xml and validates all API calls against it. + Data written via write_data is buffered by the mock and echoed back by + read_data. """ + def _participant(self): + return precice.Participant(PARTICIPANT_NAME, CONFIG_PATH, 0, 1) + + def _initialized_participant(self, n_vertices=3): + """ + Returns a participant with n_vertices vertices set on MESH_NAME and + initialize() already called, plus the corresponding vertex ids. + """ + participant = self._participant() + positions = np.random.rand(n_vertices, MESH_DIMENSIONS) + vertex_ids = participant.set_mesh_vertices(MESH_NAME, positions) + participant.initialize() + return participant, vertex_ids + + # construction and configuration + def test_constructor(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - self.assertTrue(True) + participant = self._participant() + self.assertIsNotNone(participant) def test_constructor_custom_mpi_comm(self): - participant = precice.Participant("test", "dummy.xml", 0, 1, MPI.COMM_WORLD) - self.assertTrue(True) + participant = precice.Participant( + PARTICIPANT_NAME, CONFIG_PATH, 0, 1, MPI.COMM_WORLD + ) + self.assertIsNotNone(participant) + + def test_constructor_unknown_participant(self): + with self.assertRaises(RuntimeError): + precice.Participant("UnknownParticipant", CONFIG_PATH, 0, 1) + + def test_constructor_missing_config(self): + with self.assertRaises(RuntimeError): + precice.Participant(PARTICIPANT_NAME, "missing-config.xml", 0, 1) def test_version(self): precice.__version__ + def test_get_version_information(self): + version_info = precice.get_version_information() + self.assertIn(b"precice-mock", version_info) + + # status queries + def test_get_mesh_dimensions(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - # TODO: it would be nice to be able to mock the output of the interface - # directly in the test, not in test/Participant.hpp - fake_mesh_dimension = 3 # compare to test/Participant.hpp, fake_mesh_dimension - # TODO: it would be nice to be able to mock the output of the interface - # directly in the test, not in test/Participant.hpp - self.assertEqual(fake_mesh_dimension, participant.get_mesh_dimensions("dummy")) + participant = self._participant() + self.assertEqual(MESH_DIMENSIONS, participant.get_mesh_dimensions(MESH_NAME)) + + def test_get_mesh_dimensions_unknown_mesh(self): + participant = self._participant() + with self.assertRaises(RuntimeError): + participant.get_mesh_dimensions("UnknownMesh") def test_get_data_dimensions(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_scalar_data_dimension = ( - 1 # compare to test/Participant.hpp, fake_scalar_data_dimension - ) + participant = self._participant() self.assertEqual( - fake_scalar_data_dimension, - participant.get_data_dimensions("dummy", "FakeScalarData"), - ) - fake_vector_data_dimension = ( - 3 # compare to test/Participant.hpp, fake_vector_data_dimension + 1, participant.get_data_dimensions(MESH_NAME, SCALAR_WRITE_DATA) ) self.assertEqual( - fake_vector_data_dimension, - participant.get_data_dimensions("dummy", "FakeVectorData"), - ) + MESH_DIMENSIONS, + participant.get_data_dimensions(MESH_NAME, VECTOR_WRITE_DATA), + ) + + def test_get_data_dimensions_unknown_data(self): + participant = self._participant() + with self.assertRaises(RuntimeError): + participant.get_data_dimensions(MESH_NAME, "UnknownData") + + def test_requires_initial_data(self): + participant = self._participant() + self.assertFalse(participant.requires_initial_data()) + + def test_requires_checkpoints(self): + # explicit coupling scheme: no checkpoints are required + participant, _ = self._initialized_participant() + self.assertFalse(participant.requires_writing_checkpoint()) + self.assertFalse(participant.requires_reading_checkpoint()) + + # steering methods + + def test_initialize_without_vertices_raises(self): + participant = self._participant() + with self.assertRaises(RuntimeError): + participant.initialize() + + def test_simulation_loop(self): + participant, vertex_ids = self._initialized_participant() + n_time_windows = 0 + while participant.is_coupling_ongoing(): + dt = participant.get_max_time_step_size() + self.assertEqual(TIME_WINDOW_SIZE, dt) + write_data = np.random.rand(len(vertex_ids)) + participant.write_data(MESH_NAME, SCALAR_WRITE_DATA, vertex_ids, write_data) + participant.advance(dt) + self.assertTrue(participant.is_time_window_complete()) + n_time_windows += 1 + self.assertEqual(MAX_TIME_WINDOWS, n_time_windows) + participant.finalize() + + def test_subcycling(self): + participant, _ = self._initialized_participant() + participant.advance(TIME_WINDOW_SIZE / 2) + self.assertFalse(participant.is_time_window_complete()) + participant.advance(TIME_WINDOW_SIZE / 2) + self.assertTrue(participant.is_time_window_complete()) + + def test_advance_exceeding_time_window_raises(self): + participant, _ = self._initialized_participant() + with self.assertRaises(RuntimeError): + participant.advance(2 * TIME_WINDOW_SIZE) + + # mesh access def test_requires_mesh_connectivity_for(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_bool = 0 # compare to output in test/SolverInterface.cpp - fake_mesh_name = "FakeMesh" - self.assertEqual( - fake_bool, participant.requires_mesh_connectivity_for(fake_mesh_name) - ) + participant = self._participant() + # the partner participant defines a projection-based mapping from + # FakeMesh, so connectivity is required for it + self.assertTrue(participant.requires_mesh_connectivity_for(MESH_NAME)) + self.assertFalse(participant.requires_mesh_connectivity_for(DIRECT_ACCESS_MESH)) def test_reset_mesh(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - participant.reset_mesh(fake_mesh_name) + participant, _ = self._initialized_participant() + participant.reset_mesh(MESH_NAME) + # after resetting, the mesh may be redefined from scratch + positions = np.random.rand(4, MESH_DIMENSIONS) + vertex_ids = participant.set_mesh_vertices(MESH_NAME, positions) + self.assertTrue(np.array_equal(np.arange(4), vertex_ids)) def test_set_mesh_vertices(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - n_fake_vertices = 3 # compare to test/SolverInterface.cpp, n_fake_vertices - positions = np.random.rand(n_fake_vertices, fake_dimension) - expected_output = np.array(range(n_fake_vertices)) - actual_output = participant.set_mesh_vertices(fake_mesh_name, positions) + participant = self._participant() + n_vertices = 3 + positions = np.random.rand(n_vertices, MESH_DIMENSIONS) + expected_output = np.array(range(n_vertices)) + actual_output = participant.set_mesh_vertices(MESH_NAME, positions) self.assertTrue(np.array_equal(expected_output, actual_output)) def test_set_mesh_vertices_empty(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - n_fake_vertices = 0 # compare to test/SolverInterface.cpp, n_fake_vertices - positions = np.zeros((n_fake_vertices, fake_dimension)) - expected_output = np.array(range(n_fake_vertices)) - actual_output = participant.set_mesh_vertices(fake_mesh_name, positions) - self.assertTrue(np.array_equal(expected_output, actual_output)) + participant = self._participant() + positions = np.zeros((0, MESH_DIMENSIONS)) + actual_output = participant.set_mesh_vertices(MESH_NAME, positions) + self.assertEqual(0, len(actual_output)) def test_set_mesh_vertices_list(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - n_fake_vertices = 3 # compare to test/SolverInterface.cpp, n_fake_vertices - positions = np.random.rand(n_fake_vertices, fake_dimension) + participant = self._participant() + n_vertices = 3 + positions = np.random.rand(n_vertices, MESH_DIMENSIONS) positions = list( list(positions[i, j] for j in range(positions.shape[1])) for i in range(positions.shape[0]) ) - expected_output = np.array(range(n_fake_vertices)) - actual_output = participant.set_mesh_vertices(fake_mesh_name, positions) + expected_output = np.array(range(n_vertices)) + actual_output = participant.set_mesh_vertices(MESH_NAME, positions) self.assertTrue(np.array_equal(expected_output, actual_output)) def test_set_mesh_vertices_empty_list(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) + participant = self._participant() positions = [] - n_fake_vertices = 0 - expected_output = np.array(range(n_fake_vertices)) - actual_output = participant.set_mesh_vertices(fake_mesh_name, positions) - self.assertTrue(np.array_equal(expected_output, actual_output)) + actual_output = participant.set_mesh_vertices(MESH_NAME, positions) + self.assertEqual(0, len(actual_output)) def test_set_mesh_vertices_tuple(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - n_fake_vertices = 3 # compare to test/SolverInterface.cpp, n_fake_vertices - positions = np.random.rand(n_fake_vertices, fake_dimension) + participant = self._participant() + n_vertices = 3 + positions = np.random.rand(n_vertices, MESH_DIMENSIONS) positions = tuple( tuple(positions[i, j] for j in range(positions.shape[1])) for i in range(positions.shape[0]) ) - expected_output = np.array(range(n_fake_vertices)) - actual_output = participant.set_mesh_vertices(fake_mesh_name, positions) + expected_output = np.array(range(n_vertices)) + actual_output = participant.set_mesh_vertices(MESH_NAME, positions) self.assertTrue(np.array_equal(expected_output, actual_output)) def test_set_mesh_vertices_empty_tuple(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) + participant = self._participant() positions = () - n_fake_vertices = 0 - expected_output = np.array(range(n_fake_vertices)) - actual_output = participant.set_mesh_vertices(fake_mesh_name, positions) - self.assertTrue(np.array_equal(expected_output, actual_output)) + actual_output = participant.set_mesh_vertices(MESH_NAME, positions) + self.assertEqual(0, len(actual_output)) def test_set_mesh_vertices_mixed(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - n_fake_vertices = 3 # compare to test/SolverInterface.cpp, n_fake_vertices - positions = np.random.rand(n_fake_vertices, fake_dimension) + participant = self._participant() + n_vertices = 3 + positions = np.random.rand(n_vertices, MESH_DIMENSIONS) positions = list( tuple(positions[i, j] for j in range(positions.shape[1])) for i in range(positions.shape[0]) ) - expected_output = np.array(range(n_fake_vertices)) - actual_output = participant.set_mesh_vertices(fake_mesh_name, positions) + expected_output = np.array(range(n_vertices)) + actual_output = participant.set_mesh_vertices(MESH_NAME, positions) self.assertTrue(np.array_equal(expected_output, actual_output)) + def test_set_mesh_vertices_after_initialize_raises(self): + participant, _ = self._initialized_participant() + positions = np.random.rand(3, MESH_DIMENSIONS) + with self.assertRaises(RuntimeError): + participant.set_mesh_vertices(MESH_NAME, positions) + def test_set_mesh_vertex(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - position = np.random.rand(fake_dimension) - vertex_id = participant.set_mesh_vertex(fake_mesh_name, position) - self.assertTrue(0 == vertex_id) - - def test_set_mesh_vertex_empty(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 0 # compare to test/SolverInterface.cpp, fake_dimensions - position = np.random.rand(fake_dimension) - vertex_id = participant.set_mesh_vertex(fake_mesh_name, position) - self.assertTrue(0 == vertex_id) + participant = self._participant() + position = np.random.rand(MESH_DIMENSIONS) + vertex_id = participant.set_mesh_vertex(MESH_NAME, position) + self.assertEqual(0, vertex_id) def test_set_mesh_vertex_list(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - position = list(np.random.rand(fake_dimension)) - vertex_id = participant.set_mesh_vertex(fake_mesh_name, position) - self.assertTrue(0 == vertex_id) - - def test_set_mesh_vertex_empty_list(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - position = [] - vertex_id = participant.set_mesh_vertex(fake_mesh_name, position) - self.assertTrue(0 == vertex_id) + participant = self._participant() + position = list(np.random.rand(MESH_DIMENSIONS)) + vertex_id = participant.set_mesh_vertex(MESH_NAME, position) + self.assertEqual(0, vertex_id) def test_set_mesh_vertex_tuple(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - position = tuple(np.random.rand(fake_dimension)) - vertex_id = participant.set_mesh_vertex(fake_mesh_name, position) - self.assertTrue(0 == vertex_id) - - def test_set_mesh_vertex_empty_tuple(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - position = () - vertex_id = participant.set_mesh_vertex(fake_mesh_name, position) - self.assertTrue(0 == vertex_id) + participant = self._participant() + position = tuple(np.random.rand(MESH_DIMENSIONS)) + vertex_id = participant.set_mesh_vertex(MESH_NAME, position) + self.assertEqual(0, vertex_id) + + def test_set_mesh_vertex_wrong_dimensions(self): + participant = self._participant() + # an empty position is rejected by the mock + with self.assertRaises(RuntimeError): + participant.set_mesh_vertex(MESH_NAME, []) + # a position of wrong dimensionality is rejected by the bindings + with self.assertRaises(AssertionError): + participant.set_mesh_vertex(MESH_NAME, np.random.rand(2)) def test_get_mesh_vertex_size(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - n_fake_vertices = 3 # compare to test/SolverInterface.cpp, n_fake_vertices - n_vertices = participant.get_mesh_vertex_size(fake_mesh_name) - self.assertTrue(n_fake_vertices == n_vertices) + participant = self._participant() + n_vertices = 3 + positions = np.random.rand(n_vertices, MESH_DIMENSIONS) + participant.set_mesh_vertices(MESH_NAME, positions) + self.assertEqual(n_vertices, participant.get_mesh_vertex_size(MESH_NAME)) + + # connectivity + + def test_set_mesh_edge(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(3, MESH_DIMENSIONS)) + participant.set_mesh_edge(MESH_NAME, 0, 1) + + def test_set_mesh_edge_invalid_vertex_raises(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(3, MESH_DIMENSIONS)) + with self.assertRaises(RuntimeError): + participant.set_mesh_edge(MESH_NAME, 0, 42) + + def test_set_mesh_edges(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(3, MESH_DIMENSIONS)) + vertices = np.array([[0, 1], [1, 2]]) + participant.set_mesh_edges(MESH_NAME, vertices) + + def test_set_mesh_edges_empty(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(3, MESH_DIMENSIONS)) + vertices = np.empty((0, 2), dtype=int) + participant.set_mesh_edges(MESH_NAME, vertices) + + def test_set_mesh_triangle(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(3, MESH_DIMENSIONS)) + participant.set_mesh_triangle(MESH_NAME, 0, 1, 2) + + def test_set_mesh_triangles(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(4, MESH_DIMENSIONS)) + vertices = np.array([[0, 1, 2], [1, 2, 3]]) + participant.set_mesh_triangles(MESH_NAME, vertices) + + def test_set_mesh_triangles_empty(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(3, MESH_DIMENSIONS)) + vertices = np.empty((0, 3), dtype=int) + participant.set_mesh_triangles(MESH_NAME, vertices) + + def test_set_mesh_quad(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(4, MESH_DIMENSIONS)) + participant.set_mesh_quad(MESH_NAME, 0, 1, 2, 3) + + def test_set_mesh_quads(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(5, MESH_DIMENSIONS)) + vertices = np.array([[0, 1, 2, 3], [1, 2, 3, 4]]) + participant.set_mesh_quads(MESH_NAME, vertices) + + def test_set_mesh_quads_empty(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(4, MESH_DIMENSIONS)) + vertices = np.empty((0, 4), dtype=int) + participant.set_mesh_quads(MESH_NAME, vertices) + + def test_set_mesh_tetrahedron(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(4, MESH_DIMENSIONS)) + participant.set_mesh_tetrahedron(MESH_NAME, 0, 1, 2, 3) + + def test_set_mesh_tetrahedra(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(5, MESH_DIMENSIONS)) + vertices = np.array([[0, 1, 2, 3], [1, 2, 3, 4]]) + participant.set_mesh_tetrahedra(MESH_NAME, vertices) + + # data access def test_read_write_block_scalar_data(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) + participant, vertex_ids = self._initialized_participant() write_data = np.array([3, 7, 8], dtype=np.double) - participant.write_data( - "FakeMesh", "FakeScalarData", np.array([0, 1, 2]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeScalarData", np.array([0, 1, 2]), dt - ) + participant.write_data(MESH_NAME, SCALAR_WRITE_DATA, vertex_ids, write_data) + dt = participant.get_max_time_step_size() + read_data = participant.read_data(MESH_NAME, SCALAR_READ_DATA, vertex_ids, dt) self.assertTrue(np.array_equal(write_data, read_data)) def test_read_write_block_scalar_data_single_float(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) + participant = self._participant() write_data = 8 with self.assertRaises(TypeError): - participant.write_data("FakeMesh", "FakeScalarData", 1, write_data) + participant.write_data(MESH_NAME, SCALAR_WRITE_DATA, 1, write_data) with self.assertRaises(TypeError): - participant.read_data("FakeMesh", "FakeScalarData", 1) + participant.read_data(MESH_NAME, SCALAR_READ_DATA, 1) def test_read_write_block_scalar_data_empty(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) + participant, _ = self._initialized_participant() write_data = np.array([]) - participant.write_data("FakeMesh", "FakeScalarData", [], write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeScalarData", [], dt) - self.assertTrue(len(read_data) == 0) + participant.write_data(MESH_NAME, SCALAR_WRITE_DATA, [], write_data) + dt = participant.get_max_time_step_size() + read_data = participant.read_data(MESH_NAME, SCALAR_READ_DATA, [], dt) + self.assertEqual(0, len(read_data)) def test_read_write_block_scalar_data_non_contiguous(self): """ @@ -254,81 +370,65 @@ def test_read_write_block_scalar_data_non_contiguous(self): Note: Check whether np.ndarray is contiguous via np.ndarray.flags. """ - participant = precice.Participant("test", "dummy.xml", 0, 1) + participant, vertex_ids = self._initialized_participant() dummy_array = np.random.rand(3, 3) write_data = dummy_array[:, 1] assert write_data.flags["C_CONTIGUOUS"] is False - participant.write_data( - "FakeMesh", "FakeScalarData", np.array([0, 1, 2]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeScalarData", np.array([0, 1, 2]), dt - ) + participant.write_data(MESH_NAME, SCALAR_WRITE_DATA, vertex_ids, write_data) + dt = participant.get_max_time_step_size() + read_data = participant.read_data(MESH_NAME, SCALAR_READ_DATA, vertex_ids, dt) self.assertTrue(np.array_equal(write_data, read_data)) def test_read_write_scalar_data(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) + participant, vertex_ids = self._initialized_participant() write_data = [3] - participant.write_data("FakeMesh", "FakeScalarData", [0], write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeScalarData", [0], dt) - self.assertTrue(np.array_equal(write_data, read_data)) - - def test_read_write_block_vector_data(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = np.array([[3, 7, 8], [7, 6, 5]], dtype=np.double) participant.write_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), write_data + MESH_NAME, SCALAR_WRITE_DATA, [vertex_ids[0]], write_data ) - dt = 1 + dt = participant.get_max_time_step_size() read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), dt + MESH_NAME, SCALAR_READ_DATA, [vertex_ids[0]], dt ) self.assertTrue(np.array_equal(write_data, read_data)) + def test_read_write_block_vector_data(self): + participant, vertex_ids = self._initialized_participant(n_vertices=2) + write_data = np.array([[3, 7, 8], [7, 6, 5]], dtype=np.double) + participant.write_data(MESH_NAME, VECTOR_WRITE_DATA, vertex_ids, write_data) + dt = participant.get_max_time_step_size() + read_data = participant.read_data(MESH_NAME, VECTOR_READ_DATA, vertex_ids, dt) + self.assertTrue(np.array_equal(write_data, read_data)) + def test_read_write_block_vector_data_empty(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) + participant, _ = self._initialized_participant() write_data = np.array([]) - participant.write_data("FakeMesh", "FakeVectorData", [], write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeVectorData", [], dt) - self.assertTrue(len(read_data) == 0) + participant.write_data(MESH_NAME, VECTOR_WRITE_DATA, [], write_data) + dt = participant.get_max_time_step_size() + read_data = participant.read_data(MESH_NAME, VECTOR_READ_DATA, [], dt) + self.assertEqual(0, len(read_data)) def test_read_write_block_vector_data_list(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [[3, 7, 8], [7, 6, 5]] - participant.write_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), dt - ) + participant, vertex_ids = self._initialized_participant(n_vertices=2) + write_data = [[3.0, 7.0, 8.0], [7.0, 6.0, 5.0]] + participant.write_data(MESH_NAME, VECTOR_WRITE_DATA, vertex_ids, write_data) + dt = participant.get_max_time_step_size() + read_data = participant.read_data(MESH_NAME, VECTOR_READ_DATA, vertex_ids, dt) self.assertTrue(np.array_equal(write_data, read_data)) def test_read_write_block_vector_data_tuple(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = ((3, 7, 8), (7, 6, 5)) - participant.write_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), dt - ) + participant, vertex_ids = self._initialized_participant(n_vertices=2) + write_data = ((3.0, 7.0, 8.0), (7.0, 6.0, 5.0)) + participant.write_data(MESH_NAME, VECTOR_WRITE_DATA, vertex_ids, write_data) + dt = participant.get_max_time_step_size() + read_data = participant.read_data(MESH_NAME, VECTOR_READ_DATA, vertex_ids, dt) self.assertTrue(np.array_equal(write_data, read_data)) def test_read_write_block_vector_data_mixed(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [(3, 7, 8), (7, 6, 5)] - participant.write_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), dt - ) + participant, vertex_ids = self._initialized_participant(n_vertices=2) + write_data = [(3.0, 7.0, 8.0), (7.0, 6.0, 5.0)] + participant.write_data(MESH_NAME, VECTOR_WRITE_DATA, vertex_ids, write_data) + dt = participant.get_max_time_step_size() + read_data = participant.read_data(MESH_NAME, VECTOR_READ_DATA, vertex_ids, dt) self.assertTrue(np.array_equal(write_data, read_data)) def test_read_write_block_vector_data_non_contiguous(self): @@ -337,39 +437,50 @@ def test_read_write_block_vector_data_non_contiguous(self): Note: Check whether np.ndarray is contiguous via np.ndarray.flags. """ - participant = precice.Participant("test", "dummy.xml", 0, 1) size = 6 + participant, vertex_ids = self._initialized_participant(n_vertices=size) dummy_array = np.random.rand(size, 5) write_data = dummy_array[:, 1:4] assert write_data.flags["C_CONTIGUOUS"] is False - vertex_ids = np.arange(size) - participant.write_data("FakeMesh", "FakeVectorData", vertex_ids, write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeVectorData", vertex_ids, dt) + participant.write_data(MESH_NAME, VECTOR_WRITE_DATA, vertex_ids, write_data) + dt = participant.get_max_time_step_size() + read_data = participant.read_data(MESH_NAME, VECTOR_READ_DATA, vertex_ids, dt) self.assertTrue(np.array_equal(write_data, read_data)) def test_read_write_vector_data(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) + participant, vertex_ids = self._initialized_participant() write_data = np.array([[0, 1, 2]], dtype=np.double) - participant.write_data("FakeMesh", "FakeVectorData", [0], write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeVectorData", [0], dt) + participant.write_data( + MESH_NAME, VECTOR_WRITE_DATA, [vertex_ids[0]], write_data + ) + dt = participant.get_max_time_step_size() + read_data = participant.read_data( + MESH_NAME, VECTOR_READ_DATA, [vertex_ids[0]], dt + ) self.assertTrue(np.array_equal(write_data, read_data)) def test_read_write_vector_data_list(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [[0, 1, 2]] - participant.write_data("FakeMesh", "FakeVectorData", [0], write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeVectorData", [0], dt) + participant, vertex_ids = self._initialized_participant() + write_data = [[0.0, 1.0, 2.0]] + participant.write_data( + MESH_NAME, VECTOR_WRITE_DATA, [vertex_ids[0]], write_data + ) + dt = participant.get_max_time_step_size() + read_data = participant.read_data( + MESH_NAME, VECTOR_READ_DATA, [vertex_ids[0]], dt + ) self.assertTrue(np.array_equal(write_data, read_data)) def test_read_write_vector_data_tuple(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [(1, 2, 3)] - participant.write_data("FakeMesh", "FakeVectorData", [0], write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeVectorData", [0], dt) + participant, vertex_ids = self._initialized_participant() + write_data = [(1.0, 2.0, 3.0)] + participant.write_data( + MESH_NAME, VECTOR_WRITE_DATA, [vertex_ids[0]], write_data + ) + dt = participant.get_max_time_step_size() + read_data = participant.read_data( + MESH_NAME, VECTOR_READ_DATA, [vertex_ids[0]], dt + ) self.assertTrue(np.array_equal(write_data, read_data)) def test_read_write_vector_data_non_contiguous(self): @@ -378,343 +489,182 @@ def test_read_write_vector_data_non_contiguous(self): Note: Check whether np.ndarray is contiguous via np.ndarray.flags. """ - participant = precice.Participant("test", "dummy.xml", 0, 1) + participant, vertex_ids = self._initialized_participant() dummy_array = np.random.rand(3, 3) write_data = dummy_array[:, 1] assert write_data.flags["C_CONTIGUOUS"] is False write_data = [write_data] - participant.write_data("FakeMesh", "FakeVectorData", [0], write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeVectorData", [0], dt) + participant.write_data( + MESH_NAME, VECTOR_WRITE_DATA, [vertex_ids[0]], write_data + ) + dt = participant.get_max_time_step_size() + read_data = participant.read_data( + MESH_NAME, VECTOR_READ_DATA, [vertex_ids[0]], dt + ) self.assertTrue(np.array_equal(write_data, read_data)) + def test_write_data_not_configured_for_writing_raises(self): + participant, vertex_ids = self._initialized_participant() + write_data = np.random.rand(len(vertex_ids)) + with self.assertRaises(RuntimeError): + participant.write_data(MESH_NAME, SCALAR_READ_DATA, vertex_ids, write_data) + + def test_read_data_not_configured_for_reading_raises(self): + participant, vertex_ids = self._initialized_participant() + dt = participant.get_max_time_step_size() + with self.assertRaises(RuntimeError): + participant.read_data(MESH_NAME, SCALAR_WRITE_DATA, vertex_ids, dt) + + def test_write_data_invalid_vertex_id_raises(self): + participant, vertex_ids = self._initialized_participant() + write_data = np.random.rand(len(vertex_ids)) + invalid_vertex_ids = np.copy(vertex_ids) + invalid_vertex_ids[-1] = 42 + with self.assertRaises(RuntimeError): + participant.write_data( + MESH_NAME, SCALAR_WRITE_DATA, invalid_vertex_ids, write_data + ) + + def test_read_data_before_initialize_raises(self): + participant = self._participant() + vertex_ids = participant.set_mesh_vertices( + MESH_NAME, np.random.rand(3, MESH_DIMENSIONS) + ) + with self.assertRaises(RuntimeError): + participant.read_data(MESH_NAME, SCALAR_READ_DATA, vertex_ids, 0.0) + + # just-in-time mapping + def test_jit_mapping(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [1, 2, 3] + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(3, MESH_DIMENSIONS)) + participant.set_mesh_access_region(DIRECT_ACCESS_MESH, BOUNDING_BOX) + participant.initialize() + n_coordinates = 4 + coordinates = np.random.rand(n_coordinates, MESH_DIMENSIONS) + write_data = np.random.rand(n_coordinates) participant.write_and_map_data( - "FakeMesh", "FakeScalarData", [0, 1, 2], write_data + DIRECT_ACCESS_MESH, JIT_WRITE_DATA, coordinates, write_data ) - dt = 1 + dt = participant.get_max_time_step_size() read_data = participant.map_and_read_data( - "FakeMesh", "FakeScalarData", [0, 1, 2], dt + DIRECT_ACCESS_MESH, JIT_READ_DATA, coordinates, dt ) + # the mock buffers data written via write_and_map_data and echoes it back self.assertTrue(np.array_equal(write_data, read_data)) - def test_get_version_information(self): - version_info = precice.get_version_information() - fake_version_info = b"dummy" # compare to test/SolverInterface.cpp - self.assertEqual(version_info, fake_version_info) + def test_jit_mapping_without_access_region_raises(self): + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(3, MESH_DIMENSIONS)) + participant.initialize() + coordinates = np.random.rand(4, MESH_DIMENSIONS) + write_data = np.random.rand(4) + with self.assertRaises(RuntimeError): + participant.write_and_map_data( + DIRECT_ACCESS_MESH, JIT_WRITE_DATA, coordinates, write_data + ) + + # direct access def test_set_mesh_access_region(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - fake_bounding_box = np.arange(fake_dimension * 2) - participant.set_mesh_access_region(fake_mesh_name, fake_bounding_box) + participant = self._participant() + participant.set_mesh_access_region(DIRECT_ACCESS_MESH, BOUNDING_BOX) + + def test_set_mesh_access_region_on_provided_mesh_raises(self): + participant = self._participant() + with self.assertRaises(RuntimeError): + participant.set_mesh_access_region(MESH_NAME, BOUNDING_BOX) def test_get_mesh_vertex_ids_and_coordinates(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = ( - "FakeMesh" # compare to test/SolverInterface.cpp, fake_mesh_name - ) - n_fake_vertices = 3 # compare to test/SolverInterface.cpp, n_fake_vertices - fake_dimension = 3 # compare to test/SolverInterface.cpp, fake_dimensions - vertex_ids = np.arange(n_fake_vertices) - coordinates = np.zeros((n_fake_vertices, fake_dimension)) - for i in range(n_fake_vertices): - coordinates[i, 0] = i * fake_dimension - coordinates[i, 1] = i * fake_dimension + 1 - coordinates[i, 2] = i * fake_dimension + 2 - fake_ids, fake_coordinates = participant.get_mesh_vertex_ids_and_coordinates( - fake_mesh_name - ) - self.assertTrue(np.array_equal(fake_ids, vertex_ids)) - self.assertTrue(np.array_equal(fake_coordinates, coordinates)) + participant = self._participant() + participant.set_mesh_vertices(MESH_NAME, np.random.rand(3, MESH_DIMENSIONS)) + participant.set_mesh_access_region(DIRECT_ACCESS_MESH, BOUNDING_BOX) + participant.initialize() + vertex_ids, coordinates = participant.get_mesh_vertex_ids_and_coordinates( + DIRECT_ACCESS_MESH + ) + self.assertTrue(np.array_equal(np.arange(N_DIRECT_ACCESS_VERTICES), vertex_ids)) + self.assertEqual((N_DIRECT_ACCESS_VERTICES, MESH_DIMENSIONS), coordinates.shape) + # the synthesized vertices lie inside the access region + lower_bounds = np.array(BOUNDING_BOX[0::2]) + upper_bounds = np.array(BOUNDING_BOX[1::2]) + self.assertTrue(np.all(coordinates >= lower_bounds)) + self.assertTrue(np.all(coordinates <= upper_bounds)) + + # gradient data def test_requires_gradient_data_for(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_bool = 0 # compare to output in test/SolverInterface.cpp - fake_mesh_name = "FakeMesh" - fake_data_name = "FakeName" - self.assertEqual( - fake_bool, - participant.requires_gradient_data_for(fake_mesh_name, fake_data_name), - ) - - def test_write_block_scalar_gradient_data(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = np.array([[0, 1, 2], [6, 7, 8], [9, 10, 11]], dtype=np.double) - participant.write_gradient_data( - "FakeMesh", "FakeScalarData", np.array([0, 1, 2]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeScalarData", np.array(range(9)), dt - ) + participant = self._participant() + # a gradient mapping is configured for write data on MESH_NAME self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) + participant.requires_gradient_data_for(MESH_NAME, SCALAR_WRITE_DATA) ) - - def test_write_block_scalar_gradient_data_single_float(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_dimension = 3 - n_fake_vertices = 1 - vertex_ids = np.arange(n_fake_vertices) - write_data = np.random.rand(n_fake_vertices, fake_dimension) - participant.write_gradient_data( - "FakeMesh", "FakeScalarData", vertex_ids, write_data + self.assertTrue( + participant.requires_gradient_data_for(MESH_NAME, VECTOR_WRITE_DATA) ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", - "FakeScalarData", - np.arange(n_fake_vertices * fake_dimension), - dt, + # read data never requires gradient data + self.assertFalse( + participant.requires_gradient_data_for(MESH_NAME, SCALAR_READ_DATA) ) - self.assertTrue(np.array_equal(write_data.flatten(), read_data)) - def test_write_block_scalar_gradient_data_empty(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = np.array([]) - participant.write_gradient_data("FakeMesh", "FakeScalarData", [], write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeScalarData", [], dt) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) + def test_requires_gradient_data_for_unknown_data_raises(self): + participant = self._participant() + with self.assertRaises(RuntimeError): + participant.requires_gradient_data_for(MESH_NAME, "UnknownData") + + def test_write_block_scalar_gradient_data(self): + participant, vertex_ids = self._initialized_participant() + gradients = np.random.rand(len(vertex_ids), MESH_DIMENSIONS) + participant.write_gradient_data( + MESH_NAME, SCALAR_WRITE_DATA, vertex_ids, gradients ) def test_write_block_scalar_gradient_data_non_contiguous(self): """ - Tests behavior of solver interface, if a non contiguous array is passed to the interface. + Tests behaviour of solver interface, if a non contiguous array is passed to the interface. + Note: Check whether np.ndarray is contiguous via np.ndarray.flags. """ - participant = precice.Participant("test", "dummy.xml", 0, 1) + participant, vertex_ids = self._initialized_participant() dummy_array = np.random.rand(3, 9) - write_data = dummy_array[:, 3:6] - assert write_data.flags["C_CONTIGUOUS"] is False + gradients = dummy_array[:, 3:6] + assert gradients.flags["C_CONTIGUOUS"] is False participant.write_gradient_data( - "FakeMesh", "FakeScalarData", np.array([0, 1, 2]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeScalarData", np.array(range(9)), dt - ) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) + MESH_NAME, SCALAR_WRITE_DATA, vertex_ids, gradients ) - def test_write_scalar_gradient_data(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_dimension = 3 - write_data = [np.random.rand(fake_dimension)] - participant.write_gradient_data("FakeMesh", "FakeScalarData", [0], write_data) - dt = 1 - # Gradient data is essential vector data, hence the appropriate data name is used here - read_data = participant.read_data("FakeMesh", "FakeVectorData", [0], dt) - self.assertTrue(np.array_equiv(write_data, read_data)) + def test_write_gradient_data_empty(self): + participant, _ = self._initialized_participant() + gradients = np.array([]) + participant.write_gradient_data(MESH_NAME, SCALAR_WRITE_DATA, [], gradients) def test_write_block_vector_gradient_data(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_dimension = 3 - n_fake_vertices = 4 - vertex_ids = np.arange(n_fake_vertices) - write_data = np.random.rand(n_fake_vertices, fake_dimension * fake_dimension) + participant, vertex_ids = self._initialized_participant(n_vertices=4) + gradients = np.random.rand(len(vertex_ids), MESH_DIMENSIONS * MESH_DIMENSIONS) participant.write_gradient_data( - "FakeMesh", "FakeVectorData", vertex_ids, write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", - "FakeVectorData", - np.array(range(n_fake_vertices * fake_dimension)), - dt, + MESH_NAME, VECTOR_WRITE_DATA, vertex_ids, gradients ) - self.assertTrue(np.array_equiv(write_data.flatten(), read_data.flatten())) - - def test_write_block_vector_gradient_data_empty(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = np.array([]) - participant.write_gradient_data("FakeMesh", "FakeVectorData", [], write_data) - dt = 1 - read_data = participant.read_data("FakeMesh", "FakeVectorData", [], dt) - self.assertTrue(len(read_data) == 0) def test_write_block_vector_gradient_data_list(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [ + participant, vertex_ids = self._initialized_participant(n_vertices=2) + gradients = [ [3.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0], [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 6.0, 5.0], ] participant.write_gradient_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array(range(6)), dt - ) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) - ) - - def test_write_block_vector_gradient_data_tuple(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = ( - (1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 3.0, 7.0, 8.0), - (1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 6.0, 5.0), - ) - participant.write_gradient_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array(range(6)), dt - ) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) - ) - - def test_write_block_vector_gradient_data_mixed(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [ - (1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 3.0, 7.0, 8.0), - (4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 7.0, 6.0, 5.0), - ] - participant.write_gradient_data( - "FakeMesh", "FakeVectorData", np.array([0, 1]), write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array(range(6)), dt - ) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) - ) - - def test_write_block_vector_gradient_data_non_contiguous(self): - """ - Tests behavior of solver interface, if a non contiguous array is passed to the interface. - Note: Check whether np.ndarray is contiguous via np.ndarray.flags. - """ - participant = precice.Participant("test", "dummy.xml", 0, 1) - dummy_array = np.random.rand(3, 15) - write_data = dummy_array[:, 2:11] - assert write_data.flags["C_CONTIGUOUS"] is False - vertex_ids = np.arange(3) - participant.write_gradient_data( - "FakeMesh", "FakeVectorData", vertex_ids, write_data - ) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array(range(9)), dt - ) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) - ) - - def test_write_vector_gradient_data(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [np.arange(0, 9, dtype=np.double)] - participant.write_gradient_data("FakeMesh", "FakeVectorData", [0], write_data) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array(range(3)), dt - ) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) - ) - - def test_write_vector_gradient_data_list(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]] - participant.write_gradient_data("FakeMesh", "FakeVectorData", [0], write_data) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array(range(3)), dt - ) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) + MESH_NAME, VECTOR_WRITE_DATA, vertex_ids, gradients ) def test_write_vector_gradient_data_tuple(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - write_data = [(1.0, 2.0, 3.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0)] - participant.write_gradient_data("FakeMesh", "FakeVectorData", [0], write_data) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array(range(3)), dt - ) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) - ) - - def test_write_vector_gradient_data_non_contiguous(self): - """ - Tests behavior of solver interface, if a non contiguous array is passed to the interface. - Note: Check whether np.ndarray is contiguous via np.ndarray.flags. - """ - participant = precice.Participant("test", "dummy.xml", 0, 1) - dummy_array = np.random.rand(9, 3) - write_data = dummy_array[:, 1] - assert write_data.flags["C_CONTIGUOUS"] is False - write_data = [write_data] - participant.write_gradient_data("FakeMesh", "FakeVectorData", [0], write_data) - dt = 1 - read_data = participant.read_data( - "FakeMesh", "FakeVectorData", np.array(range(3)), dt - ) - self.assertTrue( - np.array_equiv(np.array(write_data).flatten(), read_data.flatten()) + participant, vertex_ids = self._initialized_participant() + gradients = [(1.0, 2.0, 3.0, 9.0, 8.0, 7.0, 6.0, 5.0, 4.0)] + participant.write_gradient_data( + MESH_NAME, VECTOR_WRITE_DATA, [vertex_ids[0]], gradients ) - def test_set_mesh_edge(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - participant.set_mesh_edge(fake_mesh_name, 0, 1) - - def test_set_mesh_edges(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - vertices = np.array([[0, 1], [1, 2]]) - participant.set_mesh_edges(fake_mesh_name, vertices) - - def test_set_mesh_edges_empty(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - vertices = np.empty((0, 2), dtype=int) - participant.set_mesh_edges(fake_mesh_name, vertices) - - def test_set_mesh_triangle(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - participant.set_mesh_triangle(fake_mesh_name, 0, 1, 2) - - def test_set_mesh_triangles(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - vertices = np.array([[0, 1, 2], [1, 2, 3]]) - participant.set_mesh_triangles(fake_mesh_name, vertices) - - def test_set_mesh_triangles_empty(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - vertices = np.empty((0, 3), dtype=int) - participant.set_mesh_triangles(fake_mesh_name, vertices) + # profiling - def test_set_mesh_quad(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - participant.set_mesh_quad(fake_mesh_name, 0, 1, 2, 3) - - def test_set_mesh_quads(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - vertices = np.array([[0, 1, 2, 3], [1, 2, 3, 4]]) - participant.set_mesh_quads(fake_mesh_name, vertices) - - def test_set_mesh_quads_empty(self): - participant = precice.Participant("test", "dummy.xml", 0, 1) - fake_mesh_name = "FakeMesh" - vertices = np.empty((0, 4), dtype=int) - participant.set_mesh_quads(fake_mesh_name, vertices) + def test_profiling_section(self): + participant = self._participant() + participant.start_profiling_section("my-section") + participant.stop_last_profiling_section() diff --git a/tox.ini b/tox.ini index 34b02701..ab25c814 100644 --- a/tox.ini +++ b/tox.ini @@ -4,12 +4,21 @@ env_list = minversion = 4.23.2 [testenv] -description = run the tests with pytest +description = run the tests with pytest against the universal preCICE mock package = sdist wheel_build_env = .pkg +; PYPRECICE_MOCKED makes setup.py skip linking libprecice; the preCICE symbols +; are provided at runtime by preloading the universal preCICE mock +; (libpreciceMocked.so, built from extras/mock in the preCICE repository). +; Point PRECICE_MOCK_LIB to that library before running tox. setenv = PYPRECICE_MOCKED = True + LD_PRELOAD = {env:PRECICE_MOCK_LIB:} +passenv = + PRECICE_MOCK_LIB deps = pytest>=6 +commands_pre = + python -c "import os, sys; lib = os.environ.get('LD_PRELOAD', ''); sys.exit(None if os.path.isfile(lib) else 'PRECICE_MOCK_LIB must point to libpreciceMocked.so (the universal preCICE mock), which is preloaded into the test run.')" commands = pytest {tty:--color=yes} {posargs}