diff --git a/precice.pyx b/precice.pyx index 50358d68..98514b9a 100644 --- a/precice.pyx +++ b/precice.pyx @@ -26,10 +26,38 @@ cdef bytes convert(s): cdef class Interface: - # construction and configuration - # constructor + """ + Main Application Programming Interface of preCICE. + To adapt a solver to preCICE, follow the following main structure: + - Create an object of SolverInterface with Interface() + - Configure the object with Interface::configure() + - Initialize preCICE with Interface::initialize() + - Advance to the next (time)step with Interface::advance() + - Finalize preCICE with Interface::finalize() + - We use solver, simulation code, and participant as synonyms. + - The preferred name in the documentation is participant. + """ def __cinit__ (self, solver_name, configuration_file_name, solver_process_index, solver_process_size, communicator=None): + """ + Constructor of Interface class. + + Parameters + ---------- + solver_name : string + Name of the solver + configuration_file_name : string + Name of the preCICE config file + solver_process_index : int + Rank of the process + solver_process_size : int + Size of the process + + Returns + ------- + SolverInterface : object + Object pointing to the defined coupling interface + """ cdef void* communicator_ptr if communicator: communicator_ptr = communicator @@ -38,84 +66,364 @@ cdef class Interface: self.thisptr = new SolverInterface.SolverInterface (convert(solver_name), convert(configuration_file_name), solver_process_index, solver_process_size) pass - # destructor def __dealloc__ (self): + """ + Destructor of Interface class + """ del self.thisptr # steering methods - # initialize + def initialize (self): + """ + Fully initializes preCICE. + This function handles: + - Parallel communication to the coupling partner/s is setup. + - Meshes are exchanged between coupling partners and the parallel partitions are created. + - [Serial Coupling Scheme] If the solver is not starting the simulation, coupling data is received + from the coupling partner's first computation. + + Returns + ------- + max_timestep : double + Maximum length of first timestep to be computed by the solver. + """ return self.thisptr.initialize () - # initialize data def initialize_data (self): + """ + Initializes coupling data. The starting values for coupling data are zero by default. + To provide custom values, first set the data using the Data Access methods and + call this method to finally exchange the data. + + Serial Coupling Scheme: Only the first participant has to call this method, the second participant + receives the values on calling initialize(). + + Parallel Coupling Scheme: + - Values in both directions are exchanged. + - Both participants need to call initializeData(). + + Notes + ----- + Previous calls: + initialize() has been called successfully. + The action WriteInitialData is required + advance() has not yet been called. + finalize() has not yet been called. + + Tasks completed: + Initial coupling data was exchanged. + """ self.thisptr.initializeData () - # advance in time + def advance (self, double computed_timestep_length): + """ + Advances preCICE after the solver has computed one timestep. + + Parameters + ---------- + computed_timestep_length : double + Length of timestep used by the solver. + + Returns + ------- + max_timestep : double + Maximum length of next timestep to be computed by solver. + + Notes + ----- + Previous calls: + initialize() has been called successfully. + The solver has computed one timestep. + The solver has written all coupling data. + finalize() has not yet been called. + + Tasks completed: + Coupling data values specified in the configuration are exchanged. + Coupling scheme state (computed time, computed timesteps, ...) is updated. + The coupling state is logged. + Configured data mapping schemes are applied. + [Second Participant] Configured post processing schemes are applied. + Meshes with data are exported to files if configured. + """ return self.thisptr.advance (computed_timestep_length) - # finalize preCICE + def finalize (self): + """ + Finalizes preCICE. + + Notes + ----- + Previous calls: + initialize() has been called successfully. + + Tasks completed: + Communication channels are closed. + Meshes and data are deallocated. + """ self.thisptr.finalize () # status queries - # get dimensions + def get_dimensions (self): + """ + Returns the number of spatial dimensions configured. Currently, two and three dimensional problems + can be solved using preCICE. The dimension is specified in the XML configuration. + + Returns + ------- + dimension : int + The configured dimension. + """ return self.thisptr.getDimensions () - # check if coupling is going on + def is_coupling_ongoing (self): + """ + Checks if the coupled simulation is still ongoing. + A coupling is ongoing as long as + - the maximum number of timesteps has not been reached, and + - the final time has not been reached. + The user should call finalize() after this function returns false. + + Returns + ------- + tag : bool + Whether the coupling is ongoing. + + Notes + ----- + Previous calls: + initialize() has been called successfully. + """ return self.thisptr.isCouplingOngoing () - # check if data is available to be read + def is_read_data_available (self): + """ + Checks if new data to be read is available. Data is classified to be new, if it has been received + while calling initialize() and before calling advance(), or in the last call of advance(). + This is always true, if a participant does not make use of subcycling, i.e. choosing smaller + timesteps than the limits returned in intitialize() and advance(). + + It is allowed to read data even if this function returns false. This is not recommended + due to performance reasons. Use this function to prevent unnecessary reads. + + Returns + ------- + tag : bool + Whether new data is available to be read. + + Notes + ----- + Previous calls: + initialize() has been called successfully. + """ return self.thisptr.isReadDataAvailable () - # check if write data is needed + def is_write_data_required (self, double computed_timestep_length): + """ + Checks if new data has to be written before calling advance(). + This is always true, if a participant does not make use of subcycling, i.e. choosing smaller + timesteps than the limits returned in intitialize() and advance(). + + It is allowed to write data even if this function returns false. This is not recommended + due to performance reasons. Use this function to prevent unnecessary writes. + + Parameters + ---------- + computed_timestep_length : double + Length of timestep used by the solver. + + Returns + ------- + tag : bool + Whether new data has to be written. + + Notes + ----- + Previous calls: + initialize() has been called successfully. + """ return self.thisptr.isWriteDataRequired (computed_timestep_length) - # check if time-window is complete + def is_time_window_complete (self): + """ + Checks if the current coupling timewindow is completed. + The following reasons require several solver time steps per coupling time step: + - A solver chooses to perform subcycling. + - An implicit coupling timestep iteration is not yet converged. + + Returns + ------- + tag : bool + Whether the timestep is complete. + + Notes + ----- + Previous calls: + initialize() has been called successfully. + """ return self.thisptr.isTimeWindowComplete () - # returns whether the solver has to evaluate the surrogate model representation + def has_to_evaluate_surrogate_model (self): + """ + Returns whether the solver has to evaluate the surrogate model representation. + The solver may still have to evaluate the fine model representation. + DEPRECATED: Only necessary for deprecated manifold mapping. + + Returns + ------- + tag : bool + Whether the surrogate model has to be evaluated. + """ return self.thisptr.hasToEvaluateSurrogateModel () - # checks if the solver has to evaluate the fine model representation + def has_to_evaluate_fine_model (self): + """ + Checks if the solver has to evaluate the fine model representation. + The solver may still have to evaluate the surrogate model representation. + DEPRECATED: Only necessary for deprecated manifold mapping. + + Returns + ------- + tag : bool + Whether the fine model has to be evaluated. + """ return self.thisptr.hasToEvaluateFineModel () # action methods - # check if action is needed + def is_action_required (self, action): + """ + Checks if the provided action is required. + Some features of preCICE require a solver to perform specific actions, in order to be + in valid state for a coupled simulation. A solver is made eligible to use those features, + by querying for the required actions, performing them on demand, and calling markActionfulfilled() + to signalize preCICE the correct behavior of the solver. + + Parameters + ---------- + action : preCICE action + Name of the action. + + Returns + ------- + tag : bool + Returns True if action is required. + """ return self.thisptr.isActionRequired (action) - # notify precice of action being fulfilled + def mark_action_fulfilled (self, action): + """ + Indicates preCICE that a required action has been fulfilled by a solver. + + Parameters + ---------- + action : preCICE action + Name of the action. + + Notes + ----- + Previous calls: + The solver fulfilled the specified action. + """ self.thisptr.markActionFulfilled (action) # mesh access - # hasMesh + def has_mesh(self, mesh_name): + """ + Checks if the mesh with the given name is used by a solver. + + Parameters + ---------- + mesh_name : string + Name of the mesh. + + Returns + ------- + tag : bool + Returns true is the mesh is used. + """ return self.thisptr.hasMesh (convert(mesh_name)) - # get mesh ID + def get_mesh_id (self, mesh_name): + """ + Returns the ID belonging to the mesh with given name. + + Parameters + ---------- + mesh_name : string + Name of the mesh. + + Returns + ------- + id : int + ID of the corresponding mesh. + """ return self.thisptr.getMeshID (convert(mesh_name)) - # get mesh IDs + def get_mesh_ids (self): + """ + Returns the ID-set of all used meshes by this participant. + + Returns + ------- + id_array : numpy.array + Numpy array containing all IDs. + """ return self.thisptr.getMeshIDs () - # returns a handle to a created mesh + def get_mesh_handle(self, mesh_name): + """ + Returns a handle to a created mesh. + WARNING: This function is not yet available for the Python bindings + + Parameters + ---------- + mesh_name : string + Name of the mesh. + + Returns + ------- + tag : object + Handle to the mesh. + """ raise Exception("The API method get_mesh_handle is not yet available for the Python bindings.") - # creates a mesh vertex + def set_mesh_vertex(self, mesh_id, position): + """ + Creates a mesh vertex + + Parameters + ---------- + mesh_id : int + ID of the mesh to add the vertex to. + position : array_like + The coordinates of the vertex. + + Returns + ------- + vertex_id : int + ID of the vertex which is set. + + Notes + ----- + Previous calls: + Count of available elements at position matches the configured dimension + """ if not isinstance(position, np.ndarray): position = np.asarray(position) dimensions = position.size @@ -124,12 +432,64 @@ cdef class Interface: vertex_id = self.thisptr.setMeshVertex(mesh_id, _position.data) return vertex_id - # returns the number of vertices of a mesh def get_mesh_vertex_size (self, mesh_id): + """ + Returns the number of vertices of a mesh + + Parameters + ---------- + mesh_id : int + ID of the mesh. + + Returns + ------- + sum : int + Number of vertices of the mesh. + """ return self.thisptr.getMeshVertexSize(mesh_id) - # creates multiple mesh vertices def set_mesh_vertices (self, mesh_id, positions): + """ + Creates multiple mesh vertices + + Parameters + ---------- + mesh_id : int + ID of the mesh to add the vertices to. + positions : array_like + The coordinates of the vertices in a numpy array [N x D] where + N = number of vertices and D = dimensions of geometry. + + Returns + ------- + ids : numpy.array + IDs of the created vertices. + + Notes + ----- + Previous calls: + initialize() has not yet been called + count of available elements at positions matches the configured dimension * size + count of available elements at ids matches size + + Examples + -------- + Set mesh vertices for a 2D problem with 5 mesh vertices. + + >>> mesh_id = "my_mesh" + >>> positions = np.array([[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]) + >>> positions.shape + (5, 2) + >>> precice.set_mesh_vertices(mesh_id, positions) + + Set mesh vertices for a 3D problem with 5 mesh vertices. + + >>> mesh_id = "my_mesh" + >>> positions = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4], [5, 5, 5]]) + >>> positions.shape + (5, 3) + >>> precice.set_mesh_vertices(mesh_id, positions) + """ if not isinstance(positions, np.ndarray): positions = np.asarray(positions) size, dimensions = positions.shape @@ -139,16 +499,97 @@ cdef class Interface: self.thisptr.setMeshVertices (mesh_id, size, _positions.data, _ids.data) return _ids - # get vertex positions for multiple vertex ids from a given mesh def get_mesh_vertices(self, mesh_id, ids): + """ + Get vertex positions for multiple vertex ids from a given mesh + + Parameters + ---------- + mesh_id : int + ID of the mesh to read the vertices from. + ids : array_like + IDs of the vertices to lookup. + + Returns + ------- + positions : numpy.ndarray + The coordinates of the vertices in a numpy array [N x D] where + N = number of vertices and D = dimensions of geometry + + Notes + ----- + Previous calls: + count of available elements at positions matches the configured dimension * size + count of available elements at ids matches size + + Examples + -------- + Return data structure for a 2D problem with 5 vertices: + >>> mesh_id = "my_mesh" + >>> ids = [1, 2, 3, 4, 5] + >>> positions = precice.get_mesh_vertices(mesh_id, ids) + >>> positions.shape + (5, 2) + + Return data structure for a 3D problem with 5 vertices: + >>> mesh_id = "my_mesh" + >>> ids = [1, 2, 3, 4, 5] + >>> positions = precice.get_mesh_vertices(mesh_id, ids) + >>> positions.shape + (5, 3) + """ cdef np.ndarray[int, ndim=1] _ids = np.ascontiguousarray(ids, dtype=np.int32) size = _ids.size cdef np.ndarray[double, ndim=1] _positions = np.empty(size * self.get_dimensions(), dtype=np.double) self.thisptr.getMeshVertices (mesh_id, size, _ids.data, _positions.data) return _positions.reshape((size, self.get_dimensions())) - # gets mesh vertex IDs from positions def get_mesh_vertex_ids_from_positions (self, mesh_id, positions): + """ + Gets mesh vertex IDs from positions. + prefer to reuse the IDs returned from calls to set_mesh_vertex() and set_mesh_vertices(). + + Parameters + ---------- + mesh_id : int + ID of the mesh to retrieve positions from. + positions : numpy.ndarray + The coordinates of the vertices. Coordinates of vertices are stored in a + numpy array [N x D] where N = number of vertices and D = dimensions of geometry + + Returns + ------- + ids : numpy.array + IDs of mesh vertices. + + Notes + ----- + Previous calls: + count of available elements at positions matches the configured dimension * size + count of available elements at ids matches size + + Examples + -------- + Get mesh vertex ids from positions for a 2D (D=2) problem with 5 (N=5) mesh vertices. + + >>> mesh_id = "my_mesh" + >>> positions = np.array([[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]) + >>> positions.shape + (5, 2) + >>> ids = precice.get_mesh_vertex_ids_from_positions(mesh_id, positions) + >>> ids + array([1, 2, 3, 4, 5]) + + Get mesh vertex ids from positions for a 3D problem with 5 vertices. + + >>> mesh_id = "my_mesh" + >>> positions = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3], [4, 4, 4], [5, 5, 5]]) + >>> positions.shape + (5, 3) + >>> ids = precice.get_mesh_vertex_ids_from_positions(mesh_id, positions) + >>> ids + array([1, 2, 3, 4, 5]) + """ if not isinstance(positions, np.ndarray): positions = np.asarray(positions) size, dimensions = positions.shape @@ -158,41 +599,240 @@ cdef class Interface: self.thisptr.getMeshVertexIDsFromPositions (mesh_id, size, _positions.data, _ids.data) return _ids - # sets mesh edge from vertex IDs, returns edge ID def set_mesh_edge (self, mesh_id, first_vertex_id, second_vertex_id): + """ + Sets mesh edge from vertex IDs, returns edge ID. + + Parameters + ---------- + mesh_id : int + ID of the mesh to add the edge to. + firstVertexID : int + ID of the first vertex of the edge. + secondVertexID : int + ID of the second vertex of the edge. + + Returns + ------- + id : int + ID of the edge. + + Notes + ----- + Previous calls: + vertices with firstVertexID and secondVertexID were added to the mesh with the ID meshID + """ return self.thisptr.setMeshEdge (mesh_id, first_vertex_id, second_vertex_id) - # sets mesh triangle from edges def set_mesh_triangle (self, mesh_id, first_edge_id, second_edge_id, third_edge_id): + """ + Sets mesh triangle from edge IDs + + Parameters + ---------- + mesh_id : int + ID of the mesh to add the triangle to. + first_edge_id : int + ID of the first edge of the triangle. + second_edge_id : int + ID of the second edge of the triangle. + third_edge_id : int + ID of the third edge of the triangle. + + Notes + ----- + Previous calls: + edges with first_edge_id, second_edge_id, and third_edge_id were added to the mesh with the ID meshID + """ self.thisptr.setMeshTriangle (mesh_id, first_edge_id, second_edge_id, third_edge_id) - # sets mesh triangle from vertices def set_mesh_triangle_with_edges (self, mesh_id, first_vertex_id, second_vertex_id, third_vertex_id): + """ + Sets mesh triangle from vertex IDs. + WARNING: This routine is supposed to be used, when no edge information is available per se. + Edges are created on the fly within preCICE. This routine is significantly slower than the one + using edge IDs, since it needs to check, whether an edge is created already or not. + + Parameters + ---------- + mesh_id : int + ID of the mesh to add the triangle to. + first_vertex_id : int + ID of the first vertex of the triangle. + second_vertex_id : int + ID of the second vertex of the triangle. + third_vertex_id ID : int + ID of the third vertex of the triangle. + + Notes + ----- + Previous calls: + edges with first_vertex_id, second_vertex_id, and third_vertex_id were added to the mesh with the ID meshID + """ self.thisptr.setMeshTriangleWithEdges (mesh_id, first_vertex_id, second_vertex_id, third_vertex_id) - # sets mesh quad from edges def set_mesh_quad (self, mesh_id, first_edge_id, second_edge_id, third_edge_id, fourth_edge_id): + """ + Sets mesh Quad from edge IDs. + WARNING: Quads are not fully implemented yet. + + Parameters + ---------- + mesh_id : int + ID of the mesh to add the Quad to. + first_edge_id : int + ID of the first edge of the Quad. + second_edge_id : int + ID of the second edge of the Quad. + third_edge_id : int + ID of the third edge of the Quad. + fourth_edge_id : int + ID of the forth edge of the Quad. + + Notes + ----- + Previous calls: + edges with first_edge_id, second_edge_id, third_edge_id, and fourth_edge_id were added + to the mesh with the ID mesh_id + """ self.thisptr.setMeshQuad (mesh_id, first_edge_id, second_edge_id, third_edge_id, fourth_edge_id) - # sets mesh quad from vertices def set_mesh_quad_with_edges (self, mesh_id, first_vertex_id, second_vertex_id, third_vertex_id, fourth_vertex_id): + """ + Sets surface mesh quadtriangle from vertex IDs. + WARNING: This routine is supposed to be used, when no edge information is available per se. Edges are + created on the fly within preCICE. This routine is significantly slower than the one using + edge IDs, since it needs to check, whether an edge is created already or not. + + Parameters + ---------- + mesh_id : int + ID of the mesh to add the Quad to. + first_vertex_id : int + ID of the first vertex of the Quad. + second_vertex_id : int + ID of the second vertex of the Quad. + third_vertex_id : int + ID of the third vertex of the Quad. + fourth_vertex_id : int + ID of the fourth vertex of the Quad. + + Notes + ----- + Previous calls: + edges with first_vertex_id, second_vertex_id, third_vertex_id, and fourth_vertex_id were added + to the mesh with the ID mesh_id + """ self.thisptr.setMeshQuadWithEdges (mesh_id, first_vertex_id, second_vertex_id, third_vertex_id, fourth_vertex_id) # data access - # hasData def has_data (self, str data_name, mesh_id): + """ + Checks if the data with given name is used by a solver and mesh. + + Parameters + ---------- + data_name : string + Name of the data. + mesh_id : int + ID of the associated mesh. + + Returns + ------- + tag : bool + True if the mesh is already used. + """ return self.thisptr.hasData(convert(data_name), mesh_id) def get_data_id (self, str data_name, mesh_id): + """ + Returns the ID of the data associated with the given name and mesh. + + Parameters + ---------- + data_name : string + Name of the data + mesh_id : int + ID of the associated mesh. + + Returns + ------- + id : int + ID of the corresponding data. + """ return self.thisptr.getDataID (convert(data_name), mesh_id) def map_read_data_to (self, to_mesh_id): + """ + Computes and maps all read data mapped to the mesh with given ID. + This is an explicit request to map read data to the Mesh associated with toMeshID. + It also computes the mapping if necessary. + + Parameters + ---------- + to_mesh_id : int + ID of mesh to map the read data to. + + Notes + ----- + Previous calls: + A mapping to to_mesh_id was configured. + """ self.thisptr.mapReadDataTo (to_mesh_id) def map_write_data_from (self, from_mesh_id): + """ + Computes and maps all write data mapped from the mesh with given ID. This is an explicit request + to map write data from the Mesh associated with fromMeshID. It also computes the mapping if necessary. + + Parameters + ---------- + from_mesh_id : int + ID from which to map write data. + + Notes + ----- + Previous calls: + A mapping from from_mesh_id was configured. + """ self.thisptr.mapWriteDataFrom (from_mesh_id) def write_block_vector_data (self, data_id, value_indices, values): + """ + Writes vector data given as block. This function writes values of specified vertices to a dataID. + Values are provided as a block of continuous memory. valueIndices contains the indices of the vertices + Values are stored in a numpy array [N x D] where N = number of vertices and D = dimensions of geometry + + Parameters + ---------- + data_id : int + Data ID to write to. + value_indices : array_like + Indices of the vertices. + values : numpy.ndarray + Vector values of data + + Notes + ----- + Previous calls: + count of available elements at values matches the configured dimension * size + count of available elements at valueIndices matches the given size + initialize() has been called + + Examples + -------- + Write block vector data for a 2D problem with 5 vertices: + >>> data_id = 1 + >>> value_indices = [1, 2, 3, 4, 5] + >>> values = np.array([[v1_x, v1_y], [v2_x, v2_y], [v3_x, v3_y], [v4_x, v4_y], [v5_x, v5_y]]) + >>> precice.write_block_vector_data(data_id, value_indices, values) + + Write block vector data for a 3D (D=3) problem with 5 (N=5) vertices: + >>> data_id = 1 + >>> value_indices = [1, 2, 3, 4, 5] + >>> values = np.array([[v1_x, v1_y, v1_z], [v2_x, v2_y, v2_z], [v3_x, v3_y, v3_z], [v4_x, v4_y, v4_z], [v5_x, v5_y, v5_z]]) + >>> precice.write_block_vector_data(data_id, value_indices, values) + """ if not isinstance(values, np.ndarray): values = np.asarray(values) size, dimensions = values.shape @@ -204,6 +844,41 @@ cdef class Interface: self.thisptr.writeBlockVectorData (data_id, size, _value_indices.data, _values.data) def write_vector_data (self, data_id, value_index, value): + """ + Writes vector data to a vertex. This function writes a value of a specified vertex to a dataID. + Values are provided as a block of continuous memory. + The 2D-format of value is a numpy array of shape 2 + The 3D-format of value is a numpy array of shape 3 + + Parameters + ---------- + data_id : int + ID to write to. + value_index : int + Index of the vertex. + value : numpy.array + Single vector value + + Notes + ----- + Previous calls: + count of available elements at value matches the configured dimension + initialize() has been called + + Examples + -------- + Write vector data for a 2D problem with 5 vertices: + >>> data_id = 1 + >>> value_index = 5 + >>> value = np.array([v5_x, v5_y]) + >>> precice.write_vector_data(data_id, value_index, value) + + Write vector data for a 3D (D=3) problem with 5 (N=5) vertices: + >>> data_id = 1 + >>> value_index = 5 + >>> value = np.array([v5_x, v5_y, v5_z]) + >>> precice.write_vector_data(data_id, value_index, value) + """ if not isinstance(value, np.ndarray): value = np.asarray(value) dimensions = value.size @@ -212,6 +887,33 @@ cdef class Interface: self.thisptr.writeVectorData (data_id, value_index, _value.data) def write_block_scalar_data (self, data_id, value_indices, values): + """ + Writes scalar data given as a block. This function writes values of specified vertices to a dataID. + + Parameters + ---------- + data_id : int + ID to write to. + value_indices : array_like + Indices of the vertices. + values : numpy.array + Values to be written + + Notes + ----- + Previous calls: + count of available elements at values matches the given size + count of available elements at valueIndices matches the given size + initialize() has been called + + Examples + -------- + Write block scalar data for a 2D and 3D problem with 5 (N=5) vertices: + >>> data_id = 1 + >>> value_indices = [1, 2, 3, 4, 5] + >>> values = np.array([v1 v2, v3, v4, v5]) + >>> precice.write_block_scalar_data(data_id, value_indices, values) + """ cdef np.ndarray[int, ndim=1] _value_indices = np.ascontiguousarray(value_indices, dtype=np.int32) cdef np.ndarray[double, ndim=1] _values = np.ascontiguousarray(values, dtype=np.double) assert(_values.size == _value_indices.size) @@ -219,9 +921,75 @@ cdef class Interface: self.thisptr.writeBlockScalarData (data_id, size, _value_indices.data, _values.data) def write_scalar_data (self, data_id, value_index, double value): + """ + Writes scalar data to a vertex + This function writes a value of a specified vertex to a dataID. + + Parameters + ---------- + data_id : int + ID to write to. + value_index : int + Index of the vertex. + value : double + The value to write. + + Notes + ----- + Previous calls: + initialize() has been called + + Examples + -------- + Write scalar data for a 2D or 3D problem with 5 vertices: + >>> data_id = 1 + >>> value_index = 5 + >>> value = v5 + >>> precice.write_scalar_data(data_id, value_index, value) + """ self.thisptr.writeScalarData (data_id, value_index, value) def read_block_vector_data (self, data_id, value_indices): + """ + Reads vector data into a provided block. This function reads values of specified vertices + from a dataID. Values are read into a block of continuous memory. valueIndices contains + the indices of the vertices. + + Parameters + ---------- + data_id : int + ID to read from. + value_indices : array_like + Indices of the vertices. + + Returns + ------- + values : numpy.ndarray + Contains the read data. + + Notes + ----- + Previous calls: + count of available elements at values matches the configured dimension * size + count of available elements at valueIndices matches the given size + initialize() has been called + + Examples + -------- + Read block vector data for a 2D problem with 5 vertices: + >>> data_id = 1 + >>> value_indices = [1, 2, 3, 4, 5] + >>> values = read_block_vector_data(data_id, value_indices) + >>> values.shape + >>> (5, 2) + + Read block vector data for a 3D system with 5 vertices: + >>> data_id = 1 + >>> value_indices = [1, 2, 3, 4, 5] + >>> values = read_block_vector_data(data_id, value_indices) + >>> values.shape + >>> (5, 3) + """ cdef np.ndarray[int, ndim=1] _value_indices = np.ascontiguousarray(value_indices, dtype=np.int32) size = _value_indices.size dimensions = self.get_dimensions() @@ -230,12 +998,83 @@ cdef class Interface: return _values.reshape((size, dimensions)) def read_vector_data (self, data_id, value_index): + """ + Reads vector data form a vertex. This function reads a value of a specified vertex + from a dataID. + + Parameters + ---------- + data_id : int + ID to read from. + value_index : int + Index of the vertex. + + Returns + ------- + value : numpy.array + Contains the read data. + + Notes + ----- + Previous calls: + count of available elements at value matches the configured dimension + initialize() has been called + + Examples + -------- + Read vector data for 2D problem: + >>> data_id = 1 + >>> value_index = 5 + >>> value = precice.read_vector_data(data_id, value_index) + >>> value.shape + (1, 2) + + Read vector data for 2D problem: + >>> data_id = 1 + >>> value_index = 5 + >>> value = precice.read_vector_data(data_id, value_index) + >>> value.shape + (1, 3) + """ dimensions = self.get_dimensions() cdef np.ndarray[double, ndim=1] _value = np.empty(dimensions, dtype=np.double) self.thisptr.readVectorData (data_id, value_index, _value.data) return _value def read_block_scalar_data (self, data_id, value_indices): + """ + Reads scalar data as a block. This function reads values of specified vertices from a dataID. + Values are provided as a block of continuous memory. valueIndices contains the indices of the vertices. + + Parameters + ---------- + data_id : int + ID to read from. + value_indices : array_like + Indices of the vertices. + + Returns + ------- + values : numpy.array + Contains the read data. + + Notes + ----- + Previous calls: + count of available elements at values matches the given size + count of available elements at valueIndices matches the given size + initialize() has been called + + Examples + -------- + Read block scalar data for 2D and 3D problems with 5 vertices: + >>> data_id = 1 + >>> value_indices = [1, 2, 3, 4, 5] + >>> values = precice.read_block_scalar_data(data_id, value_indices) + >>> values.size + >>> 5 + + """ cdef np.ndarray[int, ndim=1] _value_indices = np.ascontiguousarray(value_indices, dtype=np.int32) size = _value_indices.size cdef np.ndarray[double, ndim=1] _values = np.empty(size, dtype=np.double) @@ -243,18 +1082,65 @@ cdef class Interface: return _values def read_scalar_data (self, data_id, value_index): + """ + Reads scalar data of a vertex. This function needs a value of a specified vertex from a dataID. + + Parameters + ---------- + data_id : int + ID to read from. + value_index : int + Index of the vertex. + + Returns + ------- + value : double + Contains the read value + + Notes + ----- + Previous calls: + initialize() has been called. + + Examples + -------- + Read scalar data for 2D and 3D problems: + >>> data_id = 1 + >>> value_index = 5 + >>> value = precice.read_scalar_data(data_id, value_index) + """ cdef double _value self.thisptr.readScalarData (data_id, value_index, _value) return _value def get_version_information (): + """ + Returns + ------- + Current preCICE version information + """ return SolverInterface.getVersionInformation() def action_write_initial_data (): + """ + Returns + ------- + Name of action for writing initial data + """ return SolverInterface.actionWriteInitialData() - + def action_write_iteration_checkpoint (): + """ + Returns + ------- + Name of action for writing iteration checkpoint + """ return SolverInterface.actionWriteIterationCheckpoint() def action_read_iteration_checkpoint (): + """ + Returns + ------- + Name of action for reading iteration checkpoint + """ return SolverInterface.actionReadIterationCheckpoint()