I am solving a 2d transient problem where I need to visualize fields into an animation. At the moment I save the solved field at each time step of interest in a list and later in the post-processing I interpolate fields in a numpy array which is plotted frame by frame. My motivation for doing it this is that I am comfortable with customizing matplotlib plots so it helps presenting things in way I need.
However, the field interpolation is turning out to be extremely slow - it takes roughly around 5 seconds to interpolate the two vector components from the solved scalar potential! Once I solve for large enough number of points it would be very impractical to keep up with this.
I wanted to know how others process the time domain results? Is using paraview and/or pyvista following the tutorial approach more efficient? Or it will be similar?
Are you compiling a UFL expression before interpolation (through a dolfinx.fem.Expression object)? If so did you make sure to compile it only once and not at each time step? Recompiling it multiple times can slow down the program a lot.
In the above, ‘self.V_list’ is the list containing the solved potential at chosen steps. Please ignore field_interp() function, it is a wrapper to interpolation code I found from one of the tutorials. But for the approach, is this what you were suggesting in your comments? If so, could you point out where it needs to change with regards to my example?
It is unclear what these functions do, as you have not supplied a minimal working example reproducing your issue.
Also, it would be very interesting to see the breakdown of what takes time in your code, i.e. how much time does Ex.interpolate and Ey.interpolate actually take in your code?
I agree, while I prepare an MWE, here is what goes in the wrapper model_A.field_interp(‘Ex’, points)
def field_interp(self, field:str, points:np.ndarray)->np.ndarray:
field_out = FD.interp2(self.mesh, getattr(self, field), points=points)
return field_out
# in the file referenced as FD, here is the edfinition of interp2
def interp2(mesh, uh, points:typing.List, ndim = -1)->np.ndarray:
'''Interpolate the given model over specified points. Adapted from
https://github.com/jorgensd/dolfinx-tutorial/blob/v0.4.0/chapter1/membrane_code.ipynb
Args:
mesh: fenics mesh object
uh: ufl object, either a solution of the problem or another variable defined on mesh
points: numpy array to interpolate on, size ndim, num_points
Output:
uh_values: np.array of shape (points.shape[1],)
'''
if np.shape(points)[0] != 2:
raise Exception("Points array must has a shape of 2 x num_points for 2D surface interpolation")
else:
points = np.array(points) # make sure it is an np array
points = np.vstack((points, np.zeros((1, points.shape[1]))))
# first task is to identify all the mesh elements inside the
bb_tree = dolfinx.geometry.BoundingBoxTree(mesh, mesh.topology.dim)
cells = []
points_on_proc = []
# Find cells whose bounding-box collide with the the points
cell_candidates = dolfinx.geometry.compute_collisions(bb_tree, points.T)
# Choose one of the cells that contains the point
colliding_cells = dolfinx.geometry.compute_colliding_cells(mesh, cell_candidates, points.T)
uh_values = np.zeros(points.shape[1])
uh_values[:] = np.nan
for i, point in enumerate(points.T):
if len(colliding_cells.links(i)) > 0:
# points_on_proc.append(point)
# cells.append(colliding_cells.links(i)[0])
cell = colliding_cells.links(i)[0]
uh_values[i] = uh.eval(point, cell)
return uh_values
If the points and cells are constant in time, calling eval is highly inefficient.
Instead you should pull the physical points back to the reference cell.
The code you produced is not a minimal working example, I cannot copy this code and reproduce any timings etc.
I would strongly suggest making an example on a unit square or cube, which illustrates the operation you want to perform.
For now, could you refer to some examples based on an efficient alternative to using uh.eval the way I did? Indeed, points and cells are constant in time.