pencil.visu.pv_plotter ====================== .. py:module:: pencil.visu.pv_plotter .. autoapi-nested-parse:: Requirements ------------ These are generic requirements for all PyVista plotter tools including ``pv_plotter.py``, ``pv_volume_plotter.py`` and ``pv_plotter_utils.py``. * pyvista -> latest version (version >= 0.31.3) otherwise there might be issues with the pyvista.streamlines_from_source() function * imageio-ffmpeg * tqdm * numpy * sklearn --- necessary for pv_volume_plotter.py parameter tests. All of these can be installed by: ``` pip3 install ``` All pyvista plotting tools are tested to work with versions: VTK==9.0.3 and pyvista==0.31.3. Furthermore, in order to save videos, PyVista requires ``imageio-ffmpeg``. Note that saving only images is faster and should be used instead in combination with e.g. imagemagick on command line. If using progress_bar parameter, ``tqdm`` is needed. Pyvista plot generation does not work on CSC Puhti without loading the module ``mesa-settings``. General ------- This file defines a routine for generating 3D visualisation from video slices. It is able to generate both images (e.g. png) and videos (mp4/gif). This uses PyVista in order to generate plots in cartesian | spherical | cylindrical coordinates. User should call the function `plot()` to generate the visualizations. See documentation of plot() for further usage examples and parameters. Plot availability for each coordinate system -------------------------------------------- | Plot type | Cartesian | Spherical | Cylinder | |-----------------------|-----------|-----------|----------| | Scalars | X | X | X | | + surface vectors | X | X | - | | + surface streamlines | X | X | - | Things to note -------------- STREAMLINES: increasing number of source points, steps in integration or even field itself can have a large effect on the used memory / time that takes to create one frame. Attributes ---------- .. autoapisummary:: pencil.visu.pv_plotter.XYZPLANE_KEYS pencil.visu.pv_plotter.CONSTANT_SEED Classes ------- .. autoapisummary:: pencil.visu.pv_plotter.PlotSettings Functions --------- .. autoapisummary:: pencil.visu.pv_plotter.plot Module Contents --------------- .. py:data:: XYZPLANE_KEYS :value: ['xy', 'xy2', 'xz', 'yz'] .. py:data:: CONSTANT_SEED .. py:class:: PlotSettings Defines all possible settings and defaults for all plots. Defining this as a class has the advantage of keeping all default and parameter settings in one maintainable place and removes the hassle of e.g. using kwargs. Quick Tutorial -------------- Class ``PlotSettings()`` defines all possible settings in addition to the parameters function plot() takes in. For further information about all the possible parameters, see documentation of ``PlotSettings``. The class can be initialized in at least three ways: 1. Pass in the parameters: >>> settings = PlotSettings(off_screen=True, preview=False, ...) 2. Define a dictionary and pass that in using asterisk notation: >>> params = { 'off_screen': True, 'preview': False, ... } >>> settings = PlotSettings(**params) 3. Initialize class and change the values in more 'object-oriented' way: >>> settings = PlotSettings() >>> settings.off_screen = True >>> settings.preview = False GENERAL PARAMETERS ------------------ off_screen: bool Whether plotting should be done on screen or off screen preview: bool Interactive preview, specific slice can be set by islice parameter in plot() window_size: tuple should be multiple of macro_block_size=16 for imageio-ffmpeg progress_bar: bool Enable tqdm progress bar videoformat: str Options: None | 'mp4' | 'gif' imageformat: str Options: None | 'png' | 'jpeg' | etc., any Pillow compatible imageformat framerate: int Movie framerate. Does not affect gif. figdir: str Path to save images moviedir: str Path to save movies bg_color: str or 3 item list Background color. Either string or 3 tuple in RGB format. timestamp: bool Add timestamp to saved output filename. PLOT PARAMETERS --------------- norm: str Normalization applied. Options: 'linear' | 'log' coordinates: str Options: 'cartesian' | 'spherical' | 'cylinder' opacities: dict Opacities per slice. Should be dict with keys 'xy', 'xy2', 'yz' and 'xz'. COORDINATE SPECIFIC PARAMETERS ------------------------------ offset: float Offset for the bottom slice. Z-coordinate of the bottom mesh is set to -zn / offset, where zn is the number of points in z-direction. spherical_xz_pos: float Options: None | -1 | . Position for xz slice in spherical coordinates. If None, position inferred from slice data. If -1, slice set to close the gap between meridional slices in one end. VECTOR PLOT PARAMS ------------------ n_vectors: int Depending on chosen vector_method: 1. method=='every_nth': Plot only every n_vectors vectors 2. method=='random': Plot a total of n_vectors, source point chosen randomly vector_size: float Affects the size of plotted vectors. vector_method: str Options: 1. 'random' = random sampled points 2. 'every_nth' = plot every nth vector (defined by n_vectors) vector_scaling: str Scaling of vectors. Options: 1. None, no scaling applied 2. 'magnitude', vectors scaled by their magnitude (to max size defined vector_max) 3. 'scalars', vectors scaled based on the scalar data on the surface. vector_max: float Maximum value for vector size. Useful, e.g. if vectors scaled by their magnitude that they're not arbitrarily large. surface_vectors: bool If True, always one vector component set to zero (depending on which mesh were on). If False, the plotted vectors have also their 3rd component, i.e. not constrained on to the 2D surface. STREAMLINE PARAMS ----------------- streamlines: bool Enable streamlines. If False, and vectors are supplied vectors are plotted instead. stream_tube_radius: float Radius for the streamline tubes. stream_variable_radius: str Enable scaling of stream tube radius. Input should be name of the scalar array used for scaling. Available arrays include 'Magnitude' and 'scalars'. If None, streamline tube radius stays constant Extra: PyVista includes some internal scalar arrays if stream_params has 'compute_vorticity'=True. This includes e.g. 'Vorticity' array. The names of these are the easiest found by modifying __addSurfaceStreamlines function, see output of: `print(stream.point_arrays)` for available arrays. stream_radius_factor: float Maximal radius of stream tube as a multiple of stream_tube_radius. stream_src_points: int or dict Number of source points given either as an integer (all meshes have same number of src points) or as a dictionary containing keys 'xy', 'xy2', 'xz' and 'yz' defining number of source points for each mesh. stream_show_source: bool Show source points as spheres in the plot. stream_log_scale: bool Enables pyvista.add_mesh log_scale, i.e. applies logarithm for the values to be added to the colorbar. stream_params: dict Any parameters pyvista streamlines_from_source() takes in OTHER THAN `surface_streamlines` and `vectors`. The following is the Pyvista's own documentation on the pyvista.streamlines_from_source, see link for documentation after the list: * integrator_type The integrator type to be used for streamline generation. The default is Runge-Kutta45. The recognized solvers are: RUNGE_KUTTA2 (2), RUNGE_KUTTA4 (4), and RUNGE_KUTTA45 (45). Options are 2, 4, or 45. Default is 45. * integration_direction Specify whether the streamline is integrated in the upstream or downstream directions (or both). Options are 'both', 'backward', or 'forward'. * initial_step_length Initial step size used for line integration, expressed in length unitsL or cell length units (see step_unit parameter). either the starting size for an adaptive integrator, e.g., RK45, or the constant / fixed size for non-adaptive ones, i.e., RK2 and RK4). * step_unit Uniform integration step unit. The valid unit is now limited to only LENGTH_UNIT ('l') and CELL_LENGTH_UNIT ('cl'). Default is CELL_LENGTH_UNIT: 'cl'. * min_step_length Minimum step size used for line integration, expressed in length or cell length units. Only valid for an adaptive integrator RK45. * max_step_length aximum step size used for line integration, expressed in length or cell length units. Only valid for an adaptive integrator RK45. * max_steps Maximum number of steps for integrating a streamline. * terminal_speed Terminal speed value, below which integration is terminated. * max_error Maximum error tolerated throughout streamline integration. * max_time Specify the maximum length of a streamline expressed in LENGTH_UNIT. * compute_vorticity Vorticity computation at streamline points (necessary for generating proper stream-ribbons using the vtkRibbonFilter. * interpolator_type Set the type of the velocity field interpolator to locate cells during streamline integration either by points or cells. The cell locator is more robust then the point locator. Options are 'point' or 'cell' (abbreviations of 'p' and 'c' are also supported). * rotation_scale This can be used to scale the rate with which the streamribbons twist. The default is 1 See https://docs.pyvista.org/core/filters.html?highlight=streamlines_from_source#pyvista.DataSetFilters.streamlines_from_source for more details on the parameters. Note that if stream_params is None the following defaults are set: >>> self.stream_params = { 'max_steps': 1000, 'max_time': 1e60, 'terminal_speed': 1e-60, 'integration_direction': 'both', 'compute_vorticity': False, 'integrator_type': 45, } RANDOM SAMPLING PARAMS ---------------------- set_seed_1: bool For debugging, always sets random seed to 1, thus all sampled points are the same. Makes comparing streamlines easier for debugging constant_seed: bool Sets random seed once in the beginning and resets the random generator always with this seed. This has the consequence of randomizing initial points, keeping them constant, e.g. throught the movie. Makes streamlines jump less around the meshes. CAMERA PARAMS ------------- camera_centre: tuple of floats Coordinates for camera centre in form (x, y, z). focal_point: tuple of floats Focal point for the camera in form (x, y, z). AXES PARAMS ----------- show_axes: bool Show axes or not. axes_labels: tuple of str Labels for the axes. Should be tuple of length 3 containing strings. axes_font: int Font for the axes labels COLORBAR PROPERTIES: field specific (vectors | streamlines) ----------------------------------------------------------- show_field_sbar: bool Show scalarbar for vectors / streamlines field_cmap: str Matplotlib compatible colormap for vectors / streamlines field_sbar_title: str Title for fields scalarbar. pos_x and pos_y are given as a float between 0 and 1 (percentage). That is it defines the distance from leftmost edge (pos_x) and bottom most edge (pos_y). E.g. pos_x = 0 would mean colorbar is set at the very leftmost edge. NOTE! if both None, colorbars set automatically, also works for multiple colorbars set nicely next to each other. field_sbar_pos_x: float If None set automatically. Else float in range [0,1]. Note, 0.03 is pretty good value (fairly close to left edge) field_sbar_pos_y: float None or float between [0,1]. None works well COLORBAR PROPERTIES: scalar specific ------------------------------------ show_scalar_sbar: bool Show scalarbar for scalars scalar_cmap: str Matplotlib compatible colormap for scalars. scalar_sbar_title: str Title for scalar scalarbar. scalar_sbar_pos_x: float None or float between [0,1]. Note, 0.88 is pretty good value (enough close to right edge). See notes above for detailed explanation of pos_x scalar_sbar_pos_y: float None or float between [0,1]. Note, None works well. See notes above for detailed explanation of pos_y COLORBAR PROPERTIES: Generic properties --------------------------------------- Following parameters apply both to field and scalar colorbars. cbar_width: float Width as a percentage, between 0 and 1 cbar_height: float Height as a percentage, between 0 and 1 cbar_title_font: int Title font size for scalarbar cbar_label_font: int Label font size for scalarbar n_colors: int Number of colors for the scalarbar. _sbar_args: dict INTERNAL VALUE. SHOULD NOT BE USED. TITLE ANNOTATIONS ----------------- title_position: str Options: 'lower_left', 'lower_right', 'upper_left', 'upper_right', 'lower_edge', 'upper_edge', 'right_edge', and 'left_edge'. title_font: int Font size for the title. str_unit: str Unit added behind the timestamp in the title. I.e. title is form '' tscale: float Multiplicative scaling for the timestamp in title. time_precision: int Number of decimals shown for the timestamp. .. py:attribute:: off_screen :type: bool :value: True .. py:attribute:: preview :type: bool :value: False .. py:attribute:: window_size :type: tuple :value: (1024, 768) .. py:attribute:: progress_bar :type: bool :value: True .. py:attribute:: videoformat :type: str :value: None .. py:attribute:: imageformat :type: str :value: 'png' .. py:attribute:: framerate :type: int :value: 15 .. py:attribute:: figdir :type: str :value: './images/' .. py:attribute:: moviedir :type: str :value: './movies/' .. py:attribute:: bg_color :type: str :value: 'white' .. py:attribute:: timestamp :type: bool :value: True .. py:attribute:: norm :type: str :value: 'linear' .. py:attribute:: coordinates :type: str :value: 'cartesian' .. py:attribute:: opacities :type: dict :value: None .. py:attribute:: offset :type: float :value: 2.0 .. py:attribute:: spherical_xz_pos :type: float :value: -1 .. py:attribute:: n_vectors :type: int :value: 500 .. py:attribute:: vector_size :type: float :value: 7 .. py:attribute:: vector_method :type: str :value: 'random' .. py:attribute:: vector_scaling :type: str :value: 'magnitude' .. py:attribute:: vector_max :type: float :value: None .. py:attribute:: surface_vectors :type: bool :value: True .. py:attribute:: streamlines :type: bool :value: False .. py:attribute:: stream_tube_radius :type: float :value: 0.05 .. py:attribute:: stream_variable_radius :type: str :value: 'Magnitude' .. py:attribute:: stream_radius_factor :type: float :value: 10 .. py:attribute:: stream_src_points :type: int :value: 50 .. py:attribute:: stream_show_source :type: bool :value: False .. py:attribute:: stream_log_scale :type: bool :value: False .. py:attribute:: stream_params :type: dict :value: None .. py:attribute:: set_seed_1 :type: bool :value: False .. py:attribute:: constant_seed :type: bool :value: False .. py:attribute:: camera_centre :type: tuple :value: None .. py:attribute:: focal_point :type: tuple :value: None .. py:attribute:: show_axes :type: bool :value: True .. py:attribute:: axes_labels :type: tuple :value: ('x', 'y', 'z') .. py:attribute:: axes_font :type: int :value: 16 .. py:attribute:: show_field_sbar :type: bool :value: True .. py:attribute:: field_cmap :type: str :value: 'bwr' .. py:attribute:: field_sbar_title :type: str :value: '' .. py:attribute:: field_sbar_pos_x :type: float :value: None .. py:attribute:: field_sbar_pos_y :type: float :value: None .. py:attribute:: show_scalar_sbar :type: bool :value: True .. py:attribute:: scalar_cmap :type: str :value: 'bwr' .. py:attribute:: scalar_sbar_title :type: str :value: '' .. py:attribute:: scalar_sbar_pos_x :type: float :value: None .. py:attribute:: scalar_sbar_pos_y :type: float :value: None .. py:attribute:: cbar_width :type: float :value: 0.06 .. py:attribute:: cbar_height :type: float :value: 0.65 .. py:attribute:: cbar_title_font :type: int :value: 10 .. py:attribute:: cbar_label_font :type: int :value: 8 .. py:attribute:: n_colors :type: int :value: 256 .. py:attribute:: title_position :type: str :value: 'upper_left' .. py:attribute:: title_font :type: int :value: 20 .. py:attribute:: str_unit :type: str :value: '' .. py:attribute:: tscale :type: float :value: 1 .. py:attribute:: time_precision :type: int :value: 3 .. py:function:: plot(slice_obj=None, datadir='./data', precision='f', fields=['uu1'], xyzplane={'xy2': 'xy2', 'xy': 'xy', 'yz': 'yz', 'xz': 'xz'}, vectors=None, tstart=0.0, tend=1e+38, islice=-1, istart=None, iend=None, color_range=None, color_levels=None, unit='unit_velocity', rescale=1.0, par=list(), debug=True, settings=PlotSettings()) -> None Create plot from slice data in cartesian | spherical | cylindrical coordinates and saves the output as images and / or videos. Creates plots for times [tstart, tend] of all the fields specified in fields argument. Internally calls __plot_field() to handle plotting of a given field over all time instants. By default this function creates images off screen without creating interactive plot window. If plotting done on screen (i.e. off_screen=False) and creating a video, one can see the video live and the video angle can be rotated, also affecting the output video. In case of fine tuning the plot parameters, e.g. camera position / focal point, one can turn preview on (in settings) to see an interactive plot window that can be rotated around and outputs the camera parameters on both command line and the plot window. :param slice_obj: Pencil slice data, if supplied no slice data is read. If None, slice data is automatically read from datadir. Default: None. :type slice_obj: pencil.read.allslices.SliceSeries, optional :param datadir: Directory of the data. Default: './data'. :type datadir: str, optional :param precision: Precision for the data read, parameter e.g. pencil.read.slices() takes in. Can be 'half', 'f' or 'd'. Default: 'f'. :type precision: str, optional :param fields: Fields that are plotted. Default: ['uu1',]. :type fields: list of strings, optional :param xyzplane: Dictionary having allowed keys defined by XYZPLANE_KEYS. Values should be the matching data that is plotted on each of these surfaces. See "Coordinate systems" below. :type xyzplane: dictionary, optional :param vectors: List of 3 strings defining the vector components of the vector field. This is used to create streamlines / vectors. :type vectors: list, optional :param tstart: Start time instant. See parameter islice for more details. Default: 0. :type tstart: float, optional :param tend: End time instant. See parameter islice for more details. Default: 1e38 :type tend: float, optional :param islice: Sequential integer number of a slice in given period (starting from 0). If set to -1, generates all slices in given period [tstart, tend]. Else generates only visualisations of the given slice number islice. Default: -1. :type islice: int, optional :param istart: First index to start plotting from. This index corresponds to time instant slice_obj.t[istart]. istart is used if it is not None, otherwise tstart is used. :type istart: int, optional :param iend: Similar to istart, end time index corresponding to time slice_obj.t[iend] :type iend: int, optional :param color_range: If supplied, list containing [cmin,cmax] for the colorbar. :type color_range: list of 2, optional :param color_levels: If set to 'common', then all times and fields have same colorbar min and max values. Default: None. :type color_levels: string, optional :param unit: Unit (string) appended to the end of the timestamp in the title. :type unit: string, optional :param rescale: TODO! :type rescale: int, optional :param par: TODO! :type par: list, optional :param debug: Enable debugging prints. Default: False. :type debug: boolean, optional :param \*\*settings: Dictionary of possible settings for the plot. See docstring at the start of this file for an ready dictionary input of all possible parameters settings could take in. :type \*\*settings: optional :param Coordinate systems: :param ------------------: :param Allowed keys for xyzplane are defined by XYZPLANE_KEYS = ['xy': :param 'xy2': :param 'xz': :param 'yz'].: :param In case of each coordinate system: :param these surfaces correspond to: :param - Cartesian: * xy = bottom * xy2 = top * xz = right vertical slice * yz = left verical slice :param - Spherical: * xy = r-theta slice, 1st meridional slice * xy2 = r-theta slice, 2nd meridional slice * xz = r-phi slice, "plane slice" between meridionals and yz * yz = theta-phi slice, spherical surface at the back (or part of it) :param - Cylinder: * xy = r-theta slice, bottom of the cylinder (circle) * xy2 = r-theta slice 2, top of the cylinder (circle) * xz = r-z slice, "radial cut", rectangle between top/bottom * yz = theta-z slice, shell of the cylinder (or part of it) .. rubric:: Examples Minimal usage example, this saves by default 'png' images at all timesteps of field 'uu1' to figure directory ./images (creating the directory if it does not exist). Debug should be left on so one can see that the script runs properly. By default this creates a cartesian plot (i.e. box plot). >>> from pencil.visu.pv_plotter import plot, PlotSettings >>> plot(debug=True) This plotter uses a settings object (dataclass) PlotSettings that contains all possible extra settings that can be varied. See documentation on PlotSettings for further information on all of the parameters. This can be used in the following way: >>> settings = { 'imageformat': None, 'videoformat': 'mp4', 'coordinates': 'cartesian' 'cbar_title': 'TITLE', 'n_colors': 100, 'camera_centre': (-170, -140, 125), 'focal_point': (30, 30, 16), 'offset': 2.5 } >>> settings = PlotSettings(**settings) >>> plot(debug=True settings=settings) Furthermore, 2D slices to be plotted can be modified by passing in xyzplane parameter >>> xyzplane = {'xy2': 'xy2','xy': 'xy', 'xz': 'xz', 'yz': 'yz'} where the string values represent the slices to be plotted on the meshes (see "Coordinate systems" for what which mesh each key corresponds given coordinate system). These values should be found in slices.__dict__.keys(). .. rubric:: Notes - NOTE! The default camera angle is most likely horrible and may not even show the data at all. This should be manually modified and e.g. easily found using preview=True that shows an interactive plot window that can be rotated around and print out the camera parameters at the same time. - NOTE! On CSC computers module 'mesa-settings' needs to be loaded (works at least on Puhti).